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