diff --git a/go.mod b/go.mod index 634744d46..01642f2b8 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/operator-framework/deppy v0.0.0-20230602120738-cbf2c66b141b github.com/operator-framework/operator-registry v1.26.3 github.com/operator-framework/rukpak v0.12.0 + github.com/stretchr/testify v1.8.1 go.uber.org/zap v1.24.0 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 @@ -49,6 +50,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect diff --git a/go.sum b/go.sum index 6aaaa7e1d..feb7bd776 100644 --- a/go.sum +++ b/go.sum @@ -273,13 +273,18 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/internal/resolution/resolver_test.go b/internal/resolution/resolver_test.go index 8b8d59105..7f0b7445b 100644 --- a/internal/resolution/resolver_test.go +++ b/internal/resolution/resolver_test.go @@ -5,11 +5,10 @@ import ( "fmt" "testing" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" "github.com/operator-framework/deppy/pkg/deppy" "github.com/operator-framework/deppy/pkg/deppy/input" "github.com/operator-framework/deppy/pkg/deppy/solver" + "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -20,116 +19,111 @@ import ( ) func TestOperatorResolver(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Operator Resolver Suite") -} -func FakeClient(objects ...client.Object) client.Client { - scheme := runtime.NewScheme() - if err := v1alpha1.AddToScheme(scheme); err != nil { - panic(fmt.Sprintf("error creating fake client: %s", err)) + testEntityCache := map[deppy.Identifier]input.Entity{"operatorhub/prometheus/0.37.0": *input.NewEntity( + "operatorhub/prometheus/0.37.0", map[string]string{ + "olm.bundle.path": `"foo.io/bar/baz"`, + "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.32.0\"}", + "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.37.0\"}", + }), + "operatorhub/prometheus/0.47.0": *input.NewEntity("operatorhub/prometheus/0.47.0", map[string]string{ + "olm.bundle.path": `"foo.io/bar/baz"`, + "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.37.0\"}", + "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}", + }), + "operatorhub/packageA/2.0.0": *input.NewEntity("operatorhub/packageA/2.0.0", map[string]string{ + "olm.bundle.path": `"foo.io/packageA/packageA:v2.0.0"`, + "olm.channel": "{\"channelName\":\"stable\",\"priority\":0}", + "olm.package": "{\"packageName\":\"packageA\",\"version\":\"2.0.0\"}", + }), } - return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build() -} -var testEntityCache = map[deppy.Identifier]input.Entity{ - "operatorhub/prometheus/0.37.0": *input.NewEntity("operatorhub/prometheus/0.37.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.32.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.37.0\"}", - }), - "operatorhub/prometheus/0.47.0": *input.NewEntity("operatorhub/prometheus/0.47.0", map[string]string{ - "olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed"`, - "olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.37.0\"}", - "olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1alpha1\"}]", - "olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}", - }), - "operatorhub/packageA/2.0.0": *input.NewEntity("operatorhub/packageA/2.0.0", map[string]string{ - "olm.bundle.path": `"foo.io/packageA/packageA:v2.0.0"`, - "olm.channel": "{\"channelName\":\"stable\",\"priority\":0}", - "olm.gvk": "[{\"group\":\"foo.io\",\"kind\":\"Foo\",\"version\":\"v1\"}]", - "olm.package": "{\"packageName\":\"packageA\",\"version\":\"2.0.0\"}", - }), -} + testEntitySource := input.NewCacheQuerier(testEntityCache) -var _ = Describe("OperatorResolver", func() { - It("should resolve the packages described by the available Operator resources", func() { - resources := []client.Object{ - &v1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{ - Name: "prometheus", - }, - Spec: v1alpha1.OperatorSpec{ - PackageName: "prometheus", - }, - }, - &v1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{ - Name: "packageA", - }, - Spec: v1alpha1.OperatorSpec{ - PackageName: "packageA", - }, - }, - } - client := FakeClient(resources...) - entitySource := input.NewCacheQuerier(testEntityCache) - variableSource := olm.NewOLMVariableSource(client) - resolver := solver.NewDeppySolver(entitySource, variableSource) - solution, err := resolver.Solve(context.Background()) - Expect(err).ToNot(HaveOccurred()) - // 2 * required package variables + 2 * bundle variables - Expect(solution.SelectedVariables()).To(HaveLen(4)) - - Expect(solution.IsSelected("operatorhub/packageA/2.0.0")).To(BeTrue()) - Expect(solution.IsSelected("operatorhub/prometheus/0.47.0")).To(BeTrue()) - Expect(solution.IsSelected("required package packageA")).To(BeTrue()) - Expect(solution.IsSelected("required package prometheus")).To(BeTrue()) - - Expect(solution.IsSelected("operatorhub/prometheus/0.37.0")).To(BeFalse()) - - }) - - It("should not return an error if there are no Operator resources", func() { - var resources []client.Object - client := FakeClient(resources...) - entitySource := input.NewCacheQuerier(testEntityCache) - variableSource := olm.NewOLMVariableSource(client) - resolver := solver.NewDeppySolver(entitySource, variableSource) - solution, err := resolver.Solve(context.Background()) - Expect(err).ToNot(HaveOccurred()) - Expect(solution.SelectedVariables()).To(HaveLen(0)) - }) - - It("should return an error if the entity source throws an error", func() { - resource := &v1alpha1.Operator{ + testResource := []client.Object{ + &v1alpha1.Operator{ ObjectMeta: metav1.ObjectMeta{ Name: "prometheus", }, Spec: v1alpha1.OperatorSpec{ PackageName: "prometheus", }, - } - client := FakeClient(resource) - entitySource := FailEntitySource{} - variableSource := olm.NewOLMVariableSource(client) - resolver := solver.NewDeppySolver(entitySource, variableSource) - solution, err := resolver.Solve(context.Background()) - Expect(solution).To(BeNil()) - Expect(err).To(HaveOccurred()) - }) - - It("should return an error if the client throws an error", func() { - client := NewFailClientWithError(fmt.Errorf("something bad happened")) - entitySource := input.NewCacheQuerier(testEntityCache) - variableSource := olm.NewOLMVariableSource(client) - resolver := solver.NewDeppySolver(entitySource, variableSource) - solution, err := resolver.Solve(context.Background()) - Expect(solution).To(BeNil()) - Expect(err).To(HaveOccurred()) - }) -}) + }, + &v1alpha1.Operator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "packageA", + }, + Spec: v1alpha1.OperatorSpec{ + PackageName: "packageA", + }, + }, + } + + for _, tt := range []struct { + name string + client client.Client + entitySource input.EntitySource + expectedSelectedVariables []deppy.Identifier + expectedError error + }{ + { + name: "should resolve the packages described by the available Operator resources", + client: FakeClient(testResource...), + entitySource: testEntitySource, + expectedSelectedVariables: []deppy.Identifier{ + "operatorhub/packageA/2.0.0", + "operatorhub/prometheus/0.47.0", + "required package packageA", + "required package prometheus"}, + expectedError: nil, + }, + { + name: "should not return an error if there are no Operator resources", + client: FakeClient(), + entitySource: testEntitySource, + expectedSelectedVariables: []deppy.Identifier{}, + expectedError: nil, + }, + { + name: "should return an error if the entity source throws an error", + client: FakeClient(testResource...), + entitySource: FailEntitySource{}, + expectedError: fmt.Errorf("error calling filter in entity source"), + }, + { + name: "should return an error if the client throws an error", + client: NewFailClientWithError(fmt.Errorf("something bad happened")), + entitySource: testEntitySource, + expectedError: fmt.Errorf("something bad happened"), + }, + } { + t.Run(tt.name, func(t *testing.T) { + variableSource := olm.NewOLMVariableSource(tt.client) + resolver := solver.NewDeppySolver(tt.entitySource, variableSource) + solution, err := resolver.Solve(context.Background()) + + if tt.expectedError != nil { + assert.Equal(t, tt.expectedError, err) + assert.Nil(t, solution) + } else { + assert.Len(t, solution.SelectedVariables(), len(tt.expectedSelectedVariables)) + for _, identifier := range tt.expectedSelectedVariables { + assert.True(t, solution.IsSelected(identifier)) + } + assert.NoError(t, err) + } + + }) + } +} + +func FakeClient(objects ...client.Object) client.Client { + scheme := runtime.NewScheme() + if err := v1alpha1.AddToScheme(scheme); err != nil { + panic(fmt.Sprintf("error creating fake client: %s", err)) + } + return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build() +} var _ input.EntitySource = &FailEntitySource{}