1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain_test
4
5import (
6 "testing"
7 "time"
8
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11
12 "bnema.dev/ermite/internal/domain"
13)
14
15func TestScopes(t *testing.T) {
16 all := domain.AllScopes()
17 assert.Len(t, all, 9)
18 for _, s := range all {
19 got, err := domain.ParseScope(s.String())
20 require.NoError(t, err)
21 assert.Equal(t, s, got)
22 }
23 for _, in := range []string{"", "repo", "repo:READ", "admin", "repo:read "} {
24 _, err := domain.ParseScope(in)
25 assert.ErrorIs(t, err, domain.ErrInvalidScope, in)
26 }
27}
28
29func TestHashTokenSecret(t *testing.T) {
30 h := domain.HashTokenSecret("ermite_abc")
31 assert.Equal(t, h, domain.HashTokenSecret("ermite_abc"))
32 assert.NotEqual(t, h, domain.HashTokenSecret("ermite_abd"))
33 assert.Len(t, h.String(), 64)
34 // SHA-256("") known value.
35 assert.Equal(t, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", domain.HashTokenSecret("").String())
36}
37
38func TestTokenExpired(t *testing.T) {
39 now := time.Date(2026, 10, 10, 8, 0, 0, 0, time.UTC)
40 assert.False(t, domain.Token{}.Expired(now), "no expiry never expires")
41 assert.False(t, domain.Token{ExpiresAt: now.Add(time.Second)}.Expired(now))
42 assert.True(t, domain.Token{ExpiresAt: now}.Expired(now), "expires at the instant")
43 assert.True(t, domain.Token{ExpiresAt: now.Add(-time.Second)}.Expired(now))
44}
45
46func TestPrincipalScopes(t *testing.T) {
47 a, b := mustName(t, "a"), mustName(t, "b")
48 tok := domain.Token{ID: "t1", Scopes: []domain.Scope{domain.ScopeRepoRead, domain.ScopeRepoWrite}, Repos: []domain.RepoName{a}}
49 p := domain.TokenPrincipal(tok)
50
51 assert.Equal(t, "token", p.Label())
52 assert.Equal(t, "t1", p.TokenID())
53 assert.False(t, p.IsRoot())
54 assert.False(t, p.IsAnonymous())
55 assert.True(t, p.HasScope(domain.ScopeRepoWrite))
56 assert.False(t, p.HasScope(domain.ScopeTokenAdmin))
57 assert.True(t, p.Allows(domain.ScopeRepoWrite, a))
58 assert.False(t, p.Allows(domain.ScopeRepoWrite, b), "outside the repository list")
59 assert.False(t, p.Allows(domain.ScopeRepoAdmin, a), "scope not held")
60
61 // Without a list, every repository.
62 all := domain.TokenPrincipal(domain.Token{Scopes: []domain.Scope{domain.ScopeRepoRead}})
63 assert.True(t, all.Allows(domain.ScopeRepoRead, a))
64 assert.True(t, all.Allows(domain.ScopeRepoRead, b))
65
66 // The principal owns copies.
67 tok.Scopes[0] = domain.ScopeTokenAdmin
68 assert.True(t, p.HasScope(domain.ScopeRepoRead))
69 p.Scopes()[0] = domain.ScopeTokenAdmin
70 assert.True(t, p.HasScope(domain.ScopeRepoRead))
71
72 assert.True(t, domain.Root().Allows(domain.ScopeTokenAdmin, a))
73 assert.False(t, domain.Anonymous().HasScope(domain.ScopeRepoRead))
74 assert.False(t, domain.Anonymous().Allows(domain.ScopeRepoRead, a))
75 assert.Equal(t, "root", domain.Root().Label())
76 assert.Equal(t, "anonymous", domain.Anonymous().Label())
77}
78
79func mustName(t *testing.T, s string) domain.RepoName {
80 t.Helper()
81 n, err := domain.ParseRepoName(s)
82 require.NoError(t, err)
83 return n
84}