1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package client_test
4
5import (
6 "strings"
7 "testing"
8 "time"
9
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12
13 "bnema.dev/ermite-api/gen/ermite"
14 "bnema.dev/ermite/internal/adapters/client"
15)
16
17func TestScopeWireNames(t *testing.T) {
18 want := map[ermite.Scope]string{
19 ermite.Scope_SCOPE_MR_REVIEW: "mr:review",
20 ermite.Scope_SCOPE_REPO_READ: "repo:read",
21 ermite.Scope_SCOPE_REPO_WRITE: "repo:write",
22 ermite.Scope_SCOPE_REPO_ADMIN: "repo:admin",
23 ermite.Scope_SCOPE_MR_WRITE: "mr:write",
24 ermite.Scope_SCOPE_MR_MERGE: "mr:merge",
25 ermite.Scope_SCOPE_CI_WRITE: "ci:write",
26 ermite.Scope_SCOPE_SECRETS_WRITE: "secrets:write",
27 ermite.Scope_SCOPE_RUNNER_REGISTER: "runner:register",
28 ermite.Scope_SCOPE_TOKEN_ADMIN: "token:admin",
29 ermite.Scope_SCOPE_ISSUE_WRITE: "issue:write",
30 }
31 for s, name := range want {
32 assert.Equal(t, name, client.ScopeName(s))
33 got, err := client.ParseScope(strings.ToUpper(name))
34 require.NoError(t, err)
35 assert.Equal(t, s, got)
36 }
37 assert.Len(t, client.AllScopes(), len(want), "the cli preset covers every scope of the contract")
38}
39
40func TestPresetExpansion(t *testing.T) {
41 cli, err := client.PresetScopes("cli")
42 require.NoError(t, err)
43 assert.Equal(t, client.AllScopes(), cli)
44 assert.NotContains(t, cli, ermite.Scope_SCOPE_UNSPECIFIED)
45
46 agent, err := client.PresetScopes("agent")
47 require.NoError(t, err)
48 assert.NotContains(t, agent, ermite.Scope_SCOPE_ISSUE_WRITE, "agents get issue:write only when asked")
49 assert.Equal(t, []ermite.Scope{ermite.Scope_SCOPE_REPO_WRITE, ermite.Scope_SCOPE_MR_WRITE}, agent)
50
51 ci, err := client.PresetScopes("CI")
52 require.NoError(t, err)
53 assert.Equal(t, []ermite.Scope{ermite.Scope_SCOPE_REPO_READ}, ci)
54
55 _, err = client.PresetScopes("root")
56 require.ErrorContains(t, err, "unknown preset")
57}
58
59func TestMergeScopes(t *testing.T) {
60 got, err := client.MergeScopes("ci", []string{"repo:write", "repo:read"})
61 require.NoError(t, err)
62 assert.Equal(t, []ermite.Scope{ermite.Scope_SCOPE_REPO_READ, ermite.Scope_SCOPE_REPO_WRITE}, got, "preset and scopes add up, sorted, no duplicates")
63
64 got, err = client.MergeScopes("", []string{"token:admin"})
65 require.NoError(t, err)
66 assert.Equal(t, []ermite.Scope{ermite.Scope_SCOPE_TOKEN_ADMIN}, got)
67
68 _, err = client.MergeScopes("", nil)
69 require.ErrorContains(t, err, "--preset")
70 _, err = client.MergeScopes("", []string{"repo:everything"})
71 require.ErrorContains(t, err, "unknown scope")
72}
73
74func TestParseExpiry(t *testing.T) {
75 now := time.Date(2026, 10, 10, 12, 0, 0, 0, time.UTC)
76 tests := []struct {
77 in string
78 want time.Time
79 }{
80 {"30d", now.Add(30 * 24 * time.Hour)},
81 {"2w", now.Add(14 * 24 * time.Hour)},
82 {"12h", now.Add(12 * time.Hour)},
83 {"1h30m", now.Add(90 * time.Minute)},
84 {"2027-01-31", time.Date(2027, 1, 31, 23, 59, 59, 0, time.UTC)},
85 {"2027-01-31T10:00:00+02:00", time.Date(2027, 1, 31, 8, 0, 0, 0, time.UTC)},
86 }
87 for _, tt := range tests {
88 got, err := client.ParseExpiry(tt.in, now)
89 require.NoError(t, err, tt.in)
90 assert.True(t, tt.want.Equal(got), "%s: %s != %s", tt.in, tt.want, got)
91 }
92 for _, in := range []string{"", "soon", "0d", "-3d", "d", "1.5d", "2026-10-09", "-1h", "99999999d"} {
93 _, err := client.ParseExpiry(in, now)
94 require.Error(t, err, in)
95 assert.Contains(t, err.Error(), "--expires")
96 }
97}