v0.3.0 9fdf386

scopes_test.go RawHistoryFolder

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

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close