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}