v2.2.6 c872d45

service_test.go RawHistoryFolder

1package registry
2
3import (
4	"bytes"
5	"context"
6	"errors"
7	"io"
8	"testing"
9
10	"github.com/bnema/zerowrap"
11	"github.com/stretchr/testify/assert"
12	"github.com/stretchr/testify/mock"
13
14	"gordon/internal/boundaries/out/mocks"
15	"gordon/internal/domain"
16)
17
18func testContext() context.Context {
19	return zerowrap.WithCtx(context.Background(), zerowrap.Default())
20}
21
22func TestService_GetManifest_Success(t *testing.T) {
23	blobStorage := mocks.NewMockBlobStorage(t)
24	manifestStorage := mocks.NewMockManifestStorage(t)
25	eventBus := mocks.NewMockEventPublisher(t)
26
27	svc := NewService(blobStorage, manifestStorage, eventBus)
28	ctx := testContext()
29
30	manifestData := []byte(`{"schemaVersion": 2}`)
31	manifestStorage.EXPECT().GetManifest("myapp", "latest").Return(manifestData, "application/vnd.docker.distribution.manifest.v2+json", nil)
32
33	result, err := svc.GetManifest(ctx, "myapp", "latest")
34
35	assert.NoError(t, err)
36	assert.NotNil(t, result)
37	assert.Equal(t, "myapp", result.Name)
38	assert.Equal(t, "latest", result.Reference)
39	assert.Equal(t, "application/vnd.docker.distribution.manifest.v2+json", result.ContentType)
40	assert.Equal(t, manifestData, result.Data)
41}
42
43func TestService_GetManifest_NotFound(t *testing.T) {
44	blobStorage := mocks.NewMockBlobStorage(t)
45	manifestStorage := mocks.NewMockManifestStorage(t)
46	eventBus := mocks.NewMockEventPublisher(t)
47
48	svc := NewService(blobStorage, manifestStorage, eventBus)
49	ctx := testContext()
50
51	manifestStorage.EXPECT().GetManifest("myapp", "notexists").Return(nil, "", errors.New("manifest not found"))
52
53	result, err := svc.GetManifest(ctx, "myapp", "notexists")
54
55	assert.Error(t, err)
56	assert.Nil(t, result)
57	assert.Contains(t, err.Error(), "failed to get manifest")
58}
59
60func TestService_PutManifest_Success(t *testing.T) {
61	blobStorage := mocks.NewMockBlobStorage(t)
62	manifestStorage := mocks.NewMockManifestStorage(t)
63	eventBus := mocks.NewMockEventPublisher(t)
64
65	svc := NewService(blobStorage, manifestStorage, eventBus)
66	ctx := testContext()
67
68	manifest := &domain.Manifest{
69		Name:        "myapp",
70		Reference:   "latest",
71		ContentType: "application/vnd.docker.distribution.manifest.v2+json",
72		Data:        []byte(`{"schemaVersion": 2}`),
73	}
74
75	manifestStorage.EXPECT().PutManifest("myapp", "latest", "application/vnd.docker.distribution.manifest.v2+json", manifest.Data).Return(nil)
76	eventBus.EXPECT().Publish(domain.EventImagePushed, mock.AnythingOfType("domain.ImagePushedPayload")).Return(nil)
77
78	err := svc.PutManifest(ctx, manifest)
79
80	assert.NoError(t, err)
81}
82
83func TestService_PutManifest_StorageError(t *testing.T) {
84	blobStorage := mocks.NewMockBlobStorage(t)
85	manifestStorage := mocks.NewMockManifestStorage(t)
86	eventBus := mocks.NewMockEventPublisher(t)
87
88	svc := NewService(blobStorage, manifestStorage, eventBus)
89	ctx := testContext()
90
91	manifest := &domain.Manifest{
92		Name:        "myapp",
93		Reference:   "latest",
94		ContentType: "application/vnd.docker.distribution.manifest.v2+json",
95		Data:        []byte(`{"schemaVersion": 2}`),
96	}
97
98	manifestStorage.EXPECT().PutManifest("myapp", "latest", "application/vnd.docker.distribution.manifest.v2+json", manifest.Data).Return(errors.New("storage full"))
99
100	err := svc.PutManifest(ctx, manifest)
101
102	assert.Error(t, err)
103	assert.Contains(t, err.Error(), "failed to store manifest")
104}
105
106func TestService_PutManifest_EventPublishError(t *testing.T) {
107	blobStorage := mocks.NewMockBlobStorage(t)
108	manifestStorage := mocks.NewMockManifestStorage(t)
109	eventBus := mocks.NewMockEventPublisher(t)
110
111	svc := NewService(blobStorage, manifestStorage, eventBus)
112	ctx := testContext()
113
114	manifest := &domain.Manifest{
115		Name:        "myapp",
116		Reference:   "latest",
117		ContentType: "application/vnd.docker.distribution.manifest.v2+json",
118		Data:        []byte(`{"schemaVersion": 2}`),
119	}
120
121	manifestStorage.EXPECT().PutManifest("myapp", "latest", "application/vnd.docker.distribution.manifest.v2+json", manifest.Data).Return(nil)
122	eventBus.EXPECT().Publish(domain.EventImagePushed, mock.Anything).Return(errors.New("event bus error"))
123
124	// Should still succeed - event publish error is logged but doesn't fail the operation
125	err := svc.PutManifest(ctx, manifest)
126
127	assert.NoError(t, err)
128}
129
130func TestService_PutManifest_NilEventBus(t *testing.T) {
131	blobStorage := mocks.NewMockBlobStorage(t)
132	manifestStorage := mocks.NewMockManifestStorage(t)
133
134	// Nil event bus
135	svc := NewService(blobStorage, manifestStorage, nil)
136	ctx := testContext()
137
138	manifest := &domain.Manifest{
139		Name:        "myapp",
140		Reference:   "latest",
141		ContentType: "application/vnd.docker.distribution.manifest.v2+json",
142		Data:        []byte(`{"schemaVersion": 2}`),
143	}
144
145	manifestStorage.EXPECT().PutManifest("myapp", "latest", "application/vnd.docker.distribution.manifest.v2+json", manifest.Data).Return(nil)
146
147	err := svc.PutManifest(ctx, manifest)
148
149	assert.NoError(t, err)
150}
151
152func TestService_DeleteManifest_Success(t *testing.T) {
153	blobStorage := mocks.NewMockBlobStorage(t)
154	manifestStorage := mocks.NewMockManifestStorage(t)
155	eventBus := mocks.NewMockEventPublisher(t)
156
157	svc := NewService(blobStorage, manifestStorage, eventBus)
158	ctx := testContext()
159
160	manifestStorage.EXPECT().DeleteManifest("myapp", "latest").Return(nil)
161
162	err := svc.DeleteManifest(ctx, "myapp", "latest")
163
164	assert.NoError(t, err)
165}
166
167func TestService_DeleteManifest_Error(t *testing.T) {
168	blobStorage := mocks.NewMockBlobStorage(t)
169	manifestStorage := mocks.NewMockManifestStorage(t)
170	eventBus := mocks.NewMockEventPublisher(t)
171
172	svc := NewService(blobStorage, manifestStorage, eventBus)
173	ctx := testContext()
174
175	manifestStorage.EXPECT().DeleteManifest("myapp", "latest").Return(errors.New("manifest not found"))
176
177	err := svc.DeleteManifest(ctx, "myapp", "latest")
178
179	assert.Error(t, err)
180	assert.Contains(t, err.Error(), "failed to delete manifest")
181}
182
183func TestService_GetBlob_Success(t *testing.T) {
184	blobStorage := mocks.NewMockBlobStorage(t)
185	manifestStorage := mocks.NewMockManifestStorage(t)
186	eventBus := mocks.NewMockEventPublisher(t)
187
188	svc := NewService(blobStorage, manifestStorage, eventBus)
189	ctx := testContext()
190
191	blobData := []byte("blob content")
192	blobStorage.EXPECT().GetBlob("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4").Return(io.NopCloser(bytes.NewReader(blobData)), nil)
193
194	reader, err := svc.GetBlob(ctx, "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")
195
196	assert.NoError(t, err)
197	assert.NotNil(t, reader)
198
199	data, _ := io.ReadAll(reader)
200	assert.Equal(t, blobData, data)
201}
202
203func TestService_GetBlob_NotFound(t *testing.T) {
204	blobStorage := mocks.NewMockBlobStorage(t)
205	manifestStorage := mocks.NewMockManifestStorage(t)
206	eventBus := mocks.NewMockEventPublisher(t)
207
208	svc := NewService(blobStorage, manifestStorage, eventBus)
209	ctx := testContext()
210
211	blobStorage.EXPECT().GetBlob("sha256:notexists").Return(nil, errors.New("blob not found"))
212
213	reader, err := svc.GetBlob(ctx, "sha256:notexists")
214
215	assert.Error(t, err)
216	assert.Nil(t, reader)
217	assert.Contains(t, err.Error(), "failed to get blob")
218}
219
220func TestService_PutBlob_Success(t *testing.T) {
221	blobStorage := mocks.NewMockBlobStorage(t)
222	manifestStorage := mocks.NewMockManifestStorage(t)
223	eventBus := mocks.NewMockEventPublisher(t)
224
225	svc := NewService(blobStorage, manifestStorage, eventBus)
226	ctx := testContext()
227
228	blobData := []byte("blob content")
229	reader := bytes.NewReader(blobData)
230
231	blobStorage.EXPECT().PutBlob("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", mock.Anything, int64(len(blobData))).Return(nil)
232
233	err := svc.PutBlob(ctx, "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", reader, int64(len(blobData)))
234
235	assert.NoError(t, err)
236}
237
238func TestService_PutBlob_Error(t *testing.T) {
239	blobStorage := mocks.NewMockBlobStorage(t)
240	manifestStorage := mocks.NewMockManifestStorage(t)
241	eventBus := mocks.NewMockEventPublisher(t)
242
243	svc := NewService(blobStorage, manifestStorage, eventBus)
244	ctx := testContext()
245
246	blobData := []byte("blob content")
247	reader := bytes.NewReader(blobData)
248
249	blobStorage.EXPECT().PutBlob("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", mock.Anything, int64(len(blobData))).Return(errors.New("storage full"))
250
251	err := svc.PutBlob(ctx, "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", reader, int64(len(blobData)))
252
253	assert.Error(t, err)
254	assert.Contains(t, err.Error(), "failed to store blob")
255}
256
257func TestService_BlobExists(t *testing.T) {
258	blobStorage := mocks.NewMockBlobStorage(t)
259	manifestStorage := mocks.NewMockManifestStorage(t)
260	eventBus := mocks.NewMockEventPublisher(t)
261
262	svc := NewService(blobStorage, manifestStorage, eventBus)
263	ctx := testContext()
264
265	blobStorage.EXPECT().BlobExists("sha256:exists").Return(true)
266	blobStorage.EXPECT().BlobExists("sha256:notexists").Return(false)
267
268	assert.True(t, svc.BlobExists(ctx, "sha256:exists"))
269	assert.False(t, svc.BlobExists(ctx, "sha256:notexists"))
270}
271
272func TestService_StartUpload_Success(t *testing.T) {
273	blobStorage := mocks.NewMockBlobStorage(t)
274	manifestStorage := mocks.NewMockManifestStorage(t)
275	eventBus := mocks.NewMockEventPublisher(t)
276
277	svc := NewService(blobStorage, manifestStorage, eventBus)
278	ctx := testContext()
279
280	blobStorage.EXPECT().StartBlobUpload("myapp").Return("1234567890-myapp", nil)
281
282	uuid, err := svc.StartUpload(ctx, "myapp")
283
284	assert.NoError(t, err)
285	assert.Equal(t, "1234567890-myapp", uuid)
286}
287
288func TestService_StartUpload_Error(t *testing.T) {
289	blobStorage := mocks.NewMockBlobStorage(t)
290	manifestStorage := mocks.NewMockManifestStorage(t)
291	eventBus := mocks.NewMockEventPublisher(t)
292
293	svc := NewService(blobStorage, manifestStorage, eventBus)
294	ctx := testContext()
295
296	blobStorage.EXPECT().StartBlobUpload("myapp").Return("", errors.New("cannot create upload"))
297
298	uuid, err := svc.StartUpload(ctx, "myapp")
299
300	assert.Error(t, err)
301	assert.Empty(t, uuid)
302	assert.Contains(t, err.Error(), "failed to start blob upload")
303}
304
305func TestService_FinishUpload_Success(t *testing.T) {
306	blobStorage := mocks.NewMockBlobStorage(t)
307	manifestStorage := mocks.NewMockManifestStorage(t)
308	eventBus := mocks.NewMockEventPublisher(t)
309
310	svc := NewService(blobStorage, manifestStorage, eventBus)
311	ctx := testContext()
312
313	blobStorage.EXPECT().FinishBlobUpload("1234567890-myapp", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4").Return(nil)
314
315	err := svc.FinishUpload(ctx, "1234567890-myapp", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")
316
317	assert.NoError(t, err)
318}
319
320func TestService_FinishUpload_Error(t *testing.T) {
321	blobStorage := mocks.NewMockBlobStorage(t)
322	manifestStorage := mocks.NewMockManifestStorage(t)
323	eventBus := mocks.NewMockEventPublisher(t)
324
325	svc := NewService(blobStorage, manifestStorage, eventBus)
326	ctx := testContext()
327
328	blobStorage.EXPECT().FinishBlobUpload("1234567890-myapp", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4").Return(errors.New("digest mismatch"))
329
330	err := svc.FinishUpload(ctx, "1234567890-myapp", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")
331
332	assert.Error(t, err)
333	assert.Contains(t, err.Error(), "failed to finish blob upload")
334}
335
336func TestService_CancelUpload_Success(t *testing.T) {
337	blobStorage := mocks.NewMockBlobStorage(t)
338	manifestStorage := mocks.NewMockManifestStorage(t)
339	eventBus := mocks.NewMockEventPublisher(t)
340
341	svc := NewService(blobStorage, manifestStorage, eventBus)
342	ctx := testContext()
343
344	blobStorage.EXPECT().CancelBlobUpload("1234567890-myapp").Return(nil)
345
346	err := svc.CancelUpload(ctx, "1234567890-myapp")
347
348	assert.NoError(t, err)
349}
350
351func TestService_CancelUpload_Error(t *testing.T) {
352	blobStorage := mocks.NewMockBlobStorage(t)
353	manifestStorage := mocks.NewMockManifestStorage(t)
354	eventBus := mocks.NewMockEventPublisher(t)
355
356	svc := NewService(blobStorage, manifestStorage, eventBus)
357	ctx := testContext()
358
359	blobStorage.EXPECT().CancelBlobUpload("1234567890-myapp").Return(errors.New("upload not found"))
360
361	err := svc.CancelUpload(ctx, "1234567890-myapp")
362
363	assert.Error(t, err)
364	assert.Contains(t, err.Error(), "failed to cancel blob upload")
365}
366
367func TestService_ListTags_Success(t *testing.T) {
368	blobStorage := mocks.NewMockBlobStorage(t)
369	manifestStorage := mocks.NewMockManifestStorage(t)
370	eventBus := mocks.NewMockEventPublisher(t)
371
372	svc := NewService(blobStorage, manifestStorage, eventBus)
373	ctx := testContext()
374
375	manifestStorage.EXPECT().ListTags("myapp").Return([]string{"latest", "v1.0", "v2.0"}, nil)
376
377	tags, err := svc.ListTags(ctx, "myapp")
378
379	assert.NoError(t, err)
380	assert.Equal(t, []string{"latest", "v1.0", "v2.0"}, tags)
381}
382
383func TestService_ListTags_Error(t *testing.T) {
384	blobStorage := mocks.NewMockBlobStorage(t)
385	manifestStorage := mocks.NewMockManifestStorage(t)
386	eventBus := mocks.NewMockEventPublisher(t)
387
388	svc := NewService(blobStorage, manifestStorage, eventBus)
389	ctx := testContext()
390
391	manifestStorage.EXPECT().ListTags("myapp").Return(nil, errors.New("repository not found"))
392
393	tags, err := svc.ListTags(ctx, "myapp")
394
395	assert.Error(t, err)
396	assert.Nil(t, tags)
397	assert.Contains(t, err.Error(), "failed to list tags")
398}
399
400func TestService_ListRepositories_Success(t *testing.T) {
401	blobStorage := mocks.NewMockBlobStorage(t)
402	manifestStorage := mocks.NewMockManifestStorage(t)
403	eventBus := mocks.NewMockEventPublisher(t)
404
405	svc := NewService(blobStorage, manifestStorage, eventBus)
406	ctx := testContext()
407
408	manifestStorage.EXPECT().ListRepositories().Return([]string{"myapp", "otherapp", "thirdapp"}, nil)
409
410	repos, err := svc.ListRepositories(ctx)
411
412	assert.NoError(t, err)
413	assert.Equal(t, []string{"myapp", "otherapp", "thirdapp"}, repos)
414}
415
416func TestService_ListRepositories_Error(t *testing.T) {
417	blobStorage := mocks.NewMockBlobStorage(t)
418	manifestStorage := mocks.NewMockManifestStorage(t)
419	eventBus := mocks.NewMockEventPublisher(t)
420
421	svc := NewService(blobStorage, manifestStorage, eventBus)
422	ctx := testContext()
423
424	manifestStorage.EXPECT().ListRepositories().Return(nil, errors.New("storage error"))
425
426	repos, err := svc.ListRepositories(ctx)
427
428	assert.Error(t, err)
429	assert.Nil(t, repos)
430	assert.Contains(t, err.Error(), "failed to list repositories")
431}

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