Skip to content

Commit e775e9f

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

12 files changed

+132
-23124
lines changed

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: 131 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
11
package render_test
22

33
import (
4-
"cmp"
54
"errors"
65
"fmt"
7-
"os"
8-
"path/filepath"
96
"reflect"
10-
"slices"
11-
"strings"
127
"testing"
138

149
"github.com/stretchr/testify/require"
10+
"gotest.tools/v3/assert"
1511
appsv1 "k8s.io/api/apps/v1"
1612
corev1 "k8s.io/api/core/v1"
13+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
"k8s.io/apimachinery/pkg/runtime/schema"
1715
"sigs.k8s.io/controller-runtime/pkg/client"
18-
"sigs.k8s.io/yaml"
1916

2017
"github.com/operator-framework/api/pkg/operators/v1alpha1"
2118

2219
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle"
23-
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
2420
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
25-
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render/registryv1"
2621
. "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing"
2722
)
2823

@@ -275,84 +270,162 @@ func Test_BundleValidatorCallsAllValidationFnsInOrder(t *testing.T) {
275270
require.NoError(t, val.Validate(nil))
276271
require.Equal(t, "hi", actual)
277272
}
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+
}()
278279

279-
// This test ensures consistent rendering behavior for registry+v1 bundles:
280-
// - Uses the bundles defined in the testdata directory
281-
// - Renders manifests using the registryv1 renderer
282-
// - Sorts rendered objects by Kind, Namespace, and Name to ensure deterministic output
283-
// - Writes the results to a temporary directory for inspection
284-
// - Ensures the rendering completes without error
285-
// - Automatically cleans up the generated files after the test completes
286-
//
287-
// This was introduced to prevent flaky diffs caused by non-deterministic manifest ordering.
288-
// Related issue: https://github.com/operator-framework/operator-controller/pull/1895
289-
func Test_RenderRegistryV1Bundle_GenerateAndAutoCleanup(t *testing.T) {
290-
bundleRoot := "testdata/bundles"
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))
291314

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) {
292324
testCases := []struct {
293325
name string
294326
installNamespace string
295327
watchNamespace string
296-
bundle string
297-
testCaseName string
328+
installModes []v1alpha1.InstallMode
329+
expectedNS string
298330
}{
299331
{
300332
name: "AllNamespaces",
301-
installNamespace: "argocd-system",
333+
installNamespace: "mock-system",
302334
watchNamespace: "",
303-
bundle: "argocd-operator.v0.6.0",
304-
testCaseName: "all-namespaces",
335+
installModes: []v1alpha1.InstallMode{
336+
{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true},
337+
},
338+
expectedNS: "mock-system",
305339
},
306340
{
307-
name: "SingleNamespaces",
308-
installNamespace: "argocd-system",
309-
watchNamespace: "argocd-watch",
310-
bundle: "argocd-operator.v0.6.0",
311-
testCaseName: "single-namespace",
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",
312348
},
313349
{
314-
name: "OwnNamespaces",
315-
installNamespace: "argocd-system",
316-
watchNamespace: "argocd-system",
317-
bundle: "argocd-operator.v0.6.0",
318-
testCaseName: "own-namespace",
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",
319357
},
320358
}
321359

322360
for _, tc := range testCases {
323361
t.Run(tc.name, func(t *testing.T) {
324-
bundlePath := filepath.Join(bundleRoot, tc.bundle)
325-
outputDir := filepath.Join(t.TempDir(), tc.bundle, tc.testCaseName)
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+
}
326372

327-
fs := os.DirFS(bundlePath)
328-
bundle, err := source.FromFS(fs).GetBundle()
329-
require.NoError(t, err)
373+
mockGen := render.ResourceGenerator(func(_ *bundle.RegistryV1, _ render.Options) ([]client.Object, error) {
374+
return expectedObjects, nil
375+
})
330376

331-
rendered, err := registryv1.Renderer.Render(bundle, tc.installNamespace,
332-
render.WithTargetNamespaces(tc.watchNamespace))
333-
require.NoError(t, err)
377+
mockBundle := bundle.RegistryV1{
378+
CSV: v1alpha1.ClusterServiceVersion{
379+
Spec: v1alpha1.ClusterServiceVersionSpec{
380+
InstallModes: tc.installModes,
381+
},
382+
},
383+
}
334384

335-
sorted := slices.SortedFunc(slices.Values(rendered), orderByKindNamespaceName)
385+
renderer := render.BundleRenderer{
386+
BundleValidator: render.BundleValidator{
387+
func(_ *bundle.RegistryV1) []error { return nil },
388+
},
389+
ResourceGenerators: []render.ResourceGenerator{mockGen},
390+
}
336391

337-
require.NoError(t, os.MkdirAll(outputDir, os.ModePerm))
392+
opts := []render.Option{
393+
render.WithTargetNamespaces(tc.watchNamespace),
394+
render.WithUniqueNameGenerator(render.DefaultUniqueNameGenerator),
395+
}
338396

339-
for idx, obj := range sorted {
340-
kind := obj.GetObjectKind().GroupVersionKind().Kind
341-
filename := fmt.Sprintf("%02d_%s_%s.yaml", idx, strings.ToLower(kind), obj.GetName())
342-
filePath := filepath.Join(outputDir, filename)
397+
// When we call:
398+
objs, err := renderer.Render(mockBundle, tc.installNamespace, opts...)
343399

344-
data, err := yaml.Marshal(obj)
345-
require.NoError(t, err)
346-
require.NoError(t, os.WriteFile(filePath, data, 0600))
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)
347410
}
348411
})
349412
}
350413
}
351414

352-
func orderByKindNamespaceName(a client.Object, b client.Object) int {
353-
return cmp.Or(
354-
cmp.Compare(a.GetObjectKind().GroupVersionKind().Kind, b.GetObjectKind().GroupVersionKind().Kind),
355-
cmp.Compare(a.GetNamespace(), b.GetNamespace()),
356-
cmp.Compare(a.GetName(), b.GetName()),
357-
)
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
358431
}

internal/operator-controller/rukpak/render/testdata/bundles/argocd-operator.v0.6.0/manifests/argocd-operator-controller-manager-metrics-service_v1_service.yaml

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

internal/operator-controller/rukpak/render/testdata/bundles/argocd-operator.v0.6.0/manifests/argocd-operator-manager-config_v1_configmap.yaml

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

internal/operator-controller/rukpak/render/testdata/bundles/argocd-operator.v0.6.0/manifests/argocd-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml

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

0 commit comments

Comments
 (0)