v2.10.2 705579a

service_test.go RawHistoryFolder

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

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