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{"latest", "v1.0", "v2.0"}, nil)
379
380 tags, err := svc.ListTags(ctx, "myapp")
381
382 assert.NoError(t, err)
383 assert.Equal(t, []string{"latest", "v1.0", "v2.0"}, tags)
384}
385
386func TestService_ListTags_Error(t *testing.T) {
387 blobStorage := mocks.NewMockBlobStorage(t)
388 manifestStorage := mocks.NewMockManifestStorage(t)
389 eventBus := mocks.NewMockEventPublisher(t)
390
391 svc := NewService(blobStorage, manifestStorage, eventBus)
392 ctx := testContext()
393
394 manifestStorage.EXPECT().ListTags("myapp").Return(nil, errors.New("repository not found"))
395
396 tags, err := svc.ListTags(ctx, "myapp")
397
398 assert.Error(t, err)
399 assert.Nil(t, tags)
400 assert.Contains(t, err.Error(), "failed to list tags")
401}
402
403func TestService_ListRepositories_Success(t *testing.T) {
404 blobStorage := mocks.NewMockBlobStorage(t)
405 manifestStorage := mocks.NewMockManifestStorage(t)
406 eventBus := mocks.NewMockEventPublisher(t)
407
408 svc := NewService(blobStorage, manifestStorage, eventBus)
409 ctx := testContext()
410
411 manifestStorage.EXPECT().ListRepositories().Return([]string{"myapp", "otherapp", "thirdapp"}, nil)
412
413 repos, err := svc.ListRepositories(ctx)
414
415 assert.NoError(t, err)
416 assert.Equal(t, []string{"myapp", "otherapp", "thirdapp"}, repos)
417}
418
419func TestService_ListRepositories_Error(t *testing.T) {
420 blobStorage := mocks.NewMockBlobStorage(t)
421 manifestStorage := mocks.NewMockManifestStorage(t)
422 eventBus := mocks.NewMockEventPublisher(t)
423
424 svc := NewService(blobStorage, manifestStorage, eventBus)
425 ctx := testContext()
426
427 manifestStorage.EXPECT().ListRepositories().Return(nil, errors.New("storage error"))
428
429 repos, err := svc.ListRepositories(ctx)
430
431 assert.Error(t, err)
432 assert.Nil(t, repos)
433 assert.Contains(t, err.Error(), "failed to list repositories")
434}