@@ -7,8 +7,11 @@ import (
77 "testing"
88
99 "github.com/stretchr/testify/require"
10+ "gotest.tools/v3/assert"
1011 appsv1 "k8s.io/api/apps/v1"
1112 corev1 "k8s.io/api/core/v1"
13+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+ "k8s.io/apimachinery/pkg/runtime/schema"
1215 "sigs.k8s.io/controller-runtime/pkg/client"
1316
1417 "github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -267,3 +270,162 @@ func Test_BundleValidatorCallsAllValidationFnsInOrder(t *testing.T) {
267270 require .NoError (t , val .Validate (nil ))
268271 require .Equal (t , "hi" , actual )
269272}
273+ func Test_Render_WithNilBundleValidator (t * testing.T ) {
274+ defer func () {
275+ if r := recover (); r == nil {
276+ t .Error ("expected panic when BundleValidator is nil" )
277+ }
278+ }()
279+
280+ renderer := render.BundleRenderer {}
281+ _ , _ = renderer .Render (bundle.RegistryV1 {}, "ns" )
282+ }
283+
284+ func Test_Render_WithNoResourceGenerators (t * testing.T ) {
285+ renderer := render.BundleRenderer {
286+ BundleValidator : render.BundleValidator {
287+ func (_ * bundle.RegistryV1 ) []error { return nil },
288+ },
289+ }
290+
291+ _ , err := renderer .Render (bundle.RegistryV1 {
292+ CSV : MakeCSV (WithInstallModeSupportFor (v1alpha1 .InstallModeTypeAllNamespaces )),
293+ }, "test" , render .WithTargetNamespaces ("test" ), render .WithUniqueNameGenerator (render .DefaultUniqueNameGenerator ))
294+
295+ require .NoError (t , err )
296+ }
297+
298+ func Test_Render_ReturnsExactSameObjectReferences (t * testing.T ) {
299+ obj1 := fakeUnstructured ("Service" , "ns" , "svc" )
300+ obj2 := fakeUnstructured ("ConfigMap" , "ns" , "cfg" )
301+
302+ mockGen := render .ResourceGenerator (func (_ * bundle.RegistryV1 , _ render.Options ) ([]client.Object , error ) {
303+ return []client.Object {obj1 , obj2 }, nil
304+ })
305+
306+ renderer := render.BundleRenderer {
307+ BundleValidator : render.BundleValidator {func (_ * bundle.RegistryV1 ) []error { return nil }},
308+ ResourceGenerators : []render.ResourceGenerator {mockGen },
309+ }
310+
311+ result , err := renderer .Render (bundle.RegistryV1 {
312+ CSV : MakeCSV (WithInstallModeSupportFor (v1alpha1 .InstallModeTypeAllNamespaces )),
313+ }, "ns" , render .WithTargetNamespaces ("ns" ), render .WithUniqueNameGenerator (render .DefaultUniqueNameGenerator ))
314+
315+ require .NoError (t , err )
316+ require .Same (t , obj1 , result [0 ])
317+ require .Same (t , obj2 , result [1 ])
318+ }
319+
320+ // Test_Render_ValidatesOutputForAllInstallModes ensures that the BundleRenderer
321+ // correctly generates and returns the exact list of client.Objects produced by the
322+ // ResourceGenerators, across all supported install modes (AllNamespaces, SingleNamespace, OwnNamespace).
323+ func Test_Render_ValidatesOutputForAllInstallModes (t * testing.T ) {
324+ testCases := []struct {
325+ name string
326+ installNamespace string
327+ watchNamespace string
328+ installModes []v1alpha1.InstallMode
329+ expectedNS string
330+ }{
331+ {
332+ name : "AllNamespaces" ,
333+ installNamespace : "mock-system" ,
334+ watchNamespace : "" ,
335+ installModes : []v1alpha1.InstallMode {
336+ {Type : v1alpha1 .InstallModeTypeAllNamespaces , Supported : true },
337+ },
338+ expectedNS : "mock-system" ,
339+ },
340+ {
341+ name : "SingleNamespace" ,
342+ installNamespace : "mock-system" ,
343+ watchNamespace : "mock-watch" ,
344+ installModes : []v1alpha1.InstallMode {
345+ {Type : v1alpha1 .InstallModeTypeSingleNamespace , Supported : true },
346+ },
347+ expectedNS : "mock-watch" ,
348+ },
349+ {
350+ name : "OwnNamespace" ,
351+ installNamespace : "mock-system" ,
352+ watchNamespace : "mock-system" ,
353+ installModes : []v1alpha1.InstallMode {
354+ {Type : v1alpha1 .InstallModeTypeOwnNamespace , Supported : true },
355+ },
356+ expectedNS : "mock-system" ,
357+ },
358+ }
359+
360+ for _ , tc := range testCases {
361+ t .Run (tc .name , func (t * testing.T ) {
362+ // Given the mocks
363+ expectedObjects := []client.Object {
364+ fakeUnstructured ("ClusterRole" , "" , "mock-operator.v0-role" ),
365+ fakeUnstructured ("ClusterRoleBinding" , "" , "mock-operator.v0-binding" ),
366+ fakeUnstructured ("ConfigMap" , tc .expectedNS , "mock-operator-manager-config" ),
367+ fakeUnstructured ("CustomResourceDefinition" , "" , "mocks.argoproj.io" ),
368+ fakeUnstructured ("Deployment" , tc .expectedNS , "mock-operator-controller-manager" ),
369+ fakeUnstructured ("Service" , tc .expectedNS , "mock-operator-controller-manager-metrics-service" ),
370+ fakeUnstructured ("ServiceAccount" , tc .expectedNS , "mock-operator-controller-manager" ),
371+ }
372+
373+ mockGen := render .ResourceGenerator (func (_ * bundle.RegistryV1 , _ render.Options ) ([]client.Object , error ) {
374+ return expectedObjects , nil
375+ })
376+
377+ mockBundle := bundle.RegistryV1 {
378+ CSV : v1alpha1.ClusterServiceVersion {
379+ Spec : v1alpha1.ClusterServiceVersionSpec {
380+ InstallModes : tc .installModes ,
381+ },
382+ },
383+ }
384+
385+ renderer := render.BundleRenderer {
386+ BundleValidator : render.BundleValidator {
387+ func (_ * bundle.RegistryV1 ) []error { return nil },
388+ },
389+ ResourceGenerators : []render.ResourceGenerator {mockGen },
390+ }
391+
392+ opts := []render.Option {
393+ render .WithTargetNamespaces (tc .watchNamespace ),
394+ render .WithUniqueNameGenerator (render .DefaultUniqueNameGenerator ),
395+ }
396+
397+ // When we call:
398+ objs , err := renderer .Render (mockBundle , tc .installNamespace , opts ... )
399+
400+ // We expected have no error and ensure that the objects
401+ // returned match with the resource generator's output.
402+ require .NoError (t , err )
403+ require .Len (t , objs , len (expectedObjects ))
404+
405+ for i , obj := range objs {
406+ exp := expectedObjects [i ]
407+ assert .Equal (t , exp .GetObjectKind ().GroupVersionKind ().Kind , obj .GetObjectKind ().GroupVersionKind ().Kind )
408+ assert .Equal (t , exp .GetName (), obj .GetName (), "unexpected name at index %d" , i )
409+ assert .Equal (t , exp .GetNamespace (), obj .GetNamespace (), "unexpected namespace at index %d" , i )
410+ }
411+ })
412+ }
413+ }
414+
415+ func fakeUnstructured (kind , namespace , name string ) client.Object {
416+ obj := & unstructured.Unstructured {}
417+ group := ""
418+ version := "v1"
419+ if kind == "CustomResourceDefinition" {
420+ group = "apiextensions.k8s.io"
421+ version = "v1"
422+ }
423+ obj .SetGroupVersionKind (schema.GroupVersionKind {
424+ Group : group ,
425+ Version : version ,
426+ Kind : kind ,
427+ })
428+ obj .SetNamespace (namespace )
429+ obj .SetName (name )
430+ return obj
431+ }
0 commit comments