Skip to content

Commit f7a2e82

Browse files
Remove extra bundles argo used to try to validate regretion in favor of cover scenarios with mocks and unit tests
1 parent 36809b3 commit f7a2e82

File tree

57 files changed

+185
-90111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+185
-90111
lines changed

Makefile

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,9 @@ generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyI
167167
$(CONTROLLER_GEN) --load-build-tags=$(GO_BUILD_TAGS) object:headerFile="hack/boilerplate.go.txt" paths="./..."
168168

169169
.PHONY: verify
170-
verify: k8s-pin kind-verify-versions fmt generate manifests crd-ref-docs generate-test-data #HELP Verify all generated code is up-to-date. Runs k8s-pin instead of just tidy.
170+
verify: k8s-pin kind-verify-versions fmt generate manifests crd-ref-docs #HELP Verify all generated code is up-to-date. Runs k8s-pin instead of just tidy.
171171
git diff --exit-code
172172

173-
# Renders registry+v1 bundles in test/convert
174-
# Used by CI in verify to catch regressions in the registry+v1 -> plain conversion code
175-
.PHONY: generate-test-data
176-
generate-test-data:
177-
go run test/convert/generate-manifests.go
178-
179173
.PHONY: fix-lint
180174
fix-lint: $(GOLANGCI_LINT) #EXHELP Fix lint issues
181175
$(GOLANGCI_LINT) run --fix --build-tags $(GO_BUILD_TAGS) $(GOLANGCI_LINT_ARGS)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ require (
3030
golang.org/x/sync v0.16.0
3131
golang.org/x/tools v0.35.0
3232
gopkg.in/yaml.v2 v2.4.0
33+
gotest.tools/v3 v3.5.2
3334
helm.sh/helm/v3 v3.18.4
3435
k8s.io/api v0.33.2
3536
k8s.io/apiextensions-apiserver v0.33.2

internal/operator-controller/rukpak/render/render_test.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import (
66
"reflect"
77
"testing"
88

9+
"github.com/google/go-cmp/cmp"
10+
"github.com/google/go-cmp/cmp/cmpopts"
911
"github.com/stretchr/testify/require"
1012
appsv1 "k8s.io/api/apps/v1"
1113
corev1 "k8s.io/api/core/v1"
14+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
15+
"k8s.io/apimachinery/pkg/runtime/schema"
1216
"sigs.k8s.io/controller-runtime/pkg/client"
1317

1418
"github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -267,3 +271,182 @@ func Test_BundleValidatorCallsAllValidationFnsInOrder(t *testing.T) {
267271
require.NoError(t, val.Validate(nil))
268272
require.Equal(t, "hi", actual)
269273
}
274+
275+
// Test_Render_ValidatesOutputForAllInstallModes ensures that the BundleRenderer
276+
// correctly generates and returns the exact list of client.Objects produced by the
277+
// ResourceGenerators, across all supported install modes (AllNamespaces, SingleNamespace, OwnNamespace).
278+
func Test_Render_ValidatesOutputForAllInstallModes(t *testing.T) {
279+
testCases := []struct {
280+
name string
281+
installNamespace string
282+
watchNamespace string
283+
installModes []v1alpha1.InstallMode
284+
expectedNS string
285+
}{
286+
{
287+
name: "AllNamespaces",
288+
installNamespace: "mock-system",
289+
watchNamespace: "",
290+
installModes: []v1alpha1.InstallMode{
291+
{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true},
292+
},
293+
expectedNS: "mock-system",
294+
},
295+
{
296+
name: "SingleNamespace",
297+
installNamespace: "mock-system",
298+
watchNamespace: "mock-watch",
299+
installModes: []v1alpha1.InstallMode{
300+
{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true},
301+
},
302+
expectedNS: "mock-watch",
303+
},
304+
{
305+
name: "OwnNamespace",
306+
installNamespace: "mock-system",
307+
watchNamespace: "mock-system",
308+
installModes: []v1alpha1.InstallMode{
309+
{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true},
310+
},
311+
expectedNS: "mock-system",
312+
},
313+
}
314+
315+
for _, tc := range testCases {
316+
t.Run(tc.name, func(t *testing.T) {
317+
expectedObjects := []client.Object{
318+
fakeUnstructured("ClusterRole", "", "mock-clusterrole"),
319+
fakeUnstructured("ClusterRoleBinding", "", "mock-clusterrolebinding"),
320+
fakeUnstructured("Role", tc.expectedNS, "mock-role"),
321+
fakeUnstructured("RoleBinding", tc.expectedNS, "mock-rolebinding"),
322+
fakeUnstructured("ConfigMap", tc.expectedNS, "mock-config"),
323+
fakeUnstructured("Secret", tc.expectedNS, "mock-secret"),
324+
fakeUnstructured("Service", tc.expectedNS, "mock-service"),
325+
fakeUnstructured("Deployment", tc.expectedNS, "mock-deployment"),
326+
fakeUnstructured("ServiceAccount", tc.expectedNS, "mock-sa"),
327+
fakeUnstructured("NetworkPolicy", tc.expectedNS, "mock-netpol"),
328+
}
329+
330+
mockGen := render.ResourceGenerator(func(_ *bundle.RegistryV1, _ render.Options) ([]client.Object, error) {
331+
return expectedObjects, nil
332+
})
333+
334+
mockBundle := bundle.RegistryV1{
335+
CSV: v1alpha1.ClusterServiceVersion{
336+
Spec: v1alpha1.ClusterServiceVersionSpec{
337+
InstallModes: tc.installModes,
338+
},
339+
},
340+
}
341+
342+
renderer := render.BundleRenderer{
343+
BundleValidator: render.BundleValidator{
344+
func(_ *bundle.RegistryV1) []error { return nil },
345+
},
346+
ResourceGenerators: []render.ResourceGenerator{mockGen},
347+
}
348+
349+
opts := []render.Option{
350+
render.WithTargetNamespaces(tc.watchNamespace),
351+
render.WithUniqueNameGenerator(render.DefaultUniqueNameGenerator),
352+
}
353+
354+
objs, err := renderer.Render(mockBundle, tc.installNamespace, opts...)
355+
require.NoError(t, err)
356+
require.Len(t, objs, len(expectedObjects))
357+
358+
// Compare expected vs actual objects
359+
gotMap := make(map[string]client.Object)
360+
for _, obj := range objs {
361+
gotMap[objectKey(obj)] = obj
362+
}
363+
364+
for _, exp := range expectedObjects {
365+
key := objectKey(exp)
366+
got, exists := gotMap[key]
367+
require.True(t, exists, "missing expected object: %s", key)
368+
369+
expObj := exp.(*unstructured.Unstructured)
370+
gotObj := got.(*unstructured.Unstructured)
371+
372+
if diff := cmp.Diff(expObj.Object, gotObj.Object, cmpopts.EquateEmpty()); diff != "" {
373+
t.Errorf("object content mismatch for %s (-want +got):\n%s", key, diff)
374+
}
375+
}
376+
})
377+
}
378+
}
379+
380+
// fakeUnstructured creates a fake unstructured client.Object with the specified kind, namespace, and name.
381+
func fakeUnstructured(kind, namespace, name string) client.Object {
382+
obj := &unstructured.Unstructured{}
383+
obj.Object = make(map[string]interface{})
384+
385+
group := ""
386+
version := "v1"
387+
388+
switch kind {
389+
case "NetworkPolicy":
390+
err := unstructured.SetNestedField(obj.Object, map[string]interface{}{
391+
"podSelector": map[string]interface{}{
392+
"matchLabels": map[string]interface{}{"app": "my-app"},
393+
},
394+
"policyTypes": []interface{}{"Ingress"},
395+
}, "spec")
396+
if err != nil {
397+
panic(fmt.Sprintf("failed to set spec for NetworkPolicy: %v", err))
398+
}
399+
case "Service":
400+
_ = unstructured.SetNestedField(obj.Object, map[string]interface{}{
401+
"ports": []interface{}{
402+
map[string]interface{}{
403+
"port": int64(8080),
404+
"targetPort": "http",
405+
},
406+
},
407+
"selector": map[string]interface{}{
408+
"app": "mock-app",
409+
},
410+
}, "spec")
411+
case "Deployment":
412+
_ = unstructured.SetNestedField(obj.Object, map[string]interface{}{
413+
"replicas": int64(1),
414+
"selector": map[string]interface{}{
415+
"matchLabels": map[string]interface{}{"app": "mock-app"},
416+
},
417+
"template": map[string]interface{}{
418+
"metadata": map[string]interface{}{
419+
"labels": map[string]interface{}{"app": "mock-app"},
420+
},
421+
"spec": map[string]interface{}{
422+
"containers": []interface{}{
423+
map[string]interface{}{
424+
"name": "controller",
425+
"image": "mock-controller:latest",
426+
},
427+
},
428+
},
429+
},
430+
}, "spec")
431+
case "ConfigMap":
432+
_ = unstructured.SetNestedField(obj.Object, map[string]interface{}{
433+
"controller": "enabled",
434+
}, "data")
435+
}
436+
437+
obj.SetGroupVersionKind(schema.GroupVersionKind{
438+
Group: group,
439+
Version: version,
440+
Kind: kind,
441+
})
442+
obj.SetNamespace(namespace)
443+
obj.SetName(name)
444+
445+
return obj
446+
}
447+
448+
// objectKey returns a unique key for a Kubernetes object based on Kind/Namespace/Name.
449+
func objectKey(obj client.Object) string {
450+
gvk := obj.GetObjectKind().GroupVersionKind()
451+
return fmt.Sprintf("%s/%s/%s", gvk.Kind, obj.GetNamespace(), obj.GetName())
452+
}

test/convert/README.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

test/convert/expected-manifests/argocd-operator.v0.6.0/all-namespaces/00_clusterrole_argocd-operator-metrics-reader.yaml

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)