v2.30.0-pre4 15d9fe3

labels_test.go RawHistoryFolder

1package auto
2
3import (
4	"context"
5	"encoding/json"
6	"io"
7	"strings"
8	"testing"
9
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/require"
12
13	"github.com/bnema/gordon/internal/boundaries/out/mocks"
14	"github.com/bnema/gordon/internal/domain"
15)
16
17func TestParseConfigDigest(t *testing.T) {
18	tests := []struct {
19		name     string
20		manifest map[string]any
21		expected string
22		wantErr  bool
23	}{
24		{
25			name: "valid manifest v2",
26			manifest: map[string]any{
27				"schemaVersion": 2,
28				"mediaType":     "application/vnd.docker.distribution.manifest.v2+json",
29				"config": map[string]any{
30					"mediaType": "application/vnd.docker.container.image.v1+json",
31					"digest":    "sha256:abc123",
32					"size":      1234,
33				},
34			},
35			expected: "sha256:abc123",
36		},
37		{
38			name: "OCI manifest",
39			manifest: map[string]any{
40				"schemaVersion": 2,
41				"mediaType":     "application/vnd.oci.image.manifest.v1+json",
42				"config": map[string]any{
43					"mediaType": "application/vnd.oci.image.config.v1+json",
44					"digest":    "sha256:def456",
45					"size":      5678,
46				},
47			},
48			expected: "sha256:def456",
49		},
50		{
51			name: "empty config digest",
52			manifest: map[string]any{
53				"schemaVersion": 2,
54				"config": map[string]any{
55					"digest": "",
56				},
57			},
58			expected: "",
59		},
60	}
61
62	for _, tt := range tests {
63		t.Run(tt.name, func(t *testing.T) {
64			data, err := json.Marshal(tt.manifest)
65			require.NoError(t, err)
66
67			result, err := ParseConfigDigest(data)
68
69			if tt.wantErr {
70				assert.Error(t, err)
71				return
72			}
73
74			require.NoError(t, err)
75			assert.Equal(t, tt.expected, result)
76		})
77	}
78}
79
80func TestParseConfigDigest_InvalidJSON(t *testing.T) {
81	_, err := ParseConfigDigest([]byte("invalid json"))
82	assert.Error(t, err)
83}
84
85func TestParseImageLabels(t *testing.T) {
86	tests := []struct {
87		name     string
88		config   map[string]any
89		expected *domain.ImageLabels
90	}{
91		{
92			name: "single domain label",
93			config: map[string]any{
94				"config": map[string]any{
95					"Labels": map[string]any{
96						"gordon.domain": "app.example.com",
97					},
98				},
99			},
100			expected: &domain.ImageLabels{
101				Domain: "app.example.com",
102			},
103		},
104		{
105			name: "multiple domains label",
106			config: map[string]any{
107				"config": map[string]any{
108					"Labels": map[string]any{
109						"gordon.domains": "app1.example.com, app2.example.com",
110					},
111				},
112			},
113			expected: &domain.ImageLabels{
114				Domains: []string{"app1.example.com", "app2.example.com"},
115			},
116		},
117		{
118			name: "all gordon labels",
119			config: map[string]any{
120				"config": map[string]any{
121					"Labels": map[string]any{
122						"gordon.domain":     "app.example.com",
123						"gordon.health":     "/healthz",
124						"gordon.proxy.port": "8080",
125						"gordon.env-file":   ".env.prod",
126					},
127				},
128			},
129			expected: &domain.ImageLabels{
130				Domain:  "app.example.com",
131				Health:  "/healthz",
132				Port:    "8080",
133				EnvFile: ".env.prod",
134			},
135		},
136		{
137			name: "no labels",
138			config: map[string]any{
139				"config": map[string]any{
140					"Labels": nil,
141				},
142			},
143			expected: &domain.ImageLabels{},
144		},
145		{
146			name: "no gordon labels",
147			config: map[string]any{
148				"config": map[string]any{
149					"Labels": map[string]any{
150						"other.label": "value",
151					},
152				},
153			},
154			expected: &domain.ImageLabels{},
155		},
156		{
157			name: "whitespace in labels",
158			config: map[string]any{
159				"config": map[string]any{
160					"Labels": map[string]any{
161						"gordon.domain": "  app.example.com  ",
162					},
163				},
164			},
165			expected: &domain.ImageLabels{
166				Domain: "app.example.com",
167			},
168		},
169		{
170			name: "deprecated port label fallback",
171			config: map[string]any{
172				"config": map[string]any{
173					"Labels": map[string]any{
174						"gordon.port": "9090",
175					},
176				},
177			},
178			expected: &domain.ImageLabels{
179				Port: "9090",
180			},
181		},
182		{
183			name: "proxy.port takes precedence over port",
184			config: map[string]any{
185				"config": map[string]any{
186					"Labels": map[string]any{
187						"gordon.proxy.port": "8080",
188						"gordon.port":       "9090",
189					},
190				},
191			},
192			expected: &domain.ImageLabels{
193				Port: "8080",
194			},
195		},
196	}
197
198	for _, tt := range tests {
199		t.Run(tt.name, func(t *testing.T) {
200			data, err := json.Marshal(tt.config)
201			require.NoError(t, err)
202
203			result, err := ParseImageLabels(data)
204			require.NoError(t, err)
205
206			assert.Equal(t, tt.expected.Domain, result.Domain)
207			assert.Equal(t, tt.expected.Domains, result.Domains)
208			assert.Equal(t, tt.expected.Health, result.Health)
209			assert.Equal(t, tt.expected.Port, result.Port)
210			assert.Equal(t, tt.expected.EnvFile, result.EnvFile)
211		})
212	}
213}
214
215func TestParseImageLabels_InvalidJSON(t *testing.T) {
216	_, err := ParseImageLabels([]byte("invalid json"))
217	assert.Error(t, err)
218}
219
220func TestExtractLabels(t *testing.T) {
221	ctx := context.Background()
222
223	manifest := map[string]any{
224		"schemaVersion": 2,
225		"config": map[string]any{
226			"digest": "sha256:configdigest123",
227		},
228	}
229	manifestData, err := json.Marshal(manifest)
230	require.NoError(t, err)
231
232	imageConf := map[string]any{
233		"config": map[string]any{
234			"Labels": map[string]any{
235				"gordon.domain": "app.example.com",
236				"gordon.health": "/healthz",
237			},
238		},
239	}
240	configData, err := json.Marshal(imageConf)
241	require.NoError(t, err)
242
243	blobStorage := mocks.NewMockBlobStorage(t)
244	blobStorage.EXPECT().GetBlob("sha256:configdigest123").Return(io.NopCloser(strings.NewReader(string(configData))), nil)
245
246	labels, err := ExtractLabels(ctx, manifestData, blobStorage)
247	require.NoError(t, err)
248	assert.Equal(t, "app.example.com", labels.Domain)
249	assert.Equal(t, "/healthz", labels.Health)
250}
251
252func TestExtractLabels_InvalidManifest(t *testing.T) {
253	ctx := context.Background()
254	blobStorage := mocks.NewMockBlobStorage(t)
255
256	_, err := ExtractLabels(ctx, []byte("bad json"), blobStorage)
257	assert.Error(t, err)
258}
259
260func TestExtractLabels_EmptyConfigDigest(t *testing.T) {
261	ctx := context.Background()
262
263	manifest := map[string]any{
264		"schemaVersion": 2,
265		"config": map[string]any{
266			"digest": "",
267		},
268	}
269	manifestData, err := json.Marshal(manifest)
270	require.NoError(t, err)
271
272	blobStorage := mocks.NewMockBlobStorage(t)
273
274	_, err = ExtractLabels(ctx, manifestData, blobStorage)
275	assert.Error(t, err)
276	assert.Contains(t, err.Error(), "no config digest")
277}

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