@@ -2,6 +2,7 @@ package internal
22
33import (
44 "fmt"
5+ "strings"
56 "testing"
67
78 "github.com/stretchr/testify/require"
@@ -302,3 +303,152 @@ func Test_EnsureGetBundleSizeValue(t *testing.T) {
302303 })
303304 }
304305}
306+
307+ func TestValidateRelatedImages (t * testing.T ) {
308+ tests := []struct {
309+ name string
310+ relatedImages []v1alpha1.RelatedImage
311+ wantError bool
312+ errCount int
313+ errContains []string
314+ }{
315+ {
316+ name : "no related images should pass" ,
317+ relatedImages : []v1alpha1.RelatedImage {},
318+ wantError : false ,
319+ },
320+ {
321+ name : "valid image with tag should pass" ,
322+ relatedImages : []v1alpha1.RelatedImage {
323+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator:v1.0.0" },
324+ },
325+ wantError : false ,
326+ },
327+ {
328+ name : "valid image with digest should pass" ,
329+ relatedImages : []v1alpha1.RelatedImage {
330+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator@sha256:abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" },
331+ },
332+ wantError : false ,
333+ },
334+ {
335+ name : "valid image with tag and digest should pass" ,
336+ relatedImages : []v1alpha1.RelatedImage {
337+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator:v1.0.0@sha256:abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" },
338+ },
339+ wantError : false ,
340+ },
341+ {
342+ name : "valid image without tag (latest implied) should pass" ,
343+ relatedImages : []v1alpha1.RelatedImage {
344+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator" },
345+ },
346+ wantError : false ,
347+ },
348+ {
349+ name : "multiple valid images should pass" ,
350+ relatedImages : []v1alpha1.RelatedImage {
351+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator:v1.0.0" },
352+ {Name : "operand" , Image : "gcr.io/my-project/my-operand@sha256:abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" },
353+ {Name : "init" , Image : "docker.io/library/busybox:latest" },
354+ },
355+ wantError : false ,
356+ },
357+ {
358+ name : "empty image field should error" ,
359+ relatedImages : []v1alpha1.RelatedImage {
360+ {Name : "operator" , Image : "" },
361+ },
362+ wantError : true ,
363+ errCount : 1 ,
364+ errContains : []string {"empty image field" },
365+ },
366+ {
367+ name : "invalid image with spaces should error" ,
368+ relatedImages : []v1alpha1.RelatedImage {
369+ {Name : "operator" , Image : "invalid image name" },
370+ },
371+ wantError : true ,
372+ errCount : 1 ,
373+ errContains : []string {"invalid image pullspec" },
374+ },
375+ {
376+ name : "invalid image with uppercase should error" ,
377+ relatedImages : []v1alpha1.RelatedImage {
378+ {Name : "operator" , Image : "quay.io/Operator-Framework/my-operator:v1.0.0" },
379+ },
380+ wantError : true ,
381+ errCount : 1 ,
382+ errContains : []string {"invalid image pullspec" , "Operator-Framework" },
383+ },
384+ {
385+ name : "invalid image with special characters should error" ,
386+ relatedImages : []v1alpha1.RelatedImage {
387+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator:v1.0.0!" },
388+ },
389+ wantError : true ,
390+ errCount : 1 ,
391+ errContains : []string {"invalid image pullspec" },
392+ },
393+ {
394+ name : "invalid digest algorithm should error" ,
395+ relatedImages : []v1alpha1.RelatedImage {
396+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator@ssha256:abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" },
397+ },
398+ wantError : true ,
399+ errCount : 1 ,
400+ errContains : []string {"invalid image pullspec" },
401+ },
402+ {
403+ name : "multiple errors should all be reported" ,
404+ relatedImages : []v1alpha1.RelatedImage {
405+ {Name : "operator" , Image : "" },
406+ {Name : "operand" , Image : "invalid image" },
407+ },
408+ wantError : true ,
409+ errCount : 2 ,
410+ errContains : []string {"relatedImages[0]" , "relatedImages[1]" },
411+ },
412+ {
413+ name : "mixed valid and invalid images should error" ,
414+ relatedImages : []v1alpha1.RelatedImage {
415+ {Name : "operator" , Image : "quay.io/operator-framework/my-operator:v1.0.0" },
416+ {Name : "bad" , Image : "invalid image" },
417+ },
418+ wantError : true ,
419+ errCount : 1 ,
420+ errContains : []string {"relatedImages[1]" , "invalid image pullspec" },
421+ },
422+ }
423+
424+ for _ , tt := range tests {
425+ t .Run (tt .name , func (t * testing.T ) {
426+ bundle := & manifests.Bundle {
427+ CSV : & v1alpha1.ClusterServiceVersion {
428+ Spec : v1alpha1.ClusterServiceVersionSpec {
429+ RelatedImages : tt .relatedImages ,
430+ },
431+ },
432+ }
433+
434+ errs := validateRelatedImages (bundle )
435+
436+ if tt .wantError {
437+ require .Equal (t , tt .errCount , len (errs ), "expected %d errors but got %d" , tt .errCount , len (errs ))
438+ // Check that each expected string appears in at least one error
439+ for _ , expectedStr := range tt .errContains {
440+ found := false
441+ for _ , err := range errs {
442+ if strings .Contains (err .Error (), expectedStr ) {
443+ found = true
444+ break
445+ }
446+ }
447+ require .True (t , found , "expected to find %q in error messages" , expectedStr )
448+ }
449+ } else {
450+ require .Equal (t , 0 , len (errs ), "expected no errors but got: %v" , errs )
451+ }
452+ })
453+ }
454+ }
0 commit comments