v2.2.7 aefe19e

token_test.go RawHistoryFolder

1package registry
2
3import (
4	"net/http"
5	"net/http/httptest"
6	"testing"
7
8	"github.com/bnema/zerowrap"
9	"github.com/stretchr/testify/assert"
10)
11
12func tokenTestLogger() zerowrap.Logger {
13	return zerowrap.Default()
14}
15
16func TestParseRequestedScopes_MultipleScopeParams(t *testing.T) {
17	log := tokenTestLogger()
18	h := &TokenHandler{log: log}
19
20	req := httptest.NewRequest(http.MethodGet, "/v2/token?scope=repository:repo1:pull&scope=repository:repo2:push", nil)
21
22	scopes := h.parseRequestedScopes(req, log)
23
24	assert.Len(t, scopes, 2)
25	assert.Contains(t, scopes, "repository:repo1:pull")
26	assert.Contains(t, scopes, "repository:repo2:push")
27}
28
29func TestParseRequestedScopes_InvalidScopeFiltered(t *testing.T) {
30	log := tokenTestLogger()
31	h := &TokenHandler{log: log}
32
33	tests := []struct {
34		name        string
35		query       string
36		wantScopes  []string
37		wantDefault bool
38	}{
39		{
40			name:        "invalid format is skipped",
41			query:       "scope=invalid-no-colons&scope=repository:valid:pull",
42			wantScopes:  []string{"repository:valid:pull"},
43			wantDefault: false,
44		},
45		{
46			name:        "missing actions is skipped",
47			query:       "scope=repository:myrepo&scope=repository:valid:pull",
48			wantScopes:  []string{"repository:valid:pull"},
49			wantDefault: false,
50		},
51		{
52			name:        "all invalid returns default",
53			query:       "scope=invalid&scope=also-invalid",
54			wantScopes:  []string{"repository:*:pull"},
55			wantDefault: true,
56		},
57	}
58
59	for _, tt := range tests {
60		t.Run(tt.name, func(t *testing.T) {
61			req := httptest.NewRequest(http.MethodGet, "/v2/token?"+tt.query, nil)
62			scopes := h.parseRequestedScopes(req, log)
63
64			assert.Equal(t, tt.wantScopes, scopes)
65		})
66	}
67}
68
69func TestParseRequestedScopes_NonRepositoryScopeRejected(t *testing.T) {
70	log := tokenTestLogger()
71	h := &TokenHandler{log: log}
72
73	tests := []struct {
74		name       string
75		query      string
76		wantScopes []string
77	}{
78		{
79			name:       "registry scope is rejected",
80			query:      "scope=registry:catalog:*&scope=repository:myrepo:pull",
81			wantScopes: []string{"repository:myrepo:pull"},
82		},
83		{
84			name:       "only non-repository scopes returns default",
85			query:      "scope=registry:catalog:*&scope=custom:something:action",
86			wantScopes: []string{"repository:*:pull"},
87		},
88	}
89
90	for _, tt := range tests {
91		t.Run(tt.name, func(t *testing.T) {
92			req := httptest.NewRequest(http.MethodGet, "/v2/token?"+tt.query, nil)
93			scopes := h.parseRequestedScopes(req, log)
94
95			assert.Equal(t, tt.wantScopes, scopes)
96		})
97	}
98}
99
100func TestParseRequestedScopes_NoScopeReturnsDefault(t *testing.T) {
101	log := tokenTestLogger()
102	h := &TokenHandler{log: log}
103
104	req := httptest.NewRequest(http.MethodGet, "/v2/token", nil)
105	scopes := h.parseRequestedScopes(req, log)
106
107	assert.Equal(t, []string{"repository:*:pull"}, scopes)
108}
109
110func TestParseRequestedScopes_EmptyScopeParamReturnsDefault(t *testing.T) {
111	log := tokenTestLogger()
112	h := &TokenHandler{log: log}
113
114	req := httptest.NewRequest(http.MethodGet, "/v2/token?scope=", nil)
115	scopes := h.parseRequestedScopes(req, log)
116
117	// Empty scope param is invalid and should return default
118	assert.Equal(t, []string{"repository:*:pull"}, scopes)
119}
120
121func TestParseRequestedScopes_ValidScopesPreserved(t *testing.T) {
122	log := tokenTestLogger()
123	h := &TokenHandler{log: log}
124
125	tests := []struct {
126		name       string
127		query      string
128		wantScopes []string
129	}{
130		{
131			name:       "single scope",
132			query:      "scope=repository:myrepo:pull",
133			wantScopes: []string{"repository:myrepo:pull"},
134		},
135		{
136			name:       "multiple actions",
137			query:      "scope=repository:myrepo:push,pull",
138			wantScopes: []string{"repository:myrepo:push,pull"},
139		},
140		{
141			name:       "wildcard repo",
142			query:      "scope=repository:*:pull",
143			wantScopes: []string{"repository:*:pull"},
144		},
145		{
146			name:       "org wildcard",
147			query:      "scope=repository:myorg/*:push,pull",
148			wantScopes: []string{"repository:myorg/*:push,pull"},
149		},
150		{
151			name:       "nested repo",
152			query:      "scope=repository:myorg/team/app:pull",
153			wantScopes: []string{"repository:myorg/team/app:pull"},
154		},
155	}
156
157	for _, tt := range tests {
158		t.Run(tt.name, func(t *testing.T) {
159			req := httptest.NewRequest(http.MethodGet, "/v2/token?"+tt.query, nil)
160			scopes := h.parseRequestedScopes(req, log)
161
162			assert.Equal(t, tt.wantScopes, scopes)
163		})
164	}
165}
166
167func TestIsLocalhostRequest(t *testing.T) {
168	tests := []struct {
169		name       string
170		remoteAddr string
171		want       bool
172	}{
173		{"127.0.0.1 is localhost", "127.0.0.1:12345", true},
174		{"127.0.0.2 is localhost", "127.0.0.2:12345", true},
175		{"::1 is localhost", "[::1]:12345", true},
176		{"::1 without brackets", "::1:12345", true},
177		{"192.168.1.1 is not localhost", "192.168.1.1:12345", false},
178		{"10.0.0.1 is not localhost", "10.0.0.1:12345", false},
179		{"public IP is not localhost", "8.8.8.8:12345", false},
180	}
181
182	for _, tt := range tests {
183		t.Run(tt.name, func(t *testing.T) {
184			req := httptest.NewRequest(http.MethodGet, "/v2/token", nil)
185			req.RemoteAddr = tt.remoteAddr
186
187			result := isLocalhostRequest(req)
188
189			assert.Equal(t, tt.want, result)
190		})
191	}
192}

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 7

Tags 106

↑↓ moveenter openesc close