v3.3.1 31f1eec

route_test.go RawHistoryFolder

1package route_test
2
3import (
4	"testing"
5
6	"github.com/stretchr/testify/assert"
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/gordon/internal/adapters/in/http/registry/route"
10)
11
12// validDigest is a syntactically valid sha256 digest.
13const validDigest = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
14
15// validUUID is a syntactically valid v4 UUID.
16const validUUID = "12345678-1234-4123-8123-123456789012"
17
18func TestParse_Routes(t *testing.T) {
19	tests := []struct {
20		path string
21		want route.Operation
22	}{
23		{"/v2/", route.Operation{Kind: route.KindBase}},
24		{"/v2", route.Operation{Kind: route.KindBase}},
25		{"/v2/_catalog", route.Operation{Kind: route.KindCatalog}},
26		{
27			"/v2/myrepo/manifests/latest",
28			route.Operation{Kind: route.KindManifest, Repository: "myrepo", Reference: "latest"},
29		},
30		{
31			"/v2/myorg/myapp/manifests/v1.0",
32			route.Operation{Kind: route.KindManifest, Repository: "myorg/myapp", Reference: "v1.0"},
33		},
34		{
35			"/v2/myrepo/blobs/" + validDigest,
36			route.Operation{Kind: route.KindBlob, Repository: "myrepo", Digest: validDigest},
37		},
38		{"/v2/myrepo/blobs/uploads/", route.Operation{Kind: route.KindUpload, Repository: "myrepo"}},
39		{
40			"/v2/myrepo/blobs/uploads/" + validUUID,
41			route.Operation{Kind: route.KindUpload, Repository: "myrepo", UploadID: validUUID},
42		},
43		{"/v2/myrepo/tags/list", route.Operation{Kind: route.KindTagList, Repository: "myrepo"}},
44	}
45	for _, tt := range tests {
46		t.Run(tt.path, func(t *testing.T) {
47			op, err := route.Parse(tt.path)
48			require.NoError(t, err)
49			assert.Equal(t, tt.want, op)
50		})
51	}
52}
53
54// TestParse_ResolvesRepositoryFromLastMarker proves a reserved-looking
55// component earlier in the path cannot shorten the repository: the parser
56// (and therefore authorization) always sees the repository the handler
57// would serve.
58func TestParse_ResolvesRepositoryFromLastMarker(t *testing.T) {
59	tests := []struct {
60		path     string
61		wantRepo string
62	}{
63		{"/v2/manifests/victim/manifests/latest", "manifests/victim"},
64		{"/v2/allowed/tags/victim/manifests/latest", "allowed/tags/victim"},
65		{"/v2/blobs/impostor/blobs/" + validDigest, "blobs/impostor"},
66	}
67	for _, tt := range tests {
68		t.Run(tt.path, func(t *testing.T) {
69			op, err := route.Parse(tt.path)
70			require.NoError(t, err)
71			assert.Equal(t, tt.wantRepo, op.Repository)
72		})
73	}
74}
75
76func TestParse_Errors(t *testing.T) {
77	notFound := []string{
78		"/healthz",
79		"/v2/myrepo",
80		"/v2/myrepo/manifests/",
81		"/v2/myrepo/manifests/a/b",
82		"/v2/myrepo/blobs/",
83		"/v2/myrepo/tags/list/",
84		"/v2//manifests/latest",
85	}
86	for _, path := range notFound {
87		t.Run("not found "+path, func(t *testing.T) {
88			_, err := route.Parse(path)
89			assert.ErrorIs(t, err, route.ErrNotFound)
90		})
91	}
92
93	cases := []struct {
94		path     string
95		wantCode string
96	}{
97		{"/v2/BadRepo/manifests/latest", route.CodeNameInvalid},
98		{"/v2/myrepo/manifests/bad tag", route.CodeTagInvalid},
99		{"/v2/myrepo/blobs/notadigest", route.CodeDigestInvalid},
100		{"/v2/myrepo/blobs/uploads/not-a-uuid", route.CodeBlobUploadInvalid},
101	}
102	for _, tc := range cases {
103		t.Run("invalid "+tc.path, func(t *testing.T) {
104			_, err := route.Parse(tc.path)
105			assert.ErrorIs(t, err, route.ErrInvalid)
106			var parseErr *route.ParseError
107			require.ErrorAs(t, err, &parseErr)
108			assert.Equal(t, tc.wantCode, parseErr.Code)
109		})
110	}
111}
112
113func TestRequiresRepositoryAuth_ProtocolRootsOnly(t *testing.T) {
114	base, err := route.Parse("/v2/")
115	require.NoError(t, err)
116	assert.False(t, base.RequiresRepositoryAuth())
117
118	catalog, err := route.Parse("/v2/_catalog")
119	require.NoError(t, err)
120	assert.False(t, catalog.RequiresRepositoryAuth())
121
122	manifest, err := route.Parse("/v2/myrepo/manifests/latest")
123	require.NoError(t, err)
124	assert.True(t, manifest.RequiresRepositoryAuth())
125}

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