diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index cd213c74f2..603e06b6be 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -77,6 +77,12 @@ $(KUBE_SCORE): $(BINGO_DIR)/kube-score.mod @echo "(re)installing $(GOBIN)/kube-score-v1.20.0" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=kube-score.mod -o=$(GOBIN)/kube-score-v1.20.0 "github.com/zegl/kube-score/cmd/kube-score" +MOCKGEN := $(GOBIN)/mockgen-v0.6.0 +$(MOCKGEN): $(BINGO_DIR)/mockgen.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/mockgen-v0.6.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=mockgen.mod -o=$(GOBIN)/mockgen-v0.6.0 "go.uber.org/mock/mockgen" + OPERATOR_SDK := $(GOBIN)/operator-sdk-v1.41.1 $(OPERATOR_SDK): $(BINGO_DIR)/operator-sdk.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. diff --git a/.bingo/mockgen.mod b/.bingo/mockgen.mod new file mode 100644 index 0000000000..43b9663307 --- /dev/null +++ b/.bingo/mockgen.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.26.3 + +require go.uber.org/mock v0.6.0 // mockgen diff --git a/.bingo/mockgen.sum b/.bingo/mockgen.sum new file mode 100644 index 0000000000..8936eb9e33 --- /dev/null +++ b/.bingo/mockgen.sum @@ -0,0 +1,8 @@ +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= diff --git a/.bingo/variables.env b/.bingo/variables.env index 05cc4cfb52..f14fdf14bc 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -28,6 +28,8 @@ KIND="${GOBIN}/kind-v0.32.0" KUBE_SCORE="${GOBIN}/kube-score-v1.20.0" +MOCKGEN="${GOBIN}/mockgen-v0.6.0" + OPERATOR_SDK="${GOBIN}/operator-sdk-v1.41.1" OPM="${GOBIN}/opm-v1.60.0" diff --git a/AGENTS.md b/AGENTS.md index e2481bc2cf..1a62ddcb5c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,7 +54,15 @@ install and manage cluster extensions. The project follows a microservices archi - `containers_image_openpgp` - required for image handling **Tools (managed via .bingo/):** -- controller-gen, golangci-lint, goreleaser, helm, kind, kustomize, setup-envtest, operator-sdk +- controller-gen, golangci-lint, goreleaser, helm, kind, kustomize, mockgen, setup-envtest, operator-sdk + +**Test Mocking:** +- **gomock** (`go.uber.org/mock`): Used for generated mock implementations. `//go:generate mockgen` directives + live in `internal/testutil/mock/generate.go` (external and internal exported interfaces) and at interface + definitions for internal unexported interfaces (source mode). Run `make generate-mocks` to regenerate. + All interface mocks should be gomock-generated. Test fakes that return preconfigured values without + interaction verification (e.g., `FakePuller`, `FakeCache` in `image/fakes.go`) are not mocks and stay + hand-written. --- @@ -140,8 +148,11 @@ make manifests # Update CRDs and reference docs (when Go-based API definitions change) make update-crds crd-ref-docs -# Generate code (DeepCopy methods) +# Generate code (DeepCopy methods and mock implementations) make generate + +# Regenerate mock implementations only +make generate-mocks ``` --- diff --git a/Makefile b/Makefile index 0b342e13f2..c5f504f9c5 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,14 @@ endif # bingo manages consistent tooling versions for things like kind, etc. include .bingo/Variables.mk +# mockgen is installed by bingo with a versioned name (mockgen-v0.6.0). +# Redefine MOCKGEN to an unversioned symlink so that go generate can find it on PATH. +_MOCKGEN_BINGO := $(MOCKGEN) +MOCKGEN := $(ROOT_DIR)/bin/mockgen +$(MOCKGEN): $(_MOCKGEN_BINGO) + @mkdir -p $(dir $@) + @ln -sf $< $@ + ifeq ($(origin KIND_CLUSTER_NAME), undefined) KIND_CLUSTER_NAME := operator-controller endif @@ -201,8 +209,12 @@ manifests: update-crds $(MANIFESTS) $(HELM) #EXHELP Generate OLMv1 manifests $(HELM) template olmv1 helm/olmv1 --values helm/tilt.yaml $(addprefix --set ,$(HELM_SETTINGS)) > /dev/null $(HELM) template olmv1 helm/olmv1 --set "options.openshift.enabled=true" > /dev/null +.PHONY: generate-mocks +generate-mocks: $(MOCKGEN) #EXHELP Generate mock implementations for testing. + PATH="$(ROOT_DIR)/bin:$$PATH" go generate ./... + .PHONY: generate -generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyInto, DeepCopyObject, and ApplyConfiguration type implementations. +generate: $(CONTROLLER_GEN) generate-mocks #EXHELP Generate code containing DeepCopy, DeepCopyInto, DeepCopyObject, and ApplyConfiguration type implementations. # Need to delete the files for them to be generated properly @find api cmd hack internal -name "zz_generated.deepcopy.go" -not -path "*/vendor/*" -delete && rm -rf applyconfigurations $(CONTROLLER_GEN) --load-build-tags=$(GO_BUILD_TAGS) applyconfiguration:headerFile="hack/boilerplate.go.txt" paths="./api/..." @@ -289,7 +301,8 @@ extension-developer-e2e: export CONTAINER_RUNTIME := $(CONTAINER_RUNTIME) extension-developer-e2e: $(OPERATOR_SDK) #EXHELP Run extension create, upgrade and delete tests. go test -count=1 -v ./test/extension-developer-e2e/... -UNIT_TEST_DIRS := $(shell go list ./... | grep -vE "/test/|/testutils") $(shell go list ./test/internal/...) +UNIT_TEST_DIRS := $(shell go list ./... | grep -vE "/test/|/testutils|/testutil/mock") $(shell go list ./test/internal/...) +COVERAGE_PKGS := $(shell go list ./... | grep -vE "/test/|/testutils|/testutil/mock" | paste -sd,) COVERAGE_UNIT_DIR := $(ROOT_DIR)/coverage/unit .PHONY: envtest-k8s-bins #HELP Uses setup-envtest to download and install the binaries required to run ENVTEST-test based locally at the project/bin directory. @@ -303,7 +316,7 @@ test-unit: $(SETUP_ENVTEST) envtest-k8s-bins #HELP Run the unit tests KUBEBUILDER_ASSETS="$(shell $(SETUP_ENVTEST) use -p path $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE))" \ CGO_ENABLED=1 go test \ -tags '$(GO_BUILD_TAGS)' \ - -cover -coverprofile ${ROOT_DIR}/coverage/unit.out \ + -cover -coverpkg=$(COVERAGE_PKGS) -coverprofile ${ROOT_DIR}/coverage/unit.out \ -count=1 -race -short \ $(UNIT_TEST_DIRS) \ -test.gocoverdir=$(COVERAGE_UNIT_DIR) diff --git a/go.mod b/go.mod index 6c4cb77c64..3835c22540 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 go.podman.io/image/v5 v5.40.0 + go.uber.org/mock v0.6.0 golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f golang.org/x/mod v0.37.0 golang.org/x/sync v0.21.0 @@ -205,7 +206,6 @@ require ( github.com/smallstep/pkcs7 v0.2.1 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect - github.com/stretchr/objx v0.5.3 // indirect github.com/ulikunitz/xz v0.5.15 // indirect github.com/vbatts/tar-split v0.12.3 // indirect github.com/vbauerster/mpb/v8 v8.12.0 // indirect diff --git a/go.sum b/go.sum index e294ca90d3..93e8a86768 100644 --- a/go.sum +++ b/go.sum @@ -584,6 +584,8 @@ go.podman.io/storage v1.63.0 h1:bj/pAWFhChbuBmejzno0iQLhU7FevGVXepRXm5pFGeA= go.podman.io/storage v1.63.0/go.mod h1:z4Z9K+7GhKjWL/Y1O17+4f8a1KGijVeC9hr3tymhSOs= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= diff --git a/internal/catalogd/controllers/core/clustercatalog_controller_test.go b/internal/catalogd/controllers/core/clustercatalog_controller_test.go index 76efafa228..f6cbe46dfb 100644 --- a/internal/catalogd/controllers/core/clustercatalog_controller_test.go +++ b/internal/catalogd/controllers/core/clustercatalog_controller_test.go @@ -4,8 +4,6 @@ import ( "context" "errors" "fmt" - "io/fs" - "net/http" "testing" "testing/fstest" "time" @@ -15,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.podman.io/image/v5/docker/reference" + "go.uber.org/mock/gomock" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" @@ -24,41 +23,25 @@ import ( ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/catalogd/storage" imageutil "github.com/operator-framework/operator-controller/internal/shared/util/image" + mockstorage "github.com/operator-framework/operator-controller/internal/testutil/mock/storage" ) -var _ storage.Instance = &MockStore{} - -type MockStore struct { - shouldError bool -} - -func (m MockStore) Store(_ context.Context, _ string, _ fs.FS) error { - if m.shouldError { - return errors.New("mockstore store error") - } - return nil -} - -func (m MockStore) Delete(_ string) error { - if m.shouldError { - return errors.New("mockstore delete error") +func newMockStore(ctrl *gomock.Controller, shouldError bool) *mockstorage.MockInstance { + m := mockstorage.NewMockInstance(ctrl) + if shouldError { + m.EXPECT().Store(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("mockstore store error")).AnyTimes() + m.EXPECT().Delete(gomock.Any()).Return(errors.New("mockstore delete error")).AnyTimes() + } else { + m.EXPECT().Store(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + m.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes() } - return nil -} - -func (m MockStore) BaseURL(_ string) string { - return "URL" -} - -func (m MockStore) StorageServerHandler() http.Handler { - panic("not needed") -} - -func (m MockStore) ContentExists(_ string) bool { - return true + m.EXPECT().BaseURL(gomock.Any()).Return("URL").AnyTimes() + m.EXPECT().ContentExists(gomock.Any()).Return(true).AnyTimes() + return m } func TestCatalogdControllerReconcile(t *testing.T) { + mockCtrl := gomock.NewController(t) for _, tt := range []struct { name string catalog *ocv1.ClusterCatalog @@ -70,8 +53,8 @@ func TestCatalogdControllerReconcile(t *testing.T) { }{ { name: "invalid source type, returns error", - puller: &imageutil.MockPuller{}, - store: &MockStore{}, + puller: &imageutil.FakePuller{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -108,10 +91,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { { name: "valid source type, unpack returns error, status updated to reflect error state and error is returned", expectedError: fmt.Errorf("source catalog content: %w", fmt.Errorf("mockpuller error")), - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ Error: errors.New("mockpuller error"), }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -153,10 +136,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { { name: "valid source type, unpack returns terminal error, status updated to reflect terminal error state(Blocked) and error is returned", expectedError: fmt.Errorf("source catalog content: %w", reconcile.TerminalError(fmt.Errorf("mockpuller terminal error"))), - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ Error: reconcile.TerminalError(errors.New("mockpuller terminal error")), }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -197,11 +180,11 @@ func TestCatalogdControllerReconcile(t *testing.T) { }, { name: "valid source type, unpack state == Unpacked, should reflect in status that it's progressing, and is serving", - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, Ref: mustRef(t, "my.org/someimage@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -256,12 +239,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { { name: "valid source type, unpack state == Unpacked, storage fails, failure reflected in status and error returned", expectedError: fmt.Errorf("error storing fbc: mockstore store error"), - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, }, - store: &MockStore{ - shouldError: true, - }, + store: newMockStore(mockCtrl, true), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -302,10 +283,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { }, { name: "storage finalizer not set, storage finalizer gets set", - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -336,10 +317,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { }, { name: "storage finalizer set, catalog deletion timestamp is not zero (or nil), finalizer removed", - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -409,12 +390,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { { name: "storage finalizer set, catalog deletion timestamp is not zero (or nil), storage delete failed, error returned, finalizer not removed and catalog continues serving", expectedError: fmt.Errorf("finalizer %q failed: %w", fbcDeletionFinalizer, fmt.Errorf("mockstore delete error")), - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, }, - store: &MockStore{ - shouldError: true, - }, + store: newMockStore(mockCtrl, true), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -479,11 +458,9 @@ func TestCatalogdControllerReconcile(t *testing.T) { { name: "storage finalizer set, catalog deletion timestamp is not zero (or nil), unpack cleanup failed, error returned, finalizer not removed but catalog stops serving", expectedError: fmt.Errorf("finalizer %q failed: %w", fbcDeletionFinalizer, fmt.Errorf("mockcache delete error")), - puller: &imageutil.MockPuller{}, - cache: &imageutil.MockCache{DeleteErr: fmt.Errorf("mockcache delete error")}, - store: &MockStore{ - shouldError: false, - }, + puller: &imageutil.FakePuller{}, + cache: &imageutil.FakeCache{DeleteErr: fmt.Errorf("mockcache delete error")}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -545,10 +522,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { }, { name: "catalog availability set to disabled, status.urls should get unset", - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -616,10 +593,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { }, { name: "catalog availability set to disabled, finalizer should get removed", - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -689,10 +666,10 @@ func TestCatalogdControllerReconcile(t *testing.T) { }, { name: "after catalog availability set to enable, finalizer should be added", - puller: &imageutil.MockPuller{ + puller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, }, - store: &MockStore{}, + store: newMockStore(mockCtrl, false), catalog: &ocv1.ClusterCatalog{ ObjectMeta: metav1.ObjectMeta{ Name: "catalog", @@ -810,7 +787,7 @@ func TestCatalogdControllerReconcile(t *testing.T) { storedCatalogs: map[string]storedCatalogData{}, } if reconciler.ImageCache == nil { - reconciler.ImageCache = &imageutil.MockCache{} + reconciler.ImageCache = &imageutil.FakeCache{} } require.NoError(t, reconciler.setupFinalizers()) ctx := context.Background() @@ -896,6 +873,7 @@ func TestPollingRequeue(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) ref := mustRef(t, "my.org/someimage@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") tc.catalog.Status = ocv1.ClusterCatalogStatus{ Conditions: []metav1.Condition{ @@ -911,11 +889,11 @@ func TestPollingRequeue(t *testing.T) { } reconciler := &ClusterCatalogReconciler{ Client: nil, - ImagePuller: &imageutil.MockPuller{ + ImagePuller: &imageutil.FakePuller{ ImageFS: &fstest.MapFS{}, Ref: ref, }, - Storage: &MockStore{}, + Storage: newMockStore(mockCtrl, false), storedCatalogs: map[string]storedCatalogData{ tc.catalog.Name: { ref: ref, @@ -1132,14 +1110,15 @@ func TestPollingReconcilerUnpack(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) scd := tc.storedCatalogData if scd == nil { scd = map[string]storedCatalogData{} } reconciler := &ClusterCatalogReconciler{ Client: nil, - ImagePuller: &imageutil.MockPuller{Error: errors.New("mockpuller error")}, - Storage: &MockStore{}, + ImagePuller: &imageutil.FakePuller{Error: errors.New("mockpuller error")}, + Storage: newMockStore(mockCtrl, false), storedCatalogs: scd, } require.NoError(t, reconciler.setupFinalizers()) diff --git a/internal/catalogd/server/handlers_test.go b/internal/catalogd/server/handlers_test.go index d565de277c..6a8d7acd17 100644 --- a/internal/catalogd/server/handlers_test.go +++ b/internal/catalogd/server/handlers_test.go @@ -1,4 +1,4 @@ -package server +package server_test import ( "bytes" @@ -13,59 +13,26 @@ import ( "testing" "github.com/graphql-go/graphql" + "go.uber.org/mock/gomock" - gql "github.com/operator-framework/operator-controller/internal/catalogd/graphql" + "github.com/operator-framework/operator-controller/internal/catalogd/server" + mockcatalogdserver "github.com/operator-framework/operator-controller/internal/testutil/mock/catalogdserver" + mockcatalogdservice "github.com/operator-framework/operator-controller/internal/testutil/mock/catalogdservice" ) -// mockCatalogStore implements CatalogStore for testing -type mockCatalogStore struct { - catalogFile *os.File - catalogStat os.FileInfo - catalogFS fs.FS - getDataErr error - getFSErr error -} - -func (m *mockCatalogStore) GetCatalogData(catalog string) (*os.File, os.FileInfo, error) { - return m.catalogFile, m.catalogStat, m.getDataErr -} - -func (m *mockCatalogStore) GetCatalogFS(catalog string) (fs.FS, error) { - return m.catalogFS, m.getFSErr -} - -func (m *mockCatalogStore) GetIndex(catalog string) (Index, error) { - return nil, nil -} - -// mockGraphQLService implements service.GraphQLService for testing -type mockGraphQLService struct { - executeResult *graphql.Result - executeErr error -} - -func (m *mockGraphQLService) GetSchema(catalog string, catalogFS fs.FS) (*gql.DynamicSchema, error) { - return nil, nil -} - -func (m *mockGraphQLService) ExecuteQuery(catalog string, catalogFS fs.FS, query string) (*graphql.Result, error) { - return m.executeResult, m.executeErr -} - -func (m *mockGraphQLService) InvalidateCache(catalog string) {} - func TestHandleV1GraphQL_MethodNotAllowed(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{} - graphqlSvc := &mockGraphQLService{} + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() req := httptest.NewRequest(http.MethodGet, "/test-catalog/api/v1/graphql", nil) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusMethodNotAllowed { t.Errorf("Expected status %d, got %d", http.StatusMethodNotAllowed, w.Code) @@ -73,17 +40,18 @@ func TestHandleV1GraphQL_MethodNotAllowed(t *testing.T) { } func TestHandleV1GraphQL_InvalidCatalogName(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{} - graphqlSvc := &mockGraphQLService{} + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() req := httptest.NewRequest(http.MethodPost, "/INVALID-CATALOG-NAME/api/v1/graphql", strings.NewReader(`{"query": "{ summary { totalSchemas } }"}`)) - req.SetPathValue("catalog", "INVALID-CATALOG-NAME") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) @@ -91,17 +59,18 @@ func TestHandleV1GraphQL_InvalidCatalogName(t *testing.T) { } func TestHandleV1GraphQL_InvalidJSON(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{} - graphqlSvc := &mockGraphQLService{} + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() req := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", strings.NewReader(`{invalid json`)) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) @@ -109,17 +78,18 @@ func TestHandleV1GraphQL_InvalidJSON(t *testing.T) { } func TestHandleV1GraphQL_EmptyQuery(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{} - graphqlSvc := &mockGraphQLService{} + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() req := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", strings.NewReader(`{"query": ""}`)) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) @@ -130,19 +100,20 @@ func TestHandleV1GraphQL_EmptyQuery(t *testing.T) { } func TestHandleV1GraphQL_QueryTooLarge(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{} - graphqlSvc := &mockGraphQLService{} + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() // Create a query larger than 100KB largeQuery := strings.Repeat("a", 100001) req := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", strings.NewReader(`{"query": "`+largeQuery+`"}`)) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code) @@ -150,19 +121,20 @@ func TestHandleV1GraphQL_QueryTooLarge(t *testing.T) { } func TestHandleV1GraphQL_BodyTooLarge(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{} - graphqlSvc := &mockGraphQLService{} + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() // Create a body larger than 1MB largeBody := strings.Repeat("a", 1<<20+1) req := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", strings.NewReader(largeBody)) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) // MaxBytesReader should cause this to fail during decode if w.Code != http.StatusBadRequest { @@ -171,15 +143,15 @@ func TestHandleV1GraphQL_BodyTooLarge(t *testing.T) { } func TestHandleV1GraphQL_Success(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") // Create a temporary directory for the mock filesystem tmpDir := t.TempDir() catalogFS := os.DirFS(tmpDir) - store := &mockCatalogStore{ - catalogFS: catalogFS, - } + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + store.EXPECT().GetCatalogFS("test-catalog").Return(catalogFS, nil) expectedResult := &graphql.Result{ Data: map[string]interface{}{ @@ -189,18 +161,17 @@ func TestHandleV1GraphQL_Success(t *testing.T) { }, } - graphqlSvc := &mockGraphQLService{ - executeResult: expectedResult, - } + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) + graphqlSvc.EXPECT().ExecuteQuery("test-catalog", catalogFS, "{ summary { totalSchemas } }").Return(expectedResult, nil) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() query := `{"query": "{ summary { totalSchemas } }"}` req := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", strings.NewReader(query)) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status %d, got %d", http.StatusOK, w.Code) @@ -231,22 +202,22 @@ func TestHandleV1GraphQL_Success(t *testing.T) { } func TestHandleV1GraphQL_GetCatalogFSError(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{ - getFSErr: fs.ErrNotExist, - } + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + store.EXPECT().GetCatalogFS("test-catalog").Return(nil, fs.ErrNotExist) - graphqlSvc := &mockGraphQLService{} + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() query := `{"query": "{ summary { totalSchemas } }"}` req := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", strings.NewReader(query)) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Errorf("Expected status %d, got %d", http.StatusNotFound, w.Code) @@ -254,27 +225,26 @@ func TestHandleV1GraphQL_GetCatalogFSError(t *testing.T) { } func TestHandleV1GraphQL_ExecuteQueryError(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") tmpDir := t.TempDir() catalogFS := os.DirFS(tmpDir) - store := &mockCatalogStore{ - catalogFS: catalogFS, - } + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + store.EXPECT().GetCatalogFS("test-catalog").Return(catalogFS, nil) - graphqlSvc := &mockGraphQLService{ - executeErr: context.DeadlineExceeded, - } + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) + graphqlSvc.EXPECT().ExecuteQuery("test-catalog", catalogFS, "{ summary { totalSchemas } }").Return(nil, context.DeadlineExceeded) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) + handler := handlers.Handler() query := `{"query": "{ summary { totalSchemas } }"}` req := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", strings.NewReader(query)) - req.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() - handlers.handleV1GraphQL(w, req) + handler.ServeHTTP(w, req) if w.Code != http.StatusInternalServerError { t.Errorf("Expected status %d, got %d", http.StatusInternalServerError, w.Code) @@ -282,16 +252,19 @@ func TestHandleV1GraphQL_ExecuteQueryError(t *testing.T) { } func TestAllowedMethodsHandler_POSTOnlyForGraphQL(t *testing.T) { + ctrl := gomock.NewController(t) rootURL, _ := url.Parse("http://localhost/") - store := &mockCatalogStore{} - graphqlSvc := &mockGraphQLService{} + store := mockcatalogdserver.NewMockCatalogStore(ctrl) + store.EXPECT().GetCatalogFS("test-catalog").Return(nil, nil) + + graphqlSvc := mockcatalogdservice.NewMockGraphQLService(ctrl) + graphqlSvc.EXPECT().ExecuteQuery("test-catalog", nil, "{ summary { totalSchemas } }").Return(nil, nil) - handlers := NewCatalogHandlers(store, graphqlSvc, rootURL, MetasHandlerDisabled, GraphQLQueriesEnabled) + handlers := server.NewCatalogHandlers(store, graphqlSvc, rootURL, server.MetasHandlerDisabled, server.GraphQLQueriesEnabled) handler := handlers.Handler() // Test POST to GraphQL endpoint - should be allowed graphqlReq := httptest.NewRequest(http.MethodPost, "/test-catalog/api/v1/graphql", bytes.NewReader([]byte(`{"query": "{ summary { totalSchemas } }"}`))) - graphqlReq.SetPathValue("catalog", "test-catalog") w := httptest.NewRecorder() handler.ServeHTTP(w, graphqlReq) diff --git a/internal/catalogd/serverutil/serverutil_test.go b/internal/catalogd/serverutil/serverutil_test.go index 439b726ed5..57ebf94f82 100644 --- a/internal/catalogd/serverutil/serverutil_test.go +++ b/internal/catalogd/serverutil/serverutil_test.go @@ -11,7 +11,6 @@ import ( "crypto/x509/pkix" "encoding/pem" "io" - "io/fs" "math/big" "net" "net/http" @@ -23,6 +22,9 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + mockstorage "github.com/operator-framework/operator-controller/internal/testutil/mock/storage" ) func TestStorageServerHandlerWrapped_Gzip(t *testing.T) { @@ -61,10 +63,9 @@ func TestStorageServerHandlerWrapped_Gzip(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) // Create a mock storage instance that returns our test content - mockStorage := &mockStorageInstance{ - content: tt.responseContent, - } + mockStorage := newMockStorageInstance(mockCtrl, tt.responseContent) cfg := CatalogServerConfig{ LocalStorage: mockStorage, @@ -109,33 +110,19 @@ func TestStorageServerHandlerWrapped_Gzip(t *testing.T) { } } -// mockStorageInstance implements storage.Instance interface for testing -type mockStorageInstance struct { - content string -} - -func (m *mockStorageInstance) StorageServerHandler() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte(m.content)) +func newMockStorageInstance(ctrl *gomock.Controller, content string) *mockstorage.MockInstance { + m := mockstorage.NewMockInstance(ctrl) + m.EXPECT().StorageServerHandler().Return(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(content)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } - }) -} - -func (m *mockStorageInstance) Store(ctx context.Context, catalogName string, fs fs.FS) error { - return nil -} - -func (m *mockStorageInstance) Delete(catalogName string) error { - return nil -} - -func (m *mockStorageInstance) ContentExists(catalog string) bool { - return true -} -func (m *mockStorageInstance) BaseURL(catalog string) string { - return "" + })).AnyTimes() + m.EXPECT().Store(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + m.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes() + m.EXPECT().ContentExists(gomock.Any()).Return(true).AnyTimes() + m.EXPECT().BaseURL(gomock.Any()).Return("").AnyTimes() + return m } // writeTempCert generates a self-signed TLS certificate and writes the PEM-encoded @@ -196,7 +183,8 @@ func TestCatalogServerTLSOptsApplied(t *testing.T) { observedMinVersion = c.MinVersion } - mockStorage := &mockStorageInstance{} + mockCtrl := gomock.NewController(t) + mockStorage := newMockStorageInstance(mockCtrl, "") cfg := CatalogServerConfig{ CatalogAddr: "127.0.0.1:0", CertFile: certFile, @@ -246,7 +234,8 @@ func TestCatalogServerTLSOptsApplied(t *testing.T) { func TestCatalogServerTLSOptsCertSourceRequired(t *testing.T) { certFile, keyFile := writeTempCert(t) - mockStorage := &mockStorageInstance{} + mockCtrl := gomock.NewController(t) + mockStorage := newMockStorageInstance(mockCtrl, "") cfg := CatalogServerConfig{ CatalogAddr: "127.0.0.1:0", CertFile: certFile, diff --git a/internal/operator-controller/action/helm_test.go b/internal/operator-controller/action/helm_test.go index 25f7f71950..086a8cc92f 100644 --- a/internal/operator-controller/action/helm_test.go +++ b/internal/operator-controller/action/helm_test.go @@ -7,88 +7,13 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/release" + "go.uber.org/mock/gomock" "sigs.k8s.io/controller-runtime/pkg/client" - actionclient "github.com/operator-framework/helm-operator-plugins/pkg/client" + mockhelmclient "github.com/operator-framework/operator-controller/internal/testutil/mock/helmclient" ) -var _ actionclient.ActionInterface = &mockActionClient{} - -type mockActionClient struct { - mock.Mock -} - -func (m *mockActionClient) Get(name string, opts ...actionclient.GetOption) (*release.Release, error) { - args := m.Called(name, opts) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*release.Release), args.Error(1) -} - -func (m *mockActionClient) History(name string, opts ...actionclient.HistoryOption) ([]*release.Release, error) { - args := m.Called(name, opts) - if args.Get(0) == nil { - return nil, args.Error(1) - } - rel := []*release.Release{ - args.Get(0).(*release.Release), - } - return rel, args.Error(1) -} - -func (m *mockActionClient) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...actionclient.InstallOption) (*release.Release, error) { - args := m.Called(name, namespace, chrt, vals, opts) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*release.Release), args.Error(1) -} - -func (m *mockActionClient) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...actionclient.UpgradeOption) (*release.Release, error) { - args := m.Called(name, namespace, chrt, vals, opts) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*release.Release), args.Error(1) -} - -func (m *mockActionClient) Uninstall(name string, opts ...actionclient.UninstallOption) (*release.UninstallReleaseResponse, error) { - args := m.Called(name, opts) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*release.UninstallReleaseResponse), args.Error(1) -} - -func (m *mockActionClient) Reconcile(rel *release.Release) error { - args := m.Called(rel) - return args.Error(0) -} - -func (m *mockActionClient) Config() *action.Configuration { - return nil -} - -var _ actionclient.ActionClientGetter = &mockActionClientGetter{} - -type mockActionClientGetter struct { - mock.Mock -} - -func (m *mockActionClientGetter) ActionClientFor(ctx context.Context, obj client.Object) (actionclient.ActionInterface, error) { - args := m.Called(ctx, obj) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(actionclient.ActionInterface), args.Error(1) -} - func TestActionClientErrorTranslation(t *testing.T) { originalError := fmt.Errorf("some error") expectedErr := fmt.Errorf("something other error") @@ -96,13 +21,15 @@ func TestActionClientErrorTranslation(t *testing.T) { return expectedErr } - ac := new(mockActionClient) - ac.On("Get", mock.Anything, mock.Anything).Return(nil, originalError) - ac.On("History", mock.Anything, mock.Anything).Return(nil, originalError) - ac.On("Install", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, originalError) - ac.On("Uninstall", mock.Anything, mock.Anything).Return(nil, originalError) - ac.On("Upgrade", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, originalError) - ac.On("Reconcile", mock.Anything, mock.Anything).Return(originalError) + ctrl := gomock.NewController(t) + ac := mockhelmclient.NewMockActionInterface(ctrl) + + ac.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, originalError).AnyTimes() + ac.EXPECT().History(gomock.Any(), gomock.Any()).Return(nil, originalError).AnyTimes() + ac.EXPECT().Install(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, originalError).AnyTimes() + ac.EXPECT().Uninstall(gomock.Any(), gomock.Any()).Return(nil, originalError).AnyTimes() + ac.EXPECT().Upgrade(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, originalError).AnyTimes() + ac.EXPECT().Reconcile(gomock.Any()).Return(originalError).AnyTimes() wrappedAc := NewWrappedActionClient(ac, errTranslator) @@ -132,23 +59,20 @@ func TestActionClientErrorTranslation(t *testing.T) { } func TestActionClientFor(t *testing.T) { - // Create a mock for the ActionClientGetter - mockActionClientGetter := new(mockActionClientGetter) - mockActionInterface := new(mockActionClient) + ctrl := gomock.NewController(t) + mockACG := mockhelmclient.NewMockActionClientGetter(ctrl) + mockAI := mockhelmclient.NewMockActionInterface(ctrl) testError := errors.New("test error") - // Set up expectations for the mock - mockActionClientGetter.On("ActionClientFor", mock.Anything, mock.Anything).Return(mockActionInterface, nil).Once() - mockActionClientGetter.On("ActionClientFor", mock.Anything, mock.Anything).Return(nil, testError).Once() + first := mockACG.EXPECT().ActionClientFor(gomock.Any(), gomock.Any()).Return(mockAI, nil) + mockACG.EXPECT().ActionClientFor(gomock.Any(), gomock.Any()).Return(nil, testError).After(first) - // Create an instance of ActionClientGetter with the mock acg := ActionClientGetter{ - ActionClientGetter: mockActionClientGetter, + ActionClientGetter: mockACG, } - // Define a test context and object ctx := context.Background() - var obj client.Object // Replace with an actual client.Object implementation + var obj client.Object // Test the successful case actionClient, err := acg.ActionClientFor(ctx, obj) diff --git a/internal/operator-controller/applier/boxcutter_test.go b/internal/operator-controller/applier/boxcutter_test.go index 31cfc81557..8814a61e53 100644 --- a/internal/operator-controller/applier/boxcutter_test.go +++ b/internal/operator-controller/applier/boxcutter_test.go @@ -11,8 +11,8 @@ import ( "testing/fstest" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/storage/driver" appsv1 "k8s.io/api/apps/v1" @@ -39,6 +39,9 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/labels" bundlecsv "github.com/operator-framework/operator-controller/internal/testing/bundle/csv" bundlefs "github.com/operator-framework/operator-controller/internal/testing/bundle/fs" + mockapplier "github.com/operator-framework/operator-controller/internal/testutil/mock/applier" + mockauthorization "github.com/operator-framework/operator-controller/internal/testutil/mock/authorization" + mockctrlclient "github.com/operator-framework/operator-controller/internal/testutil/mock/ctrlclient" ) var ( @@ -163,37 +166,35 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T) } func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) { - r := &FakeManifestProvider{ - GetFn: func(_ fs.FS, _ *ocv1.ClusterExtension) ([]client.Object, error) { - return []client.Object{ - &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-service", - }, - }, - &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Namespace: "test-ns", - Labels: map[string]string{"my-label": "my-label-value"}, - Annotations: map[string]string{"my-annotation": "my-annotation-value"}, - // Fields to be sanitized - Finalizers: []string{"test"}, - OwnerReferences: []metav1.OwnerReference{{Kind: "TestOwner"}}, - CreationTimestamp: metav1.Time{Time: metav1.Now().Time}, - UID: "1a2b3c4d", - ResourceVersion: "12345", - Generation: 123, - ManagedFields: []metav1.ManagedFieldsEntry{{Manager: "test-manager"}}, - DeletionTimestamp: &metav1.Time{Time: metav1.Now().Time}, - DeletionGracePeriodSeconds: func(i int64) *int64 { return &i }(30), - }, Status: appsv1.DeploymentStatus{ - Replicas: 3, - }, - }, - }, nil + ctrl := gomock.NewController(t) + r := mockapplier.NewMockManifestProvider(ctrl) + r.EXPECT().Get(gomock.Any(), gomock.Any()).Return([]client.Object{ + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-service", + }, }, - } + &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-deployment", + Namespace: "test-ns", + Labels: map[string]string{"my-label": "my-label-value"}, + Annotations: map[string]string{"my-annotation": "my-annotation-value"}, + // Fields to be sanitized + Finalizers: []string{"test"}, + OwnerReferences: []metav1.OwnerReference{{Kind: "TestOwner"}}, + CreationTimestamp: metav1.Time{Time: metav1.Now().Time}, + UID: "1a2b3c4d", + ResourceVersion: "12345", + Generation: 123, + ManagedFields: []metav1.ManagedFieldsEntry{{Manager: "test-manager"}}, + DeletionTimestamp: &metav1.Time{Time: metav1.Now().Time}, + DeletionGracePeriodSeconds: func(i int64) *int64 { return &i }(30), + }, Status: appsv1.DeploymentStatus{ + Replicas: 3, + }, + }, + }, nil).AnyTimes() b := applier.SimpleRevisionGenerator{ Scheme: k8scheme.Scheme, @@ -285,11 +286,9 @@ func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) { } func Test_SimpleRevisionGenerator_GenerateRevision_BundleAnnotations(t *testing.T) { - r := &FakeManifestProvider{ - GetFn: func(_ fs.FS, _ *ocv1.ClusterExtension) ([]client.Object, error) { - return []client.Object{}, nil - }, - } + ctrl := gomock.NewController(t) + r := mockapplier.NewMockManifestProvider(ctrl) + r.EXPECT().Get(gomock.Any(), gomock.Any()).Return([]client.Object{}, nil).AnyTimes() b := applier.SimpleRevisionGenerator{ Scheme: k8scheme.Scheme, @@ -371,14 +370,15 @@ func Test_SimpleRevisionGenerator_Renderer_Integration(t *testing.T) { Name: "test-extension", }, } - r := &FakeManifestProvider{ - GetFn: func(b fs.FS, e *ocv1.ClusterExtension) ([]client.Object, error) { + ctrl := gomock.NewController(t) + r := mockapplier.NewMockManifestProvider(ctrl) + r.EXPECT().Get(dummyBundle, ext).DoAndReturn( + func(b fs.FS, e *ocv1.ClusterExtension) ([]client.Object, error) { t.Log("by checking renderer was called with the correct parameters") require.Equal(t, dummyBundle, b) require.Equal(t, ext, e) return nil, nil - }, - } + }).AnyTimes() b := applier.SimpleRevisionGenerator{ Scheme: k8scheme.Scheme, ManifestProvider: r, @@ -407,11 +407,9 @@ func Test_SimpleRevisionGenerator_AppliesObjectLabelsAndRevisionAnnotations(t *t }, }, } - r := &FakeManifestProvider{ - GetFn: func(b fs.FS, e *ocv1.ClusterExtension) ([]client.Object, error) { - return renderedObjs, nil - }, - } + ctrl := gomock.NewController(t) + r := mockapplier.NewMockManifestProvider(ctrl) + r.EXPECT().Get(gomock.Any(), gomock.Any()).Return(renderedObjs, nil).AnyTimes() b := applier.SimpleRevisionGenerator{ Scheme: k8scheme.Scheme, @@ -445,11 +443,9 @@ func Test_SimpleRevisionGenerator_AppliesObjectLabelsAndRevisionAnnotations(t *t } func Test_SimpleRevisionGenerator_PropagatesProgressDeadlineMinutes(t *testing.T) { - r := &FakeManifestProvider{ - GetFn: func(b fs.FS, e *ocv1.ClusterExtension) ([]client.Object, error) { - return []client.Object{}, nil - }, - } + ctrl := gomock.NewController(t) + r := mockapplier.NewMockManifestProvider(ctrl) + r.EXPECT().Get(gomock.Any(), gomock.Any()).Return([]client.Object{}, nil).AnyTimes() b := applier.SimpleRevisionGenerator{ Scheme: k8scheme.Scheme, @@ -504,11 +500,10 @@ func Test_SimpleRevisionGenerator_PropagatesProgressDeadlineMinutes(t *testing.T } func Test_SimpleRevisionGenerator_Failure(t *testing.T) { - r := &FakeManifestProvider{ - GetFn: func(b fs.FS, e *ocv1.ClusterExtension) ([]client.Object, error) { - return nil, fmt.Errorf("some-error") - }, - } + ctrl := gomock.NewController(t) + r := mockapplier.NewMockManifestProvider(ctrl) + r.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("some-error")).AnyTimes() + b := applier.SimpleRevisionGenerator{ Scheme: k8scheme.Scheme, ManifestProvider: r, @@ -591,7 +586,7 @@ func TestBoxcutter_Apply(t *testing.T) { } testCases := []struct { name string - mockBuilder applier.ClusterObjectSetGenerator + mockBuilder func(t *testing.T) applier.ClusterObjectSetGenerator existingObjs []client.Object expectedErr string validate func(t *testing.T, c client.Client) @@ -599,32 +594,37 @@ func TestBoxcutter_Apply(t *testing.T) { }{ { name: "first revision", - mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return ocv1ac.ClusterObjectSet(""). - WithAnnotations(revisionAnnotations). - WithLabels(map[string]string{ - labels.OwnerNameKey: ext.Name, - }). - WithSpec(ocv1ac.ClusterObjectSetSpec(). - WithPhases( - ocv1ac.ClusterObjectSetPhase(). - WithName(string(applier.PhaseDeploy)). - WithObjects( - ocv1ac.ClusterObjectSetObject(). - WithObject(unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "test-cm", + mockBuilder: func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithAnnotations(revisionAnnotations). + WithLabels(map[string]string{ + labels.OwnerNameKey: ext.Name, + }). + WithSpec(ocv1ac.ClusterObjectSetSpec(). + WithPhases( + ocv1ac.ClusterObjectSetPhase(). + WithName(string(applier.PhaseDeploy)). + WithObjects( + ocv1ac.ClusterObjectSetObject(). + WithObject(unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "test-cm", + }, }, - }, - }), - ), - ), - ), nil - }, + }), + ), + ), + ), nil + }).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m }, validate: func(t *testing.T, c client.Client) { revList := &ocv1.ClusterObjectSetList{} @@ -642,32 +642,37 @@ func TestBoxcutter_Apply(t *testing.T) { }, { name: "no change, revision exists", - mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return ocv1ac.ClusterObjectSet(""). - WithAnnotations(revisionAnnotations). - WithLabels(map[string]string{ - labels.OwnerNameKey: ext.Name, - }). - WithSpec(ocv1ac.ClusterObjectSetSpec(). - WithPhases( - ocv1ac.ClusterObjectSetPhase(). - WithName(string(applier.PhaseDeploy)). - WithObjects( - ocv1ac.ClusterObjectSetObject(). - WithObject(unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "test-cm", + mockBuilder: func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithAnnotations(revisionAnnotations). + WithLabels(map[string]string{ + labels.OwnerNameKey: ext.Name, + }). + WithSpec(ocv1ac.ClusterObjectSetSpec(). + WithPhases( + ocv1ac.ClusterObjectSetPhase(). + WithName(string(applier.PhaseDeploy)). + WithObjects( + ocv1ac.ClusterObjectSetObject(). + WithObject(unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "test-cm", + }, }, - }, - }), - ), - ), - ), nil - }, + }), + ), + ), + ), nil + }).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m }, existingObjs: []client.Object{ defaultDesiredRevision, @@ -683,32 +688,37 @@ func TestBoxcutter_Apply(t *testing.T) { }, { name: "new revision created when objects in new revision are different", - mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return ocv1ac.ClusterObjectSet(""). - WithAnnotations(revisionAnnotations). - WithLabels(map[string]string{ - labels.OwnerNameKey: ext.Name, - }). - WithSpec(ocv1ac.ClusterObjectSetSpec(). - WithPhases( - ocv1ac.ClusterObjectSetPhase(). - WithName(string(applier.PhaseDeploy)). - WithObjects( - ocv1ac.ClusterObjectSetObject(). - WithObject(unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "v1", - "kind": "Secret", - "metadata": map[string]interface{}{ - "name": "new-secret", + mockBuilder: func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithAnnotations(revisionAnnotations). + WithLabels(map[string]string{ + labels.OwnerNameKey: ext.Name, + }). + WithSpec(ocv1ac.ClusterObjectSetSpec(). + WithPhases( + ocv1ac.ClusterObjectSetPhase(). + WithName(string(applier.PhaseDeploy)). + WithObjects( + ocv1ac.ClusterObjectSetObject(). + WithObject(unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": "new-secret", + }, }, - }, - }), - ), - ), - ), nil - }, + }), + ), + ), + ), nil + }).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m }, clientIterceptor: allowedRevisionValue(2), existingObjs: []client.Object{ @@ -736,10 +746,12 @@ func TestBoxcutter_Apply(t *testing.T) { }, { name: "error from GenerateRevision", - mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return nil, errors.New("render boom") - }, + mockBuilder: func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("render boom")).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m }, expectedErr: "render boom", validate: func(t *testing.T, c client.Client) { @@ -752,15 +764,20 @@ func TestBoxcutter_Apply(t *testing.T) { }, { name: "keep at most 5 past revisions", - mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return ocv1ac.ClusterObjectSet(""). - WithAnnotations(revisionAnnotations). - WithLabels(map[string]string{ - labels.OwnerNameKey: ext.Name, - }). - WithSpec(ocv1ac.ClusterObjectSetSpec()), nil - }, + mockBuilder: func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithAnnotations(revisionAnnotations). + WithLabels(map[string]string{ + labels.OwnerNameKey: ext.Name, + }). + WithSpec(ocv1ac.ClusterObjectSetSpec()), nil + }).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m }, existingObjs: []client.Object{ &ocv1.ClusterObjectSet{ @@ -853,15 +870,20 @@ func TestBoxcutter_Apply(t *testing.T) { }, { name: "keep active revisions when they are out of limit", - mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return ocv1ac.ClusterObjectSet(""). - WithAnnotations(revisionAnnotations). - WithLabels(map[string]string{ - labels.OwnerNameKey: ext.Name, - }). - WithSpec(ocv1ac.ClusterObjectSetSpec()), nil - }, + mockBuilder: func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithAnnotations(revisionAnnotations). + WithLabels(map[string]string{ + labels.OwnerNameKey: ext.Name, + }). + WithSpec(ocv1ac.ClusterObjectSetSpec()), nil + }).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m }, existingObjs: []client.Object{ &ocv1.ClusterObjectSet{ @@ -970,32 +992,37 @@ func TestBoxcutter_Apply(t *testing.T) { }, { name: "annotation-only update (same phases, different annotations)", - mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return ocv1ac.ClusterObjectSet(""). - WithAnnotations(revisionAnnotations). - WithLabels(map[string]string{ - labels.OwnerNameKey: ext.Name, - }). - WithSpec(ocv1ac.ClusterObjectSetSpec(). - WithPhases( - ocv1ac.ClusterObjectSetPhase(). - WithName(string(applier.PhaseDeploy)). - WithObjects( - ocv1ac.ClusterObjectSetObject(). - WithObject(unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "test-cm", + mockBuilder: func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithAnnotations(revisionAnnotations). + WithLabels(map[string]string{ + labels.OwnerNameKey: ext.Name, + }). + WithSpec(ocv1ac.ClusterObjectSetSpec(). + WithPhases( + ocv1ac.ClusterObjectSetPhase(). + WithName(string(applier.PhaseDeploy)). + WithObjects( + ocv1ac.ClusterObjectSetObject(). + WithObject(unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "test-cm", + }, }, - }, - }), - ), - ), - ), nil - }, + }), + ), + ), + ), nil + }).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m }, existingObjs: []client.Object{ ext, @@ -1064,7 +1091,7 @@ func TestBoxcutter_Apply(t *testing.T) { boxcutter := &applier.Boxcutter{ Client: fakeClient, Scheme: testScheme, - RevisionGenerator: tc.mockBuilder, + RevisionGenerator: tc.mockBuilder(t), FieldOwner: "test-owner", SystemNamespace: "olmv1-system", } @@ -1127,32 +1154,38 @@ func Test_PreAuthorizer_Integration(t *testing.T) { }, } fakeClient := fake.NewClientBuilder().WithScheme(testScheme).Build() - dummyGenerator := &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotation map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return ocv1ac.ClusterObjectSet(""). - WithSpec(ocv1ac.ClusterObjectSetSpec(). - WithPhases( - ocv1ac.ClusterObjectSetPhase(). - WithName("some-phase"). - WithObjects( - ocv1ac.ClusterObjectSetObject(). - WithObject(unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "data": map[string]string{ - "test-data": "test-data", - }, - }, - }), - ), - ), - ), nil - }, - } dummyBundleFs := fstest.MapFS{} revisionAnnotations := map[string]string{} + newDummyGenerator := func(t *testing.T) applier.ClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevision(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotation map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithSpec(ocv1ac.ClusterObjectSetSpec(). + WithPhases( + ocv1ac.ClusterObjectSetPhase(). + WithName("some-phase"). + WithObjects( + ocv1ac.ClusterObjectSetObject(). + WithObject(unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "data": map[string]string{ + "test-data": "test-data", + }, + }, + }), + ), + ), + ), nil + }).AnyTimes() + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return m + } + for _, tc := range []struct { name string preAuthorizer func(t *testing.T) authorization.PreAuthorizer @@ -1161,20 +1194,22 @@ func Test_PreAuthorizer_Integration(t *testing.T) { { name: "preauthorizer called with correct parameters", preAuthorizer: func(t *testing.T) authorization.PreAuthorizer { - return &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - require.Equal(t, "system:serviceaccount:test-namespace:test-sa", user.GetName()) - require.Empty(t, user.GetUID()) - require.Nil(t, user.GetExtra()) - require.Empty(t, user.GetGroups()) + ctrl := gomock.NewController(t) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, userInfo user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { + require.Equal(t, "system:serviceaccount:test-namespace:test-sa", userInfo.GetName()) + require.Empty(t, userInfo.GetUID()) + require.Nil(t, userInfo.GetExtra()) + require.Empty(t, userInfo.GetGroups()) t.Log("has correct additional permissions") require.Len(t, additionalRequiredPerms, 1) - perms := additionalRequiredPerms[0](user) + perms := additionalRequiredPerms[0](userInfo) require.Len(t, perms, 1) require.Equal(t, authorizer.AttributesRecord{ - User: user, + User: userInfo, Name: "test-ext-1", APIGroup: "olm.operatorframework.io", APIVersion: "v1", @@ -1188,17 +1223,16 @@ func Test_PreAuthorizer_Integration(t *testing.T) { require.NoError(t, err) require.Equal(t, "---\napiVersion: v1\ndata:\n test-data: test-data\nkind: ConfigMap\n", string(manifests)) return nil, nil - }, - } + }).AnyTimes() + return mockPA }, }, { name: "preauthorizer errors are returned", preAuthorizer: func(t *testing.T) authorization.PreAuthorizer { - return &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return nil, errors.New("test error") - }, - } + ctrl := gomock.NewController(t) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("test error")).AnyTimes() + return mockPA }, validate: func(t *testing.T, err error) { require.Error(t, err) @@ -1208,22 +1242,21 @@ func Test_PreAuthorizer_Integration(t *testing.T) { }, { name: "preauthorizer missing permissions are returned as an error", preAuthorizer: func(t *testing.T) authorization.PreAuthorizer { - return &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return []authorization.ScopedPolicyRules{ + ctrl := gomock.NewController(t) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]authorization.ScopedPolicyRules{ + { + Namespace: "", + MissingRules: []rbacv1.PolicyRule{ { - Namespace: "", - MissingRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods"}, - Verbs: []string{"get", "list", "watch"}, - }, - }, + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list", "watch"}, }, - }, nil + }, }, - } + }, nil).AnyTimes() + return mockPA }, validate: func(t *testing.T, err error) { require.Error(t, err) @@ -1235,22 +1268,21 @@ func Test_PreAuthorizer_Integration(t *testing.T) { }, { name: "preauthorizer missing permissions and errors are combined and returned as an error", preAuthorizer: func(t *testing.T) authorization.PreAuthorizer { - return &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return []authorization.ScopedPolicyRules{ + ctrl := gomock.NewController(t) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]authorization.ScopedPolicyRules{ + { + Namespace: "", + MissingRules: []rbacv1.PolicyRule{ { - Namespace: "", - MissingRules: []rbacv1.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"pods"}, - Verbs: []string{"get", "list", "watch"}, - }, - }, + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list", "watch"}, }, - }, errors.New("test error") + }, }, - } + }, errors.New("test error")).AnyTimes() + return mockPA }, validate: func(t *testing.T, err error) { require.Error(t, err) @@ -1263,11 +1295,10 @@ func Test_PreAuthorizer_Integration(t *testing.T) { }, { name: "successful call to preauthorizer does not block applier", preAuthorizer: func(t *testing.T) authorization.PreAuthorizer { - return &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return nil, nil - }, - } + ctrl := gomock.NewController(t) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + return mockPA }, validate: func(t *testing.T, err error) { require.NoError(t, err) @@ -1279,7 +1310,7 @@ func Test_PreAuthorizer_Integration(t *testing.T) { Client: fakeClient, Scheme: testScheme, FieldOwner: "test-owner", - RevisionGenerator: dummyGenerator, + RevisionGenerator: newDummyGenerator(t), PreAuthorizer: tc.preAuthorizer(t), SystemNamespace: "olmv1-system", } @@ -1294,68 +1325,90 @@ func Test_PreAuthorizer_Integration(t *testing.T) { } func TestBoxcutterStorageMigrator(t *testing.T) { + // defaultHelmRevisionResult returns the default result for GenerateRevisionFromHelmRelease in storage migrator tests. + defaultHelmRevisionResult := func(ext *ocv1.ClusterExtension) *ocv1ac.ClusterObjectSetApplyConfiguration { + return ocv1ac.ClusterObjectSet("test-revision"). + WithLabels(map[string]string{ + labels.OwnerNameKey: ext.Name, + }). + WithSpec(ocv1ac.ClusterObjectSetSpec()) + } + + // newStorageMigratorGenerator creates a gomock ClusterObjectSetGenerator for storage migrator tests. + // GenerateRevision is not expected to be called. GenerateRevisionFromHelmRelease returns the default result. + newStorageMigratorGenerator := func(t *testing.T) *mockapplier.MockClusterObjectSetGenerator { + ctrl := gomock.NewController(t) + m := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + m.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, helmRelease *release.Release, e *ocv1.ClusterExtension, objectLabels map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return defaultHelmRevisionResult(e), nil + }).AnyTimes() + return m + } + t.Run("creates revision", func(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{ + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + } + ctrl := gomock.NewController(t) + brb := newStorageMigratorGenerator(t) + mag := newMockActionGetter(ctrl, mockActionGetterConfig{ currentRel: &release.Release{ Name: "test123", Info: &release.Info{Status: release.StatusDeployed}, }, - } - client := &clientMock{} + }) + mockClient := mockctrlclient.NewMockClient(ctrl) + mockStatusWriter := mockctrlclient.NewMockSubResourceWriter(ctrl) + mockClient.EXPECT().Status().Return(mockStatusWriter).AnyTimes() + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) - client. - On("Apply", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetApplyConfiguration"), mock.Anything). - Once(). - Run(func(args mock.Arguments) { + mockClient.EXPECT().Apply(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.ApplyOption) error { // Verify the migration marker label is set before apply - rev := args.Get(1).(*ocv1ac.ClusterObjectSetApplyConfiguration) + rev := obj.(*ocv1ac.ClusterObjectSetApplyConfiguration) require.Equal(t, "true", rev.Labels[labels.MigratedFromHelmKey], "Migration marker label should be set") - }). - Return(nil) - client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). - Once(). - Run(func(args mock.Arguments) { + return nil + }) + mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { // Simulate Get() returning the created revision with server-managed fields - rev := args.Get(2).(*ocv1.ClusterObjectSet) + rev := obj.(*ocv1.ClusterObjectSet) rev.Name = "test-revision" rev.Generation = 1 rev.ResourceVersion = "1" rev.Labels = map[string]string{ labels.MigratedFromHelmKey: "true", } - }). - Return(nil) + return nil + }) + + var updatedObj client.Object + mockStatusWriter.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + updatedObj = obj + return nil + }) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - client.AssertExpectations(t) - // Verify the migrated revision has Succeeded=True status with Succeeded reason and a migration message - statusWriter := client.Status().(*statusWriterMock) - require.True(t, statusWriter.updateCalled, "Status().Update() should be called during migration") - require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") + require.NotNil(t, updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + rev, ok := updatedObj.(*ocv1.ClusterObjectSet) require.True(t, ok, "Updated object should be a ClusterObjectSet") succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) @@ -1370,21 +1423,24 @@ func TestBoxcutterStorageMigrator(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{} - client := &clientMock{} + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + } + // GenerateRevisionFromHelmRelease should not be called when revisions already exist + ctrl := gomock.NewController(t) + brb := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + mag := newMockActionGetter(ctrl, mockActionGetterConfig{}) + + mockClient := mockctrlclient.NewMockClient(ctrl) + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - existingRev := ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-revision", @@ -1407,39 +1463,40 @@ func TestBoxcutterStorageMigrator(t *testing.T) { }, } - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). - Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterObjectSetList) - list.Items = []ocv1.ClusterObjectSet{existingRev} - }). - Return(nil) + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + cosList := list.(*ocv1.ClusterObjectSetList) + cosList.Items = []ocv1.ClusterObjectSet{existingRev} + return nil + }) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - - client.AssertExpectations(t) }) t.Run("sets status when revision exists but status is missing", func(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{} - client := &clientMock{} + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + } + ctrl := gomock.NewController(t) + brb := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + mag := newMockActionGetter(ctrl, mockActionGetterConfig{}) + + mockClient := mockctrlclient.NewMockClient(ctrl) + mockStatusWriter := mockctrlclient.NewMockSubResourceWriter(ctrl) + mockClient.EXPECT().Status().Return(mockStatusWriter).AnyTimes() + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - existingRev := ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-revision", @@ -1454,33 +1511,34 @@ func TestBoxcutterStorageMigrator(t *testing.T) { // Status is empty - simulating the case where creation succeeded but status update failed } - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). - Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterObjectSetList) - list.Items = []ocv1.ClusterObjectSet{existingRev} - }). - Return(nil) + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + cosList := list.(*ocv1.ClusterObjectSetList) + cosList.Items = []ocv1.ClusterObjectSet{existingRev} + return nil + }) - client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). - Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterObjectSet) + mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + rev := obj.(*ocv1.ClusterObjectSet) *rev = existingRev - }). - Return(nil) + return nil + }) + + var updatedObj client.Object + mockStatusWriter.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + updatedObj = obj + return nil + }) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - client.AssertExpectations(t) - // Verify the status was set - statusWriter := client.Status().(*statusWriterMock) - require.True(t, statusWriter.updateCalled, "Status().Update() should be called to set missing status") - require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") + require.NotNil(t, updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + rev, ok := updatedObj.(*ocv1.ClusterObjectSet) require.True(t, ok, "Updated object should be a ClusterObjectSet") succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) @@ -1493,21 +1551,25 @@ func TestBoxcutterStorageMigrator(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{} - client := &clientMock{} + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + } + ctrl := gomock.NewController(t) + brb := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + mag := newMockActionGetter(ctrl, mockActionGetterConfig{}) + + mockClient := mockctrlclient.NewMockClient(ctrl) + mockStatusWriter := mockctrlclient.NewMockSubResourceWriter(ctrl) + mockClient.EXPECT().Status().Return(mockStatusWriter).AnyTimes() + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - // Migrated revision with Succeeded=False (e.g., from a previous failed status update attempt) // This simulates a revision whose Succeeded condition should be corrected from False to True during migration. existingRev := ocv1.ClusterObjectSet{ @@ -1532,33 +1594,34 @@ func TestBoxcutterStorageMigrator(t *testing.T) { }, } - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). - Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterObjectSetList) - list.Items = []ocv1.ClusterObjectSet{existingRev} - }). - Return(nil) + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + cosList := list.(*ocv1.ClusterObjectSetList) + cosList.Items = []ocv1.ClusterObjectSet{existingRev} + return nil + }) - client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). - Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterObjectSet) + mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + rev := obj.(*ocv1.ClusterObjectSet) *rev = existingRev - }). - Return(nil) + return nil + }) + + var updatedObj client.Object + mockStatusWriter.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + updatedObj = obj + return nil + }) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - client.AssertExpectations(t) - // Verify the status was updated from False to True - statusWriter := client.Status().(*statusWriterMock) - require.True(t, statusWriter.updateCalled, "Status().Update() should be called to update False to True") - require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") + require.NotNil(t, updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + rev, ok := updatedObj.(*ocv1.ClusterObjectSet) require.True(t, ok, "Updated object should be a ClusterObjectSet") succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) @@ -1571,21 +1634,23 @@ func TestBoxcutterStorageMigrator(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{} - client := &clientMock{} + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + } + ctrl := gomock.NewController(t) + brb := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + mag := newMockActionGetter(ctrl, mockActionGetterConfig{}) + + mockClient := mockctrlclient.NewMockClient(ctrl) + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - // Revision 1 created by normal Boxcutter operation (no migration label) // This simulates the first rollout - status should NOT be set as it may still be in progress existingRev := ocv1.ClusterObjectSet{ @@ -1599,40 +1664,50 @@ func TestBoxcutterStorageMigrator(t *testing.T) { }, } - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). - Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterObjectSetList) - list.Items = []ocv1.ClusterObjectSet{existingRev} - }). - Return(nil) + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + cosList := list.(*ocv1.ClusterObjectSetList) + cosList.Items = []ocv1.ClusterObjectSet{existingRev} + return nil + }) // The migration flow calls Get() to re-fetch the revision before checking its status. // Even for non-migrated revisions, Get() is called to determine if status needs to be set. - client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). - Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterObjectSet) + mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + rev := obj.(*ocv1.ClusterObjectSet) *rev = existingRev - }). - Return(nil) + return nil + }) + + // Status() and Update() should NOT be called for non-migrated revisions. + // gomock will fail if any unexpected calls are made. err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - - client.AssertExpectations(t) - - // Verify the status was NOT set for non-migrated revision - statusWriter := client.Status().(*statusWriterMock) - require.False(t, statusWriter.updateCalled, "Status().Update() should NOT be called for non-migrated revisions") }) t.Run("migrates from most recent deployed release when latest is failed", func(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{ + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + } + expectedRelease := &release.Release{ + Name: "test123", + Version: 2, + Info: &release.Info{Status: release.StatusDeployed}, + } + + ctrl := gomock.NewController(t) + brb := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + brb.EXPECT().GenerateRevisionFromHelmRelease(gomock.Any(), expectedRelease, gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, helmRelease *release.Release, e *ocv1.ClusterExtension, objectLabels map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return defaultHelmRevisionResult(e), nil + }).Times(1) + + mag := newMockActionGetter(ctrl, mockActionGetterConfig{ currentRel: &release.Release{ Name: "test123", Version: 3, @@ -1644,74 +1719,64 @@ func TestBoxcutterStorageMigrator(t *testing.T) { Version: 3, Info: &release.Info{Status: release.StatusFailed}, }, - { - Name: "test123", - Version: 2, - Info: &release.Info{Status: release.StatusDeployed}, - }, + expectedRelease, { Name: "test123", Version: 1, Info: &release.Info{Status: release.StatusSuperseded}, }, }, - } - client := &clientMock{} + }) + + mockClient := mockctrlclient.NewMockClient(ctrl) + mockStatusWriter := mockctrlclient.NewMockSubResourceWriter(ctrl) + mockClient.EXPECT().Status().Return(mockStatusWriter).AnyTimes() + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) - client. - On("Apply", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetApplyConfiguration"), mock.Anything). - Once(). - Run(func(args mock.Arguments) { + mockClient.EXPECT().Apply(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.ApplyOption) error { // Verify the migration marker label is set before apply - rev := args.Get(1).(*ocv1ac.ClusterObjectSetApplyConfiguration) + rev := obj.(*ocv1ac.ClusterObjectSetApplyConfiguration) require.Equal(t, "true", rev.Labels[labels.MigratedFromHelmKey], "Migration marker label should be set") - }). - Return(nil) + return nil + }) - client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). - Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterObjectSet) - rev.ObjectMeta.Name = "test-revision" - rev.ObjectMeta.Generation = 1 - rev.ObjectMeta.ResourceVersion = "1" + mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + rev := obj.(*ocv1.ClusterObjectSet) + rev.Name = "test-revision" + rev.Generation = 1 + rev.ResourceVersion = "1" rev.Labels = map[string]string{ labels.MigratedFromHelmKey: "true", } - }). - Return(nil) + return nil + }) + + var updatedObj client.Object + mockStatusWriter.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + updatedObj = obj + return nil + }) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - client.AssertExpectations(t) - - // Verify the correct release (version 2, deployed) was used instead of version 3 (failed) - require.NotNil(t, brb.helmReleaseUsed, "GenerateRevisionFromHelmRelease should have been called") - assert.Equal(t, 2, brb.helmReleaseUsed.Version, "Should use version 2 (deployed), not version 3 (failed)") - assert.Equal(t, release.StatusDeployed, brb.helmReleaseUsed.Info.Status, "Should use deployed release") - // Verify the migrated revision has Succeeded=True status - statusWriter := client.Status().(*statusWriterMock) - require.True(t, statusWriter.updateCalled, "Status().Update() should be called during migration") - require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") + require.NotNil(t, updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + rev, ok := updatedObj.(*ocv1.ClusterObjectSet) require.True(t, ok, "Updated object should be a ClusterObjectSet") succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) @@ -1723,8 +1788,13 @@ func TestBoxcutterStorageMigrator(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{ + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, + } + ctrl := gomock.NewController(t) + // GenerateRevisionFromHelmRelease should NOT be called when no deployed release exists + brb := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + mag := newMockActionGetter(ctrl, mockActionGetterConfig{ currentRel: &release.Release{ Name: "test123", Info: &release.Info{Status: release.StatusFailed}, @@ -1741,143 +1811,53 @@ func TestBoxcutterStorageMigrator(t *testing.T) { Info: &release.Info{Status: release.StatusFailed}, }, }, - } - client := &clientMock{} + }) + + mockClient := mockctrlclient.NewMockClient(ctrl) + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - - client.AssertExpectations(t) - // brb.GenerateRevisionFromHelmRelease should NOT have been called - require.False(t, brb.generateRevisionFromHelmReleaseCalled, "GenerateRevisionFromHelmRelease should NOT be called when no deployed release exists") + // gomock will verify GenerateRevisionFromHelmRelease was NOT called (no expectation set) }) t.Run("does not create revision when no helm release", func(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) - brb := &mockBundleRevisionBuilder{} - mag := &mockActionGetter{ - getClientErr: driver.ErrReleaseNotFound, + ext := &ocv1.ClusterExtension{ + ObjectMeta: metav1.ObjectMeta{Name: "test123"}, } - client := &clientMock{} + ctrl := gomock.NewController(t) + brb := mockapplier.NewMockClusterObjectSetGenerator(ctrl) + mag := newMockActionGetter(ctrl, mockActionGetterConfig{ + getClientErr: driver.ErrReleaseNotFound, + }) + + mockClient := mockctrlclient.NewMockClient(ctrl) + sm := &applier.BoxcutterStorageMigrator{ RevisionGenerator: brb, ActionClientGetter: mag, - Client: client, + Client: mockClient, Scheme: testScheme, FieldOwner: "test-owner", } - ext := &ocv1.ClusterExtension{ - ObjectMeta: metav1.ObjectMeta{Name: "test123"}, - } - - client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). + mockClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()). Return(nil) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) require.NoError(t, err) - - client.AssertExpectations(t) }) } - -// mockBundleRevisionBuilder is a mock implementation of the ClusterObjectSetGenerator for testing. -type mockBundleRevisionBuilder struct { - makeRevisionFunc func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotation map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) - generateRevisionFromHelmReleaseCalled bool - helmReleaseUsed *release.Release -} - -func (m *mockBundleRevisionBuilder) GenerateRevision(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - return m.makeRevisionFunc(ctx, bundleFS, ext, objectLabels, revisionAnnotations) -} - -func (m *mockBundleRevisionBuilder) GenerateRevisionFromHelmRelease( - ctx context.Context, - helmRelease *release.Release, ext *ocv1.ClusterExtension, - objectLabels map[string]string, -) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { - m.generateRevisionFromHelmReleaseCalled = true - m.helmReleaseUsed = helmRelease - return ocv1ac.ClusterObjectSet("test-revision"). - WithLabels(map[string]string{ - labels.OwnerNameKey: ext.Name, - }). - WithSpec(ocv1ac.ClusterObjectSetSpec()), nil -} - -type clientMock struct { - mock.Mock - statusWriter *statusWriterMock -} - -func (m *clientMock) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { - args := m.Called(ctx, list, opts) - return args.Error(0) -} - -func (m *clientMock) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { - args := m.Called(ctx, key, obj, opts) - return args.Error(0) -} - -func (m *clientMock) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.ApplyOption) error { - args := m.Called(ctx, obj, opts) - return args.Error(0) -} - -func (m *clientMock) Status() client.StatusWriter { - if m.statusWriter == nil { - m.statusWriter = &statusWriterMock{mock: &m.Mock} - } - return m.statusWriter -} - -type statusWriterMock struct { - mock *mock.Mock - updatedObj client.Object - updateCalled bool - applyCalled bool -} - -func (s *statusWriterMock) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { - // Capture the status update for test verification - s.updatedObj = obj - s.updateCalled = true - return nil -} - -func (s *statusWriterMock) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { - return nil -} - -func (s *statusWriterMock) Create(ctx context.Context, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error { - return nil -} - -// Apply is required by controller-runtime v0.23.0+ StatusWriter interface -func (s *statusWriterMock) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error { - // Track Apply calls to detect unexpected usage in tests - s.applyCalled = true - // Apply is not currently used by the code under test, but tracking the call - // helps ensure tests fail if this assumption changes - return fmt.Errorf("unexpected call to StatusWriter.Apply() - this method is not expected to be used in these tests") -} diff --git a/internal/operator-controller/applier/helm_test.go b/internal/operator-controller/applier/helm_test.go index 3e3ef7479b..3ce6816014 100644 --- a/internal/operator-controller/applier/helm_test.go +++ b/internal/operator-controller/applier/helm_test.go @@ -4,12 +4,12 @@ import ( "context" "errors" "io" - "io/fs" "os" "testing" "testing/fstest" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" @@ -18,84 +18,20 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authorization/authorizer" - "sigs.k8s.io/controller-runtime/pkg/client" helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/operator-controller/applier" "github.com/operator-framework/operator-controller/internal/operator-controller/authorization" - "github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager" - cmcache "github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache" + mockapplier "github.com/operator-framework/operator-controller/internal/testutil/mock/applier" + mockauthorization "github.com/operator-framework/operator-controller/internal/testutil/mock/authorization" + mockcmcache "github.com/operator-framework/operator-controller/internal/testutil/mock/cmcache" + mockcontentmanager "github.com/operator-framework/operator-controller/internal/testutil/mock/contentmanager" + mockhelmclient "github.com/operator-framework/operator-controller/internal/testutil/mock/helmclient" ) -var _ contentmanager.Manager = (*mockManagedContentCacheManager)(nil) - -type mockManagedContentCacheManager struct { - err error - cache cmcache.Cache -} - -func (m *mockManagedContentCacheManager) Get(_ context.Context, _ *ocv1.ClusterExtension) (cmcache.Cache, error) { - if m.err != nil { - return nil, m.err - } - return m.cache, nil -} - -func (m *mockManagedContentCacheManager) Delete(_ *ocv1.ClusterExtension) error { - return m.err -} - -type mockManagedContentCache struct { - err error -} - -var _ cmcache.Cache = (*mockManagedContentCache)(nil) - -func (m *mockManagedContentCache) Close() error { - if m.err != nil { - return m.err - } - return nil -} - -func (m *mockManagedContentCache) Watch(_ context.Context, _ cmcache.Watcher, _ ...client.Object) error { - if m.err != nil { - return m.err - } - return nil -} - -type mockPreflight struct { - installErr error - upgradeErr error -} - -type mockPreAuthorizer struct { - fn func(context.Context, user.Info, io.Reader, ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) -} - -func (p *mockPreAuthorizer) PreAuthorize(ctx context.Context, manifestManager user.Info, manifestReader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return p.fn(ctx, manifestManager, manifestReader, additionalRequiredPerms...) -} - -func (mp *mockPreflight) Install(context.Context, []client.Object) error { - return mp.installErr -} - -func (mp *mockPreflight) Upgrade(context.Context, []client.Object) error { - return mp.upgradeErr -} - -type mockHelmReleaseToObjectsConverter struct { -} - -func (mockHelmReleaseToObjectsConverter) GetObjectsFromRelease(*release.Release) ([]client.Object, error) { - return nil, nil -} - -type mockActionGetter struct { +type mockActionGetterConfig struct { actionClientForErr error getClientErr error historyErr error @@ -109,54 +45,52 @@ type mockActionGetter struct { history []*release.Release } -func (mag *mockActionGetter) ActionClientFor(ctx context.Context, obj client.Object) (helmclient.ActionInterface, error) { - return mag, mag.actionClientForErr -} - -func (mag *mockActionGetter) Get(name string, opts ...helmclient.GetOption) (*release.Release, error) { - return mag.currentRel, mag.getClientErr -} - -func (mag *mockActionGetter) History(name string, opts ...helmclient.HistoryOption) ([]*release.Release, error) { - return mag.history, mag.historyErr -} +func newMockActionGetter(ctrl *gomock.Controller, cfg mockActionGetterConfig) *mockhelmclient.MockActionClientGetterAndInterface { + m := mockhelmclient.NewMockActionClientGetterAndInterface(ctrl) -func (mag *mockActionGetter) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.InstallOption) (*release.Release, error) { - i := action.Install{} - for _, opt := range opts { - if err := opt(&i); err != nil { - return nil, err - } - } - if i.DryRun { - return mag.desiredRel, mag.dryRunInstallErr + if cfg.actionClientForErr != nil { + m.EXPECT().ActionClientFor(gomock.Any(), gomock.Any()).Return(nil, cfg.actionClientForErr).AnyTimes() + } else { + m.EXPECT().ActionClientFor(gomock.Any(), gomock.Any()).Return(m, nil).AnyTimes() } - return mag.desiredRel, mag.installErr -} -func (mag *mockActionGetter) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.UpgradeOption) (*release.Release, error) { - i := action.Upgrade{} - for _, opt := range opts { - if err := opt(&i); err != nil { - return nil, err - } - } - if i.DryRun { - return mag.desiredRel, mag.dryRunUpgradeErr - } - return mag.desiredRel, mag.upgradeErr -} - -func (mag *mockActionGetter) Uninstall(name string, opts ...helmclient.UninstallOption) (*release.UninstallReleaseResponse, error) { - return nil, nil -} - -func (mag *mockActionGetter) Reconcile(rel *release.Release) error { - return mag.reconcileErr -} - -func (mag *mockActionGetter) Config() *action.Configuration { - return nil + m.EXPECT().Get(gomock.Any(), gomock.Any()).Return(cfg.currentRel, cfg.getClientErr).AnyTimes() + m.EXPECT().History(gomock.Any(), gomock.Any()).Return(cfg.history, cfg.historyErr).AnyTimes() + m.EXPECT().Config().Return(nil).AnyTimes() + m.EXPECT().Reconcile(gomock.Any()).Return(cfg.reconcileErr).AnyTimes() + m.EXPECT().Uninstall(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + + // Install with dry-run support + m.EXPECT().Install(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(name, ns string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.InstallOption) (*release.Release, error) { + i := action.Install{} + for _, opt := range opts { + if err := opt(&i); err != nil { + return nil, err + } + } + if i.DryRun { + return cfg.desiredRel, cfg.dryRunInstallErr + } + return cfg.desiredRel, cfg.installErr + }).AnyTimes() + + // Upgrade with dry-run support + m.EXPECT().Upgrade(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(name, ns string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.UpgradeOption) (*release.Release, error) { + u := action.Upgrade{} + for _, opt := range opts { + if err := opt(&u); err != nil { + return nil, err + } + } + if u.DryRun { + return cfg.desiredRel, cfg.dryRunUpgradeErr + } + return cfg.desiredRel, cfg.upgradeErr + }).AnyTimes() + + return m } var ( @@ -250,10 +184,11 @@ func TestApply_Base(t *testing.T) { }) t.Run("fails trying to obtain an action client", func(t *testing.T) { - mockAcg := &mockActionGetter{actionClientForErr: errors.New("failed getting action client")} + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{actionClientForErr: errors.New("failed getting action client")}) helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, + HelmChartProvider: newDummyHelmChartProvider(ctrl), } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -264,10 +199,11 @@ func TestApply_Base(t *testing.T) { }) t.Run("fails getting current release and !driver.ErrReleaseNotFound", func(t *testing.T) { - mockAcg := &mockActionGetter{getClientErr: errors.New("failed getting current release")} + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{getClientErr: errors.New("failed getting current release")}) helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, + HelmChartProvider: newDummyHelmChartProvider(ctrl), } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -280,13 +216,14 @@ func TestApply_Base(t *testing.T) { func TestApply_Installation(t *testing.T) { t.Run("fails during dry-run installation", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, dryRunInstallErr: errors.New("failed attempting to dry-run install chart"), - } + }) helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, + HelmChartProvider: newDummyHelmChartProvider(ctrl), } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -297,16 +234,23 @@ func TestApply_Installation(t *testing.T) { }) t.Run("fails during pre-flight installation", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, installErr: errors.New("failed installing chart"), - } - mockPf := &mockPreflight{installErr: errors.New("failed during install pre-flight check")} + }) + mockPf := mockapplier.NewMockPreflight(ctrl) + mockPf.EXPECT().Install(gomock.Any(), gomock.Any()).Return(errors.New("failed during install pre-flight check")).AnyTimes() + mockPf.EXPECT().Upgrade(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, Preflights: []applier.Preflight{mockPf}, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -317,14 +261,18 @@ func TestApply_Installation(t *testing.T) { }) t.Run("fails during installation", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, installErr: errors.New("failed installing chart"), - } + }) + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -335,20 +283,30 @@ func TestApply_Installation(t *testing.T) { }) t.Run("successful installation", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - } + }) + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + + mockCache := mockcmcache.NewMockCache(ctrl) + mockCache.EXPECT().Close().Return(nil).AnyTimes() + mockCache.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockMgr := mockcontentmanager.NewMockManager(ctrl) + mockMgr.EXPECT().Get(gomock.Any(), gomock.Any()).Return(mockCache, nil).AnyTimes() + mockMgr.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, - Manager: &mockManagedContentCacheManager{ - cache: &mockManagedContentCache{}, - }, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, + Manager: mockMgr, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -360,45 +318,54 @@ func TestApply_Installation(t *testing.T) { func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) { t.Run("preauthorizer called with correct parameters", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, installErr: errors.New("failed installing chart"), desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - } - mockPf := &mockPreflight{installErr: errors.New("failed during install pre-flight check")} + }) + mockPf := mockapplier.NewMockPreflight(ctrl) + mockPf.EXPECT().Install(gomock.Any(), gomock.Any()).Return(errors.New("failed during install pre-flight check")).AnyTimes() + mockPf.EXPECT().Upgrade(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, userInfo user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { + t.Log("has correct user") + require.Equal(t, "system:serviceaccount:test-namespace:test-sa", userInfo.GetName()) + require.Empty(t, userInfo.GetUID()) + require.Nil(t, userInfo.GetExtra()) + require.Empty(t, userInfo.GetGroups()) + + t.Log("has correct additional permissions") + require.Len(t, additionalRequiredPerms, 1) + perms := additionalRequiredPerms[0](userInfo) + + require.Len(t, perms, 1) + require.Equal(t, authorizer.AttributesRecord{ + User: userInfo, + Name: "test-ext", + APIGroup: "olm.operatorframework.io", + APIVersion: "v1", + Resource: "clusterextensions/finalizers", + ResourceRequest: true, + Verb: "update", + }, perms[0]) + return nil, nil + }).AnyTimes() + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + helmApplier := applier.Helm{ - ActionClientGetter: mockAcg, - Preflights: []applier.Preflight{mockPf}, - PreAuthorizer: &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - t.Log("has correct user") - require.Equal(t, "system:serviceaccount:test-namespace:test-sa", user.GetName()) - require.Empty(t, user.GetUID()) - require.Nil(t, user.GetExtra()) - require.Empty(t, user.GetGroups()) - - t.Log("has correct additional permissions") - require.Len(t, additionalRequiredPerms, 1) - perms := additionalRequiredPerms[0](user) - - require.Len(t, perms, 1) - require.Equal(t, authorizer.AttributesRecord{ - User: user, - Name: "test-ext", - APIGroup: "olm.operatorframework.io", - APIVersion: "v1", - Resource: "clusterextensions/finalizers", - ResourceRequest: true, - Verb: "update", - }, perms[0]) - return nil, nil - }, - }, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, + ActionClientGetter: mockAcg, + Preflights: []applier.Preflight{mockPf}, + PreAuthorizer: mockPA, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, } _, _, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -406,13 +373,14 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) { }) t.Run("fails during dry-run installation", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, dryRunInstallErr: errors.New("failed attempting to dry-run install chart"), - } + }) helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, + HelmChartProvider: newDummyHelmChartProvider(ctrl), } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -423,25 +391,31 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) { }) t.Run("fails during pre-flight installation", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, installErr: errors.New("failed installing chart"), desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - } - mockPf := &mockPreflight{installErr: errors.New("failed during install pre-flight check")} + }) + mockPf := mockapplier.NewMockPreflight(ctrl) + mockPf.EXPECT().Install(gomock.Any(), gomock.Any()).Return(errors.New("failed during install pre-flight check")).AnyTimes() + mockPf.EXPECT().Upgrade(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + helmApplier := applier.Helm{ - ActionClientGetter: mockAcg, - Preflights: []applier.Preflight{mockPf}, - PreAuthorizer: &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return nil, nil - }, - }, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, + ActionClientGetter: mockAcg, + Preflights: []applier.Preflight{mockPf}, + PreAuthorizer: mockPA, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -452,21 +426,21 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) { }) t.Run("fails during installation because of pre-authorization failure", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - } + }) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errPreAuth).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - PreAuthorizer: &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return nil, errPreAuth - }, - }, - HelmChartProvider: DummyHelmChartProvider, + PreAuthorizer: mockPA, + HelmChartProvider: newDummyHelmChartProvider(ctrl), } // Use a ClusterExtension with valid Spec fields. validCE := &ocv1.ClusterExtension{ @@ -485,21 +459,21 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) { }) t.Run("fails during installation due to missing RBAC rules", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - } + }) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(missingRBAC, nil).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - PreAuthorizer: &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return missingRBAC, nil - }, - }, - HelmChartProvider: DummyHelmChartProvider, + PreAuthorizer: mockPA, + HelmChartProvider: newDummyHelmChartProvider(ctrl), } // Use a ClusterExtension with valid Spec fields. validCE := &ocv1.ClusterExtension{ @@ -518,25 +492,34 @@ func TestApply_InstallationWithPreflightPermissionsEnabled(t *testing.T) { }) t.Run("successful installation", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - } + }) + mockPA := mockauthorization.NewMockPreAuthorizer(ctrl) + mockPA.EXPECT().PreAuthorize(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + + mockCache := mockcmcache.NewMockCache(ctrl) + mockCache.EXPECT().Close().Return(nil).AnyTimes() + mockCache.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockMgr := mockcontentmanager.NewMockManager(ctrl) + mockMgr.EXPECT().Get(gomock.Any(), gomock.Any()).Return(mockCache, nil).AnyTimes() + mockMgr.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes() + helmApplier := applier.Helm{ - ActionClientGetter: mockAcg, - PreAuthorizer: &mockPreAuthorizer{ - fn: func(ctx context.Context, user user.Info, reader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { - return nil, nil - }, - }, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, - Manager: &mockManagedContentCacheManager{ - cache: &mockManagedContentCache{}, - }, + ActionClientGetter: mockAcg, + PreAuthorizer: mockPA, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, + Manager: mockMgr, } // Use a ClusterExtension with valid Spec fields. @@ -562,12 +545,13 @@ func TestApply_Upgrade(t *testing.T) { } t.Run("fails during dry-run upgrade", func(t *testing.T) { - mockAcg := &mockActionGetter{ + ctrl := gomock.NewController(t) + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ dryRunUpgradeErr: errors.New("failed attempting to dry-run upgrade chart"), - } + }) helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, + HelmChartProvider: newDummyHelmChartProvider(ctrl), } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -578,20 +562,27 @@ func TestApply_Upgrade(t *testing.T) { }) t.Run("fails during pre-flight upgrade", func(t *testing.T) { + ctrl := gomock.NewController(t) testDesiredRelease := *testCurrentRelease testDesiredRelease.Manifest = "do-not-match-current" - mockAcg := &mockActionGetter{ + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ upgradeErr: errors.New("failed upgrading chart"), currentRel: testCurrentRelease, desiredRel: &testDesiredRelease, - } - mockPf := &mockPreflight{upgradeErr: errors.New("failed during upgrade pre-flight check")} + }) + mockPf := mockapplier.NewMockPreflight(ctrl) + mockPf.EXPECT().Install(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockPf.EXPECT().Upgrade(gomock.Any(), gomock.Any()).Return(errors.New("failed during upgrade pre-flight check")).AnyTimes() + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, Preflights: []applier.Preflight{mockPf}, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -602,19 +593,27 @@ func TestApply_Upgrade(t *testing.T) { }) t.Run("fails during upgrade", func(t *testing.T) { + ctrl := gomock.NewController(t) testDesiredRelease := *testCurrentRelease testDesiredRelease.Manifest = "do-not-match-current" - mockAcg := &mockActionGetter{ + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ upgradeErr: errors.New("failed upgrading chart"), currentRel: testCurrentRelease, desiredRel: &testDesiredRelease, - } - mockPf := &mockPreflight{} + }) + mockPf := mockapplier.NewMockPreflight(ctrl) + mockPf.EXPECT().Install(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockPf.EXPECT().Upgrade(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + helmApplier := applier.Helm{ - ActionClientGetter: mockAcg, Preflights: []applier.Preflight{mockPf}, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, + ActionClientGetter: mockAcg, + Preflights: []applier.Preflight{mockPf}, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -625,20 +624,27 @@ func TestApply_Upgrade(t *testing.T) { }) t.Run("fails during upgrade reconcile (StateUnchanged)", func(t *testing.T) { + ctrl := gomock.NewController(t) // make sure desired and current are the same this time testDesiredRelease := *testCurrentRelease - mockAcg := &mockActionGetter{ + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ reconcileErr: errors.New("failed reconciling charts"), currentRel: testCurrentRelease, desiredRel: &testDesiredRelease, - } - mockPf := &mockPreflight{} + }) + mockPf := mockapplier.NewMockPreflight(ctrl) + mockPf.EXPECT().Install(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockPf.EXPECT().Upgrade(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, Preflights: []applier.Preflight{mockPf}, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -649,20 +655,30 @@ func TestApply_Upgrade(t *testing.T) { }) t.Run("successful upgrade", func(t *testing.T) { + ctrl := gomock.NewController(t) testDesiredRelease := *testCurrentRelease testDesiredRelease.Manifest = validManifest - mockAcg := &mockActionGetter{ + mockAcg := newMockActionGetter(ctrl, mockActionGetterConfig{ currentRel: testCurrentRelease, desiredRel: &testDesiredRelease, - } + }) + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + + mockCache := mockcmcache.NewMockCache(ctrl) + mockCache.EXPECT().Close().Return(nil).AnyTimes() + mockCache.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockMgr := mockcontentmanager.NewMockManager(ctrl) + mockMgr.EXPECT().Get(gomock.Any(), gomock.Any()).Return(mockCache, nil).AnyTimes() + mockMgr.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes() + helmApplier := applier.Helm{ ActionClientGetter: mockAcg, - HelmChartProvider: DummyHelmChartProvider, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, - Manager: &mockManagedContentCacheManager{ - cache: &mockManagedContentCache{}, - }, + HelmChartProvider: newDummyHelmChartProvider(ctrl), + HelmReleaseToObjectsConverter: mockConverter, + Manager: mockMgr, } installSucceeded, installStatus, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -674,46 +690,62 @@ func TestApply_Upgrade(t *testing.T) { func TestApply_RegistryV1ToChartConverterIntegration(t *testing.T) { t.Run("generates bundle resources in AllNamespaces install mode", func(t *testing.T) { + ctrl := gomock.NewController(t) + + mockChartProvider := mockapplier.NewMockHelmChartProvider(ctrl) + mockChartProvider.EXPECT().Get(gomock.Any(), testCE).Return(nil, nil).Times(1) + + mockConverter := mockapplier.NewMockHelmReleaseToObjectsConverterInterface(ctrl) + mockConverter.EXPECT().GetObjectsFromRelease(gomock.Any()).Return(nil, nil).AnyTimes() + + mockCache := mockcmcache.NewMockCache(ctrl) + mockCache.EXPECT().Close().Return(nil).AnyTimes() + mockCache.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockMgr := mockcontentmanager.NewMockManager(ctrl) + mockMgr.EXPECT().Get(gomock.Any(), gomock.Any()).Return(mockCache, nil).AnyTimes() + mockMgr.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes() + helmApplier := applier.Helm{ - ActionClientGetter: &mockActionGetter{ + ActionClientGetter: newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - }, - HelmChartProvider: &FakeHelmChartProvider{ - fn: func(bundleFS fs.FS, ext *ocv1.ClusterExtension) (*chart.Chart, error) { - require.Equal(t, testCE, ext) - return nil, nil - }, - }, - HelmReleaseToObjectsConverter: mockHelmReleaseToObjectsConverter{}, - Manager: &mockManagedContentCacheManager{ - cache: &mockManagedContentCache{}, - }, + }), + HelmChartProvider: mockChartProvider, + HelmReleaseToObjectsConverter: mockConverter, + Manager: mockMgr, } _, _, _ = helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) }) t.Run("surfaces chart generation errors", func(t *testing.T) { + ctrl := gomock.NewController(t) + + mockChartProvider := mockapplier.NewMockHelmChartProvider(ctrl) + mockChartProvider.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, errors.New("some error")).AnyTimes() + + mockCache := mockcmcache.NewMockCache(ctrl) + mockCache.EXPECT().Close().Return(nil).AnyTimes() + mockCache.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + + mockMgr := mockcontentmanager.NewMockManager(ctrl) + mockMgr.EXPECT().Get(gomock.Any(), gomock.Any()).Return(mockCache, nil).AnyTimes() + mockMgr.EXPECT().Delete(gomock.Any()).Return(nil).AnyTimes() + helmApplier := applier.Helm{ - ActionClientGetter: &mockActionGetter{ + ActionClientGetter: newMockActionGetter(ctrl, mockActionGetterConfig{ getClientErr: driver.ErrReleaseNotFound, desiredRel: &release.Release{ Info: &release.Info{Status: release.StatusDeployed}, Manifest: validManifest, }, - }, - HelmChartProvider: &FakeHelmChartProvider{ - fn: func(bundleFs fs.FS, ext *ocv1.ClusterExtension) (*chart.Chart, error) { - return nil, errors.New("some error") - }, - }, - Manager: &mockManagedContentCacheManager{ - cache: &mockManagedContentCache{}, - }, + }), + HelmChartProvider: mockChartProvider, + Manager: mockMgr, } _, _, err := helmApplier.Apply(context.TODO(), validFS, testCE, testObjectLabels, testStorageLabels) @@ -721,16 +753,8 @@ func TestApply_RegistryV1ToChartConverterIntegration(t *testing.T) { }) } -type FakeHelmChartProvider struct { - fn func(fs.FS, *ocv1.ClusterExtension) (*chart.Chart, error) -} - -func (f FakeHelmChartProvider) Get(bundle fs.FS, ext *ocv1.ClusterExtension) (*chart.Chart, error) { - return f.fn(bundle, ext) -} - -var DummyHelmChartProvider = &FakeHelmChartProvider{ - fn: func(fs fs.FS, ext *ocv1.ClusterExtension) (*chart.Chart, error) { - return &chart.Chart{}, nil - }, +func newDummyHelmChartProvider(ctrl *gomock.Controller) *mockapplier.MockHelmChartProvider { + m := mockapplier.NewMockHelmChartProvider(ctrl) + m.EXPECT().Get(gomock.Any(), gomock.Any()).Return(&chart.Chart{}, nil).AnyTimes() + return m } diff --git a/internal/operator-controller/applier/provider_test.go b/internal/operator-controller/applier/provider_test.go index 4369fccd26..6fb9760417 100644 --- a/internal/operator-controller/applier/provider_test.go +++ b/internal/operator-controller/applier/provider_test.go @@ -2,11 +2,11 @@ package applier_test import ( "errors" - "io/fs" "testing" "testing/fstest" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -24,6 +24,8 @@ import ( . "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing" bundlecsv "github.com/operator-framework/operator-controller/internal/testing/bundle/csv" bundlefs "github.com/operator-framework/operator-controller/internal/testing/bundle/fs" + mockapplier "github.com/operator-framework/operator-controller/internal/testutil/mock/applier" + mockrender "github.com/operator-framework/operator-controller/internal/testutil/mock/render" ) func Test_RegistryV1ManifestProvider_Integration(t *testing.T) { @@ -234,8 +236,9 @@ func Test_RegistryV1ManifestProvider_WebhookSupport(t *testing.T) { }) t.Run("accepts bundles with webhook definitions if support is enabled and a certificate provider is defined", func(t *testing.T) { + ctrl := gomock.NewController(t) provider := applier.RegistryV1ManifestProvider{ - CertificateProvider: FakeCertProvider{}, + CertificateProvider: mockrender.NewMockCertificateProvider(ctrl), IsWebhookSupportEnabled: true, } @@ -729,8 +732,9 @@ func Test_RegistryV1ManifestProvider_DeploymentConfig(t *testing.T) { func Test_RegistryV1HelmChartProvider_Integration(t *testing.T) { t.Run("surfaces bundle source errors", func(t *testing.T) { + ctrl := gomock.NewController(t) provider := applier.RegistryV1HelmChartProvider{ - ManifestProvider: DummyManifestProvider, + ManifestProvider: newDummyManifestProvider(ctrl), } ext := &ocv1.ClusterExtension{ Spec: ocv1.ClusterExtensionSpec{ @@ -743,12 +747,12 @@ func Test_RegistryV1HelmChartProvider_Integration(t *testing.T) { }) t.Run("surfaces manifest provider failures", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockMP := mockapplier.NewMockManifestProvider(ctrl) + mockMP.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, errors.New("some error")).AnyTimes() + provider := applier.RegistryV1HelmChartProvider{ - ManifestProvider: &FakeManifestProvider{ - GetFn: func(bundle fs.FS, ext *ocv1.ClusterExtension) ([]client.Object, error) { - return nil, errors.New("some error") - }, - }, + ManifestProvider: mockMP, } ext := &ocv1.ClusterExtension{ @@ -803,16 +807,8 @@ func Test_RegistryV1HelmChartProvider_Chart(t *testing.T) { require.Len(t, chart.Templates, 1) } -var DummyManifestProvider = &FakeManifestProvider{ - GetFn: func(bundle fs.FS, ext *ocv1.ClusterExtension) ([]client.Object, error) { - return []client.Object{}, nil - }, -} - -type FakeManifestProvider struct { - GetFn func(bundleFS fs.FS, ext *ocv1.ClusterExtension) ([]client.Object, error) -} - -func (f *FakeManifestProvider) Get(bundleFS fs.FS, ext *ocv1.ClusterExtension) ([]client.Object, error) { - return f.GetFn(bundleFS, ext) +func newDummyManifestProvider(ctrl *gomock.Controller) *mockapplier.MockManifestProvider { + m := mockapplier.NewMockManifestProvider(ctrl) + m.EXPECT().Get(gomock.Any(), gomock.Any()).Return([]client.Object{}, nil).AnyTimes() + return m } diff --git a/internal/operator-controller/authorization/rbac_test.go b/internal/operator-controller/authorization/rbac_test.go index fffcba64a0..e13db42bf6 100644 --- a/internal/operator-controller/authorization/rbac_test.go +++ b/internal/operator-controller/authorization/rbac_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/meta/testrestmapper" @@ -19,6 +20,8 @@ import ( "k8s.io/kubernetes/pkg/registry/rbac/validation" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + mockrbac "github.com/operator-framework/operator-controller/internal/testutil/mock/rbac" ) var ( @@ -767,17 +770,16 @@ func TestParseEscalationErrorForMissingRules_ParsingLogic(t *testing.T) { func TestParseEscalationErrorForMissingRules_KubernetesCompatibility(t *testing.T) { testCases := []struct { name string - ruleResolver validation.AuthorizationRuleResolver + rules []rbacv1.PolicyRule + rulesErr error wantRules []rbacv1.PolicyRule expectedErrorString string expectedResult *parseResult }{ { - name: "missing rules", - ruleResolver: mockRulesResolver{ - rules: []rbacv1.PolicyRule{}, - err: nil, - }, + name: "missing rules", + rules: []rbacv1.PolicyRule{}, + rulesErr: nil, wantRules: []rbacv1.PolicyRule{ {APIGroups: []string{""}, Resources: []string{"secrets"}, Verbs: []string{"get"}, ResourceNames: []string{"test-secret"}}, {APIGroups: []string{""}, Resources: []string{"configmaps"}, Verbs: []string{"get", "list", "watch"}}, @@ -807,11 +809,9 @@ func TestParseEscalationErrorForMissingRules_KubernetesCompatibility(t *testing. }, }, { - name: "resolution failure", - ruleResolver: mockRulesResolver{ - rules: []rbacv1.PolicyRule{}, - err: errors.New("resolution error"), - }, + name: "resolution failure", + rules: []rbacv1.PolicyRule{}, + rulesErr: errors.New("resolution error"), wantRules: []rbacv1.PolicyRule{ {APIGroups: []string{""}, Resources: []string{"secrets"}, Verbs: []string{"get"}, ResourceNames: []string{"test-secret"}}, {APIGroups: []string{""}, Resources: []string{"configmaps"}, Verbs: []string{"get", "list", "watch"}}, @@ -844,6 +844,10 @@ func TestParseEscalationErrorForMissingRules_KubernetesCompatibility(t *testing. } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + ruleResolver := mockrbac.NewMockAuthorizationRuleResolver(ctrl) + ruleResolver.EXPECT().RulesFor(gomock.Any(), gomock.Any(), gomock.Any()).Return(tc.rules, tc.rulesErr) + ctx := request.WithUser(request.WithNamespace(context.Background(), "namespace"), &user.DefaultInfo{ Name: "user", Groups: []string{"a", "b"}, @@ -853,7 +857,7 @@ func TestParseEscalationErrorForMissingRules_KubernetesCompatibility(t *testing. // error message that we are attempting to parse correctly. The hope is that // these tests will start failing if we bump to a new version of kubernetes // that causes our parsing logic to be incorrect. - err := validation.ConfirmNoEscalation(ctx, tc.ruleResolver, tc.wantRules) + err := validation.ConfirmNoEscalation(ctx, ruleResolver, tc.wantRules) require.Error(t, err) require.Equal(t, tc.expectedErrorString, err.Error()) @@ -863,20 +867,3 @@ func TestParseEscalationErrorForMissingRules_KubernetesCompatibility(t *testing. }) } } - -type mockRulesResolver struct { - rules []rbacv1.PolicyRule - err error -} - -func (m mockRulesResolver) GetRoleReferenceRules(ctx context.Context, roleRef rbacv1.RoleRef, namespace string) ([]rbacv1.PolicyRule, error) { - panic("unimplemented") -} - -func (m mockRulesResolver) RulesFor(ctx context.Context, user user.Info, namespace string) ([]rbacv1.PolicyRule, error) { - return m.rules, m.err -} - -func (m mockRulesResolver) VisitRulesFor(ctx context.Context, user user.Info, namespace string, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) { - panic("unimplemented") -} diff --git a/internal/operator-controller/catalogmetadata/client/client_test.go b/internal/operator-controller/catalogmetadata/client/client_test.go index 66817c504b..7ddb7e386b 100644 --- a/internal/operator-controller/catalogmetadata/client/client_test.go +++ b/internal/operator-controller/catalogmetadata/client/client_test.go @@ -12,12 +12,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/operator-framework/operator-registry/alpha/declcfg" ocv1 "github.com/operator-framework/operator-controller/api/v1" catalogClient "github.com/operator-framework/operator-controller/internal/operator-controller/catalogmetadata/client" + mockcatalogclient "github.com/operator-framework/operator-controller/internal/testutil/mock/catalogclient" + mockhttputil "github.com/operator-framework/operator-controller/internal/testutil/mock/httputil" ) func defaultCatalog() *ocv1.ClusterCatalog { @@ -41,11 +44,11 @@ func TestClientGetPackage(t *testing.T) { } type testCase struct { - name string - catalog func() *ocv1.ClusterCatalog - pkgName string - cache catalogClient.Cache - assert func(*testing.T, *declcfg.DeclarativeConfig, error) + name string + catalog func() *ocv1.ClusterCatalog + pkgName string + setupCache func(ctrl *gomock.Controller) catalogClient.Cache + assert func(*testing.T, *declcfg.DeclarativeConfig, error) } for _, tc := range []testCase{ { @@ -60,7 +63,11 @@ func TestClientGetPackage(t *testing.T) { { name: "served, cache returns error", catalog: defaultCatalog, - cache: &fakeCache{getErr: errors.New("fetch error")}, + setupCache: func(ctrl *gomock.Controller) catalogClient.Cache { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, errors.New("fetch error")) + return cache + }, assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { assert.ErrorContains(t, err, `error retrieving cache for catalog "catalog-1"`) }, @@ -68,7 +75,11 @@ func TestClientGetPackage(t *testing.T) { { name: "served, invalid package path", catalog: defaultCatalog, - cache: &fakeCache{getFS: testFS}, + setupCache: func(ctrl *gomock.Controller) catalogClient.Cache { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(testFS, nil) + return cache + }, pkgName: "/", assert: func(t *testing.T, dc *declcfg.DeclarativeConfig, err error) { assert.ErrorContains(t, err, `error getting package "/"`) @@ -78,7 +89,11 @@ func TestClientGetPackage(t *testing.T) { name: "served, package missing", catalog: defaultCatalog, pkgName: "pkg-missing", - cache: &fakeCache{getFS: testFS}, + setupCache: func(ctrl *gomock.Controller) catalogClient.Cache { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(testFS, nil) + return cache + }, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { require.NoError(t, err) assert.Equal(t, &declcfg.DeclarativeConfig{}, fbc) @@ -88,9 +103,13 @@ func TestClientGetPackage(t *testing.T) { name: "served, invalid package present", catalog: defaultCatalog, pkgName: "invalid-pkg-present", - cache: &fakeCache{getFS: fstest.MapFS{ - "invalid-pkg-present/olm.package/invalid-pkg-present.json": &fstest.MapFile{Data: []byte(`{"schema": "olm.package","name": 12345}`)}, - }}, + setupCache: func(ctrl *gomock.Controller) catalogClient.Cache { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(fstest.MapFS{ + "invalid-pkg-present/olm.package/invalid-pkg-present.json": &fstest.MapFile{Data: []byte(`{"schema": "olm.package","name": 12345}`)}, + }, nil) + return cache + }, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { require.ErrorContains(t, err, `error loading package "invalid-pkg-present"`) assert.Nil(t, fbc) @@ -100,7 +119,11 @@ func TestClientGetPackage(t *testing.T) { name: "served, package present", catalog: defaultCatalog, pkgName: "pkg-present", - cache: &fakeCache{getFS: testFS}, + setupCache: func(ctrl *gomock.Controller) catalogClient.Cache { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(testFS, nil) + return cache + }, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { require.NoError(t, err) assert.Equal(t, &declcfg.DeclarativeConfig{Packages: []declcfg.Package{{Schema: declcfg.SchemaPackage, Name: "pkg-present"}}}, fbc) @@ -110,9 +133,11 @@ func TestClientGetPackage(t *testing.T) { name: "cache unpopulated", catalog: defaultCatalog, pkgName: "pkg-present", - cache: &fakeCache{putFunc: func(source string, errToCache error) (fs.FS, error) { - return testFS, nil - }}, + setupCache: func(ctrl *gomock.Controller) catalogClient.Cache { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, nil) + return cache + }, assert: func(t *testing.T, fbc *declcfg.DeclarativeConfig, err error) { assert.ErrorContains(t, err, `cache for catalog "catalog-1" not found`) }, @@ -120,14 +145,18 @@ func TestClientGetPackage(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { ctx := context.Background() + ctrl := gomock.NewController(t) + + var cache catalogClient.Cache + if tc.setupCache != nil { + cache = tc.setupCache(ctrl) + } - c := catalogClient.New(tc.cache, func() (*http.Client, error) { + mockTripper := mockhttputil.NewMockRoundTripper(ctrl) + c := catalogClient.New(cache, func() (*http.Client, error) { return &http.Client{ // This is to prevent actual network calls - Transport: &fakeTripper{resp: &http.Response{ - StatusCode: http.StatusOK, - Body: http.NoBody, - }}, + Transport: mockTripper, }, nil }) fbc, err := c.GetPackage(ctx, tc.catalog(), tc.pkgName) @@ -142,42 +171,54 @@ func TestClientPopulateCache(t *testing.T) { } type testCase struct { - name string - catalog func() *ocv1.ClusterCatalog - httpClient func() (*http.Client, error) - putFuncConstructor func(t *testing.T) func(source string, errToCache error) (fs.FS, error) - assert func(t *testing.T, fs fs.FS, err error) + name string + catalog func() *ocv1.ClusterCatalog + setupMocks func(t *testing.T, ctrl *gomock.Controller) (catalogClient.Cache, func() (*http.Client, error)) + assert func(t *testing.T, fs fs.FS, err error) } for _, tt := range []testCase{ { name: "cache unpopulated, successful http request", catalog: defaultCatalog, - httpClient: func() (*http.Client, error) { - return &http.Client{ - // This is to prevent actual network calls - Transport: &fakeTripper{resp: &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(strings.NewReader("fake-success-response-body")), - }}, - }, nil + setupMocks: func(t *testing.T, ctrl *gomock.Controller) (catalogClient.Cache, func() (*http.Client, error)) { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) { + buf := new(strings.Builder) + if source != nil { + _, _ = io.Copy(buf, source) + } + assert.Equal(t, "fake-success-response-body", buf.String()) + assert.NoError(t, errToCache) + return testFS, errToCache + }, + ) + + mockTripper := mockhttputil.NewMockRoundTripper(ctrl) + mockTripper.EXPECT().RoundTrip(gomock.Any()).Return(&http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader("fake-success-response-body")), + }, nil) + + httpClient := func() (*http.Client, error) { + return &http.Client{Transport: mockTripper}, nil + } + return cache, httpClient }, assert: func(t *testing.T, fs fs.FS, err error) { require.NoError(t, err) assert.Equal(t, testFS, fs) }, - putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { - return func(source string, errToCache error) (fs.FS, error) { - assert.Equal(t, "fake-success-response-body", source) - assert.NoError(t, errToCache) - return testFS, errToCache - } - }, }, { name: "not served", catalog: func() *ocv1.ClusterCatalog { return &ocv1.ClusterCatalog{ObjectMeta: metav1.ObjectMeta{Name: "catalog-1"}} }, + setupMocks: func(t *testing.T, ctrl *gomock.Controller) (catalogClient.Cache, func() (*http.Client, error)) { + cache := mockcatalogclient.NewMockCache(ctrl) + return cache, nil + }, assert: func(t *testing.T, fs fs.FS, err error) { assert.Nil(t, fs) assert.ErrorContains(t, err, `catalog "catalog-1" is not being served`) @@ -186,15 +227,20 @@ func TestClientPopulateCache(t *testing.T) { { name: "cache unpopulated, error on getting a http client", catalog: defaultCatalog, - httpClient: func() (*http.Client, error) { - return nil, errors.New("fake error getting a http client") - }, - putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { - return func(source string, errToCache error) (fs.FS, error) { - assert.Empty(t, source) - assert.Error(t, errToCache) - return nil, errToCache + setupMocks: func(t *testing.T, ctrl *gomock.Controller) (catalogClient.Cache, func() (*http.Client, error)) { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) { + assert.Nil(t, source) + assert.Error(t, errToCache) + return nil, errToCache + }, + ) + + httpClient := func() (*http.Client, error) { + return nil, errors.New("fake error getting a http client") } + return cache, httpClient }, assert: func(t *testing.T, fs fs.FS, err error) { assert.Nil(t, fs) @@ -204,18 +250,23 @@ func TestClientPopulateCache(t *testing.T) { { name: "cache unpopulated, error on http request", catalog: defaultCatalog, - httpClient: func() (*http.Client, error) { - return &http.Client{ - // This is to prevent actual network calls - Transport: &fakeTripper{err: errors.New("fake error on a http request")}, - }, nil - }, - putFuncConstructor: func(t *testing.T) func(source string, errToCache error) (fs.FS, error) { - return func(source string, errToCache error) (fs.FS, error) { - assert.Empty(t, source) - assert.Error(t, errToCache) - return nil, errToCache + setupMocks: func(t *testing.T, ctrl *gomock.Controller) (catalogClient.Cache, func() (*http.Client, error)) { + cache := mockcatalogclient.NewMockCache(ctrl) + cache.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) { + assert.Nil(t, source) + assert.Error(t, errToCache) + return nil, errToCache + }, + ) + + mockTripper := mockhttputil.NewMockRoundTripper(ctrl) + mockTripper.EXPECT().RoundTrip(gomock.Any()).Return(nil, errors.New("fake error on a http request")) + + httpClient := func() (*http.Client, error) { + return &http.Client{Transport: mockTripper}, nil } + return cache, httpClient }, assert: func(t *testing.T, fs fs.FS, err error) { assert.Nil(t, fs) @@ -225,14 +276,19 @@ func TestClientPopulateCache(t *testing.T) { { name: "cache unpopulated, unexpected http status", catalog: defaultCatalog, - httpClient: func() (*http.Client, error) { - return &http.Client{ - // This is to prevent actual network calls - Transport: &fakeTripper{resp: &http.Response{ - StatusCode: http.StatusInternalServerError, - Body: io.NopCloser(strings.NewReader("fake-unexpected-code-response-body")), - }}, - }, nil + setupMocks: func(t *testing.T, ctrl *gomock.Controller) (catalogClient.Cache, func() (*http.Client, error)) { + cache := mockcatalogclient.NewMockCache(ctrl) + + mockTripper := mockhttputil.NewMockRoundTripper(ctrl) + mockTripper.EXPECT().RoundTrip(gomock.Any()).Return(&http.Response{ + StatusCode: http.StatusInternalServerError, + Body: io.NopCloser(strings.NewReader("fake-unexpected-code-response-body")), + }, nil) + + httpClient := func() (*http.Client, error) { + return &http.Client{Transport: mockTripper}, nil + } + return cache, httpClient }, assert: func(t *testing.T, fs fs.FS, err error) { assert.Nil(t, fs) @@ -242,47 +298,12 @@ func TestClientPopulateCache(t *testing.T) { } { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() + ctrl := gomock.NewController(t) - cache := &fakeCache{} - if tt.putFuncConstructor != nil { - cache.putFunc = tt.putFuncConstructor(t) - } - - c := catalogClient.New(cache, tt.httpClient) + cache, httpClient := tt.setupMocks(t, ctrl) + c := catalogClient.New(cache, httpClient) fs, err := c.PopulateCache(ctx, tt.catalog()) tt.assert(t, fs, err) }) } } - -type fakeCache struct { - getFS fs.FS - getErr error - - putFunc func(source string, errToCache error) (fs.FS, error) -} - -func (c *fakeCache) Get(catalogName, resolvedRef string) (fs.FS, error) { - return c.getFS, c.getErr -} - -func (c *fakeCache) Put(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) { - if c.putFunc != nil { - buf := new(strings.Builder) - if source != nil { - io.Copy(buf, source) // nolint:errcheck - } - return c.putFunc(buf.String(), errToCache) - } - - return nil, errors.New("unexpected error") -} - -type fakeTripper struct { - resp *http.Response - err error -} - -func (ft *fakeTripper) RoundTrip(*http.Request) (*http.Response, error) { - return ft.resp, ft.err -} diff --git a/internal/operator-controller/config/config_test.go b/internal/operator-controller/config/config_test.go index 2f38ad6d35..461480b285 100644 --- a/internal/operator-controller/config/config_test.go +++ b/internal/operator-controller/config/config_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/utils/ptr" @@ -14,6 +15,7 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/config" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle" "github.com/operator-framework/operator-controller/internal/testing/bundle/csv" + mockconfig "github.com/operator-framework/operator-controller/internal/testutil/mock/config" ) func Test_UnmarshalConfig(t *testing.T) { @@ -353,8 +355,10 @@ func Test_UnmarshalConfig_EmptySchema(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - noSchemaBundle := &mockNoSchemaBundle{} - schema, err := noSchemaBundle.GetConfigSchema() + ctrl := gomock.NewController(t) + mockProvider := mockconfig.NewMockSchemaProvider(ctrl) + mockProvider.EXPECT().GetConfigSchema().Return(nil, nil) + schema, err := mockProvider.GetConfigSchema() require.NoError(t, err) config, err := config.UnmarshalConfig(tc.rawConfig, schema, "my-namespace") @@ -535,9 +539,17 @@ func Test_UnmarshalConfig_HelmLike(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - // Create a mock Helm package (real Helm would read values.schema.json) - helmBundle := &mockHelmBundle{schema: tc.helmSchema} - schema, err := helmBundle.GetConfigSchema() + ctrl := gomock.NewController(t) + mockProvider := mockconfig.NewMockSchemaProvider(ctrl) + + // Pre-unmarshal the schema string, mimicking what a real Helm bundle would do + var schemaMap map[string]any + if tc.helmSchema != "" { + require.NoError(t, json.Unmarshal([]byte(tc.helmSchema), &schemaMap)) + } + mockProvider.EXPECT().GetConfigSchema().Return(schemaMap, nil) + + schema, err := mockProvider.GetConfigSchema() require.NoError(t, err) // Same validation function works for Helm, registry+v1, registry+v2, etc. @@ -559,42 +571,8 @@ func Test_UnmarshalConfig_HelmLike(t *testing.T) { } } -// mockHelmBundle shows how Helm would plug into the validation system. -// -// Real implementation would: -// 1. Read values.schema.json from the Helm chart package -// 2. Parse it into a map[string]any -// 3. Return it (just like registry+v1 returns its generated schema) -// 4. Let the shared validation logic handle the rest -type mockHelmBundle struct { - schema string -} - -// GetConfigSchema returns the schema (in real Helm, read from values.schema.json). -func (h *mockHelmBundle) GetConfigSchema() (map[string]any, error) { - if h.schema == "" { - return nil, nil - } - var schemaMap map[string]any - if err := json.Unmarshal([]byte(h.schema), &schemaMap); err != nil { - return nil, err - } - return schemaMap, nil -} - -// mockNoSchemaBundle represents a bundle that doesn't provide a configuration schema. -type mockNoSchemaBundle struct{} - -func (e *mockNoSchemaBundle) GetConfigSchema() (map[string]any, error) { - // Return nil to indicate "no schema" (skip validation) - return nil, nil -} - // Test_GetDeploymentConfig tests the GetDeploymentConfig accessor method. func Test_GetDeploymentConfig(t *testing.T) { - // Create a bundle that returns nil schema (no validation) - bundle := &mockNoSchemaBundle{} - tests := []struct { name string rawConfig []byte @@ -646,7 +624,10 @@ func Test_GetDeploymentConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - schema, err := bundle.GetConfigSchema() + ctrl := gomock.NewController(t) + mockProvider := mockconfig.NewMockSchemaProvider(ctrl) + mockProvider.EXPECT().GetConfigSchema().Return(nil, nil) + schema, err := mockProvider.GetConfigSchema() require.NoError(t, err) cfg, err := config.UnmarshalConfig(tt.rawConfig, schema, "") @@ -681,7 +662,10 @@ func Test_GetDeploymentConfig(t *testing.T) { } }`) - schema, err := bundle.GetConfigSchema() + ctrl := gomock.NewController(t) + mockProvider := mockconfig.NewMockSchemaProvider(ctrl) + mockProvider.EXPECT().GetConfigSchema().Return(nil, nil) + schema, err := mockProvider.GetConfigSchema() require.NoError(t, err) cfg, err := config.UnmarshalConfig(rawConfig, schema, "") diff --git a/internal/operator-controller/contentmanager/cache/cache.go b/internal/operator-controller/contentmanager/cache/cache.go index f56cfd575d..a4fea5efc9 100644 --- a/internal/operator-controller/contentmanager/cache/cache.go +++ b/internal/operator-controller/contentmanager/cache/cache.go @@ -40,6 +40,7 @@ type CloserSyncingSource interface { io.Closer } +//go:generate mockgen -destination mock_sourcerer_gen_test.go -package cache -mock_names sourcerer=MockSourcerer github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache sourcerer,Watcher,CloserSyncingSource type sourcerer interface { // Source returns a CloserSyncingSource for the provided // GroupVersionKind. If the CloserSyncingSource encounters an diff --git a/internal/operator-controller/contentmanager/cache/cache_test.go b/internal/operator-controller/contentmanager/cache/cache_test.go index da44551681..2d20c42cb2 100644 --- a/internal/operator-controller/contentmanager/cache/cache_test.go +++ b/internal/operator-controller/contentmanager/cache/cache_test.go @@ -7,196 +7,175 @@ import ( "time" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/util/workqueue" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ocv1 "github.com/operator-framework/operator-controller/api/v1" ) -type mockWatcher struct { - err error -} - -var _ Watcher = (*mockWatcher)(nil) - -func (mw *mockWatcher) Watch(source.Source) error { - return mw.err -} - -type mockSourcerer struct { - err error - source CloserSyncingSource -} - -var _ sourcerer = (*mockSourcerer)(nil) - -func (ms *mockSourcerer) Source(_ schema.GroupVersionKind, _ client.Object, _ func(context.Context)) (CloserSyncingSource, error) { - if ms.err != nil { - return nil, ms.err - } - return ms.source, nil -} - -type mockSource struct { - err error -} - -var _ CloserSyncingSource = (*mockSource)(nil) +func TestCacheWatch(t *testing.T) { + ctrl := gomock.NewController(t) -func (ms *mockSource) Start(_ context.Context, _ workqueue.TypedRateLimitingInterface[reconcile.Request]) error { - return ms.err -} + mockSrc := NewMockCloserSyncingSource(ctrl) + mockSrc.EXPECT().WaitForSync(gomock.Any()).Return(nil) -func (ms *mockSource) WaitForSync(ctx context.Context) error { - return ms.err -} + mockSrcr := NewMockSourcerer(ctrl) + mockSrcr.EXPECT().Source(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSrc, nil) -func (ms *mockSource) Close() error { - return ms.err -} + mockW := NewMockWatcher(ctrl) + mockW.EXPECT().Watch(gomock.Any()).Return(nil) -func TestCacheWatch(t *testing.T) { - c := NewCache( - &mockSourcerer{ - source: &mockSource{}, - }, - &ocv1.ClusterExtension{}, - time.Second, - ) + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) pod := &corev1.Pod{} podGvk := corev1.SchemeGroupVersion.WithKind("Pod") pod.SetGroupVersionKind(podGvk) - require.NoError(t, c.Watch(context.Background(), &mockWatcher{}, pod)) + require.NoError(t, c.Watch(context.Background(), mockW, pod)) require.Contains(t, c.(*cache).sources, podGvk, "sources", c.(*cache).sources) } func TestCacheWatchInvalidGVK(t *testing.T) { - c := NewCache( - &mockSourcerer{ - source: &mockSource{}, - }, - &ocv1.ClusterExtension{}, - time.Second, - ) + ctrl := gomock.NewController(t) + + mockSrcr := NewMockSourcerer(ctrl) + mockW := NewMockWatcher(ctrl) + + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) pod := &corev1.Pod{} - require.Error(t, c.Watch(context.Background(), &mockWatcher{}, pod), "should fail on invalid GVK") + require.Error(t, c.Watch(context.Background(), mockW, pod), "should fail on invalid GVK") } func TestCacheWatchSourcererError(t *testing.T) { - c := NewCache( - &mockSourcerer{ - err: errors.New("error"), - }, - &ocv1.ClusterExtension{}, - time.Second, - ) + ctrl := gomock.NewController(t) + + mockSrcr := NewMockSourcerer(ctrl) + mockSrcr.EXPECT().Source(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("error")) + + mockW := NewMockWatcher(ctrl) + + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) pod := &corev1.Pod{} podGvk := corev1.SchemeGroupVersion.WithKind("Pod") pod.SetGroupVersionKind(podGvk) - require.Error(t, c.Watch(context.Background(), &mockWatcher{}, pod), "should fail when sourcerer returns an error") + require.Error(t, c.Watch(context.Background(), mockW, pod), "should fail when sourcerer returns an error") } func TestCacheWatchWatcherError(t *testing.T) { - c := NewCache( - &mockSourcerer{ - source: &mockSource{}, - }, - &ocv1.ClusterExtension{}, - time.Second, - ) + ctrl := gomock.NewController(t) + + mockSrc := NewMockCloserSyncingSource(ctrl) + + mockSrcr := NewMockSourcerer(ctrl) + mockSrcr.EXPECT().Source(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSrc, nil) + + mockW := NewMockWatcher(ctrl) + mockW.EXPECT().Watch(gomock.Any()).Return(errors.New("error")) + + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) pod := &corev1.Pod{} podGvk := corev1.SchemeGroupVersion.WithKind("Pod") pod.SetGroupVersionKind(podGvk) - require.Error(t, c.Watch(context.Background(), &mockWatcher{err: errors.New("error")}, pod), "should fail when watcher returns an error") + require.Error(t, c.Watch(context.Background(), mockW, pod), "should fail when watcher returns an error") } func TestCacheWatchSourceWaitForSyncError(t *testing.T) { - c := NewCache( - &mockSourcerer{ - source: &mockSource{ - err: errors.New("error"), - }, - }, - &ocv1.ClusterExtension{}, - time.Second, - ) + ctrl := gomock.NewController(t) + + mockSrc := NewMockCloserSyncingSource(ctrl) + mockSrc.EXPECT().WaitForSync(gomock.Any()).Return(errors.New("error")) + + mockSrcr := NewMockSourcerer(ctrl) + mockSrcr.EXPECT().Source(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockSrc, nil) + + mockW := NewMockWatcher(ctrl) + mockW.EXPECT().Watch(gomock.Any()).Return(nil) + + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) pod := &corev1.Pod{} podGvk := corev1.SchemeGroupVersion.WithKind("Pod") pod.SetGroupVersionKind(podGvk) - require.Error(t, c.Watch(context.Background(), &mockWatcher{}, pod), "should fail when source fails to sync") + require.Error(t, c.Watch(context.Background(), mockW, pod), "should fail when source fails to sync") require.NotContains(t, c.(*cache).sources, podGvk, "should not contain source entry in mapping") } func TestCacheWatchExistingSourceNotPanic(t *testing.T) { - c := NewCache( - &mockSourcerer{ - source: &mockSource{}, - }, - &ocv1.ClusterExtension{}, - time.Second, - ) + ctrl := gomock.NewController(t) + + mockSrcr := NewMockSourcerer(ctrl) + mockW := NewMockWatcher(ctrl) + mockSrc := NewMockCloserSyncingSource(ctrl) + + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) pod := &corev1.Pod{} podGvk := corev1.SchemeGroupVersion.WithKind("Pod") pod.SetGroupVersionKind(podGvk) - require.NoError(t, c.(*cache).addSource(podGvk, &mockSource{})) + require.NoError(t, c.(*cache).addSource(podGvk, mockSrc)) // In this case, a panic means there is a logic error somewhere in the // cache.Watch() method. It should never hit the condition where it panics // as it should never attempt to create a new source for one that already exists. - require.NotPanics(t, func() { _ = c.Watch(context.Background(), &mockWatcher{}, pod) }, "should never panic") + require.NotPanics(t, func() { _ = c.Watch(context.Background(), mockW, pod) }, "should never panic") } func TestCacheWatchRemovesStaleSources(t *testing.T) { - c := NewCache( - &mockSourcerer{ - source: &mockSource{}, - }, - &ocv1.ClusterExtension{}, - time.Second, + ctrl := gomock.NewController(t) + + podSrc := NewMockCloserSyncingSource(ctrl) + podSrc.EXPECT().WaitForSync(gomock.Any()).Return(nil) + podSrc.EXPECT().Close().Return(nil) + + secretSrc := NewMockCloserSyncingSource(ctrl) + secretSrc.EXPECT().WaitForSync(gomock.Any()).Return(nil) + + mockSrcr := NewMockSourcerer(ctrl) + gomock.InOrder( + mockSrcr.EXPECT().Source(gomock.Any(), gomock.Any(), gomock.Any()).Return(podSrc, nil), + mockSrcr.EXPECT().Source(gomock.Any(), gomock.Any(), gomock.Any()).Return(secretSrc, nil), ) + mockW := NewMockWatcher(ctrl) + mockW.EXPECT().Watch(gomock.Any()).Return(nil).Times(2) + + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) + pod := &corev1.Pod{} podGvk := corev1.SchemeGroupVersion.WithKind("Pod") pod.SetGroupVersionKind(podGvk) - require.NoError(t, c.Watch(context.Background(), &mockWatcher{}, pod)) + require.NoError(t, c.Watch(context.Background(), mockW, pod)) require.Contains(t, c.(*cache).sources, podGvk) secret := &corev1.Secret{} secretGvk := corev1.SchemeGroupVersion.WithKind("Secret") secret.SetGroupVersionKind(secretGvk) - require.NoError(t, c.Watch(context.Background(), &mockWatcher{}, secret)) + require.NoError(t, c.Watch(context.Background(), mockW, secret)) require.Contains(t, c.(*cache).sources, secretGvk) require.NotContains(t, c.(*cache).sources, podGvk) } func TestCacheWatchRemovingStaleSourcesError(t *testing.T) { - c := NewCache( - &mockSourcerer{ - source: &mockSource{}, - }, - &ocv1.ClusterExtension{}, - time.Second, - ) + ctrl := gomock.NewController(t) + + mockSrcr := NewMockSourcerer(ctrl) + + staleSrc := NewMockCloserSyncingSource(ctrl) + staleSrc.EXPECT().Close().Return(errors.New("error")) + + mockW := NewMockWatcher(ctrl) + + c := NewCache(mockSrcr, &ocv1.ClusterExtension{}, time.Second) podGvk := corev1.SchemeGroupVersion.WithKind("Pod") - require.NoError(t, c.(*cache).addSource(podGvk, &mockSource{ - err: errors.New("error"), - })) + require.NoError(t, c.(*cache).addSource(podGvk, staleSrc)) secret := &corev1.Secret{} secretGvk := corev1.SchemeGroupVersion.WithKind("Secret") secret.SetGroupVersionKind(secretGvk) - require.Error(t, c.Watch(context.Background(), &mockWatcher{}, secret)) + require.Error(t, c.Watch(context.Background(), mockW, secret)) } diff --git a/internal/operator-controller/contentmanager/cache/mock_sourcerer_gen_test.go b/internal/operator-controller/contentmanager/cache/mock_sourcerer_gen_test.go new file mode 100644 index 0000000000..a57d35fdd7 --- /dev/null +++ b/internal/operator-controller/contentmanager/cache/mock_sourcerer_gen_test.go @@ -0,0 +1,165 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache (interfaces: sourcerer,Watcher,CloserSyncingSource) +// +// Generated by this command: +// +// mockgen -destination mock_sourcerer_gen_test.go -package cache -mock_names sourcerer=MockSourcerer github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache sourcerer,Watcher,CloserSyncingSource +// + +// Package cache is a generated GoMock package. +package cache + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + schema "k8s.io/apimachinery/pkg/runtime/schema" + workqueue "k8s.io/client-go/util/workqueue" + client "sigs.k8s.io/controller-runtime/pkg/client" + reconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" + source "sigs.k8s.io/controller-runtime/pkg/source" +) + +// MockSourcerer is a mock of sourcerer interface. +type MockSourcerer struct { + ctrl *gomock.Controller + recorder *MockSourcererMockRecorder + isgomock struct{} +} + +// MockSourcererMockRecorder is the mock recorder for MockSourcerer. +type MockSourcererMockRecorder struct { + mock *MockSourcerer +} + +// NewMockSourcerer creates a new mock instance. +func NewMockSourcerer(ctrl *gomock.Controller) *MockSourcerer { + mock := &MockSourcerer{ctrl: ctrl} + mock.recorder = &MockSourcererMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSourcerer) EXPECT() *MockSourcererMockRecorder { + return m.recorder +} + +// Source mocks base method. +func (m *MockSourcerer) Source(arg0 schema.GroupVersionKind, arg1 client.Object, arg2 func(context.Context)) (CloserSyncingSource, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Source", arg0, arg1, arg2) + ret0, _ := ret[0].(CloserSyncingSource) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Source indicates an expected call of Source. +func (mr *MockSourcererMockRecorder) Source(arg0, arg1, arg2 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Source", reflect.TypeOf((*MockSourcerer)(nil).Source), arg0, arg1, arg2) +} + +// MockWatcher is a mock of Watcher interface. +type MockWatcher struct { + ctrl *gomock.Controller + recorder *MockWatcherMockRecorder + isgomock struct{} +} + +// MockWatcherMockRecorder is the mock recorder for MockWatcher. +type MockWatcherMockRecorder struct { + mock *MockWatcher +} + +// NewMockWatcher creates a new mock instance. +func NewMockWatcher(ctrl *gomock.Controller) *MockWatcher { + mock := &MockWatcher{ctrl: ctrl} + mock.recorder = &MockWatcherMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWatcher) EXPECT() *MockWatcherMockRecorder { + return m.recorder +} + +// Watch mocks base method. +func (m *MockWatcher) Watch(arg0 source.Source) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Watch", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Watch indicates an expected call of Watch. +func (mr *MockWatcherMockRecorder) Watch(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockWatcher)(nil).Watch), arg0) +} + +// MockCloserSyncingSource is a mock of CloserSyncingSource interface. +type MockCloserSyncingSource struct { + ctrl *gomock.Controller + recorder *MockCloserSyncingSourceMockRecorder + isgomock struct{} +} + +// MockCloserSyncingSourceMockRecorder is the mock recorder for MockCloserSyncingSource. +type MockCloserSyncingSourceMockRecorder struct { + mock *MockCloserSyncingSource +} + +// NewMockCloserSyncingSource creates a new mock instance. +func NewMockCloserSyncingSource(ctrl *gomock.Controller) *MockCloserSyncingSource { + mock := &MockCloserSyncingSource{ctrl: ctrl} + mock.recorder = &MockCloserSyncingSourceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCloserSyncingSource) EXPECT() *MockCloserSyncingSourceMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockCloserSyncingSource) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockCloserSyncingSourceMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCloserSyncingSource)(nil).Close)) +} + +// Start mocks base method. +func (m *MockCloserSyncingSource) Start(arg0 context.Context, arg1 workqueue.TypedRateLimitingInterface[reconcile.Request]) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockCloserSyncingSourceMockRecorder) Start(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockCloserSyncingSource)(nil).Start), arg0, arg1) +} + +// WaitForSync mocks base method. +func (m *MockCloserSyncingSource) WaitForSync(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitForSync", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitForSync indicates an expected call of WaitForSync. +func (mr *MockCloserSyncingSourceMockRecorder) WaitForSync(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForSync", reflect.TypeOf((*MockCloserSyncingSource)(nil).WaitForSync), ctx) +} diff --git a/internal/operator-controller/controllers/clustercatalog_controller_test.go b/internal/operator-controller/controllers/clustercatalog_controller_test.go index aad0bb006b..da16ed54f1 100644 --- a/internal/operator-controller/controllers/clustercatalog_controller_test.go +++ b/internal/operator-controller/controllers/clustercatalog_controller_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -17,6 +18,7 @@ import ( ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/operator-controller/controllers" "github.com/operator-framework/operator-controller/internal/operator-controller/scheme" + mockcontrollers "github.com/operator-framework/operator-controller/internal/testutil/mock/controllers" ) func TestClusterCatalogReconcilerFinalizers(t *testing.T) { @@ -24,14 +26,10 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { catalogKey := types.NamespacedName{Name: "test-catalog"} for _, tt := range []struct { - name string - catalog *ocv1.ClusterCatalog - catalogCache mockCatalogCache - catalogCachePopulator mockCatalogCachePopulator - wantGetCacheCalled bool - wantRemoveCacheCalled bool - wantPopulateCacheCalled bool - wantErr string + name string + catalog *ocv1.ClusterCatalog + setupMocks func(*gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) + wantErr string }{ { name: "catalog exists - cache unpopulated", @@ -47,14 +45,18 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { }, }, }, - catalogCachePopulator: mockCatalogCachePopulator{ - populateCacheFunc: func(ctx context.Context, catalog *ocv1.ClusterCatalog) (fs.FS, error) { - assert.Equal(t, catalogKey.Name, catalog.Name) - return nil, nil - }, + setupMocks: func(ctrl *gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) { + cache := mockcontrollers.NewMockCatalogCache(ctrl) + cache.EXPECT().Get(catalogKey.Name, fakeResolvedRef).Return(nil, nil) + populator := mockcontrollers.NewMockCatalogCachePopulator(ctrl) + populator.EXPECT().PopulateCache(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, catalog *ocv1.ClusterCatalog) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalog.Name) + return nil, nil + }, + ) + return cache, populator }, - wantGetCacheCalled: true, - wantPopulateCacheCalled: true, }, { name: "catalog exists - cache already populated", @@ -70,15 +72,19 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { }, }, }, - catalogCache: mockCatalogCache{ - getFunc: func(catalogName, resolvedRef string) (fs.FS, error) { - assert.Equal(t, catalogKey.Name, catalogName) - assert.Equal(t, fakeResolvedRef, resolvedRef) - // Just any non-nil fs.FS to simulate existence of cache - return fstest.MapFS{}, nil - }, + setupMocks: func(ctrl *gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) { + cache := mockcontrollers.NewMockCatalogCache(ctrl) + cache.EXPECT().Get(catalogKey.Name, fakeResolvedRef).DoAndReturn( + func(catalogName, resolvedRef string) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalogName) + assert.Equal(t, fakeResolvedRef, resolvedRef) + // Just any non-nil fs.FS to simulate existence of cache + return fstest.MapFS{}, nil + }, + ) + populator := mockcontrollers.NewMockCatalogCachePopulator(ctrl) + return cache, populator }, - wantGetCacheCalled: true, }, { name: "catalog exists - catalog not yet resolved", @@ -87,6 +93,11 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { Name: catalogKey.Name, }, }, + setupMocks: func(ctrl *gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) { + cache := mockcontrollers.NewMockCatalogCache(ctrl) + populator := mockcontrollers.NewMockCatalogCachePopulator(ctrl) + return cache, populator + }, }, { name: "catalog exists - error on cache population", @@ -102,15 +113,19 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { }, }, }, - catalogCachePopulator: mockCatalogCachePopulator{ - populateCacheFunc: func(ctx context.Context, catalog *ocv1.ClusterCatalog) (fs.FS, error) { - assert.Equal(t, catalogKey.Name, catalog.Name) - return nil, errors.New("fake error from populate cache function") - }, + setupMocks: func(ctrl *gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) { + cache := mockcontrollers.NewMockCatalogCache(ctrl) + cache.EXPECT().Get(catalogKey.Name, fakeResolvedRef).Return(nil, nil) + populator := mockcontrollers.NewMockCatalogCachePopulator(ctrl) + populator.EXPECT().PopulateCache(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, catalog *ocv1.ClusterCatalog) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalog.Name) + return nil, errors.New("fake error from populate cache function") + }, + ) + return cache, populator }, - wantGetCacheCalled: true, - wantPopulateCacheCalled: true, - wantErr: "error populating cache for catalog", + wantErr: "error populating cache for catalog", }, { name: "catalog exists - error on cache get", @@ -126,41 +141,56 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { }, }, }, - catalogCache: mockCatalogCache{ - getFunc: func(catalogName, resolvedRef string) (fs.FS, error) { - assert.Equal(t, catalogKey.Name, catalogName) - assert.Equal(t, fakeResolvedRef, resolvedRef) - return nil, errors.New("fake error from cache get function") - }, + setupMocks: func(ctrl *gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) { + cache := mockcontrollers.NewMockCatalogCache(ctrl) + cache.EXPECT().Get(catalogKey.Name, fakeResolvedRef).DoAndReturn( + func(catalogName, resolvedRef string) (fs.FS, error) { + assert.Equal(t, catalogKey.Name, catalogName) + assert.Equal(t, fakeResolvedRef, resolvedRef) + return nil, errors.New("fake error from cache get function") + }, + ) + populator := mockcontrollers.NewMockCatalogCachePopulator(ctrl) + populator.EXPECT().PopulateCache(gomock.Any(), gomock.Any()).Return(nil, nil) + return cache, populator }, - wantGetCacheCalled: true, - wantPopulateCacheCalled: true, }, { name: "catalog does not exist", - catalogCache: mockCatalogCache{ - removeFunc: func(catalogName string) error { - assert.Equal(t, catalogKey.Name, catalogName) - return nil - }, + setupMocks: func(ctrl *gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) { + cache := mockcontrollers.NewMockCatalogCache(ctrl) + cache.EXPECT().Remove(catalogKey.Name).DoAndReturn( + func(catalogName string) error { + assert.Equal(t, catalogKey.Name, catalogName) + return nil + }, + ) + populator := mockcontrollers.NewMockCatalogCachePopulator(ctrl) + return cache, populator }, - wantRemoveCacheCalled: true, }, { name: "catalog does not exist - error on removal", - catalogCache: mockCatalogCache{ - removeFunc: func(catalogName string) error { - assert.Equal(t, catalogKey.Name, catalogName) - return errors.New("fake error from remove") - }, + setupMocks: func(ctrl *gomock.Controller) (controllers.CatalogCache, controllers.CatalogCachePopulator) { + cache := mockcontrollers.NewMockCatalogCache(ctrl) + cache.EXPECT().Remove(catalogKey.Name).DoAndReturn( + func(catalogName string) error { + assert.Equal(t, catalogKey.Name, catalogName) + return errors.New("fake error from remove") + }, + ) + populator := mockcontrollers.NewMockCatalogCachePopulator(ctrl) + return cache, populator }, - wantRemoveCacheCalled: true, - wantErr: "error removing cache for catalog", + wantErr: "error removing cache for catalog", }, } { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() + mockCtrl := gomock.NewController(t) + cache, populator := tt.setupMocks(mockCtrl) + clientBuilder := fake.NewClientBuilder().WithScheme(scheme.Scheme) if tt.catalog != nil { clientBuilder = clientBuilder.WithObjects(tt.catalog) @@ -169,8 +199,8 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { reconciler := &controllers.ClusterCatalogReconciler{ Client: cl, - CatalogCache: controllers.CatalogCache(&tt.catalogCache), - CatalogCachePopulator: controllers.CatalogCachePopulator(&tt.catalogCachePopulator), + CatalogCache: cache, + CatalogCachePopulator: populator, } result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: catalogKey}) @@ -180,49 +210,6 @@ func TestClusterCatalogReconcilerFinalizers(t *testing.T) { require.ErrorContains(t, err, tt.wantErr) } require.Equal(t, ctrl.Result{}, result) - - assert.Equal(t, tt.wantRemoveCacheCalled, tt.catalogCache.removeFuncCalled) - assert.Equal(t, tt.wantGetCacheCalled, tt.catalogCache.getFuncCalled) - assert.Equal(t, tt.wantPopulateCacheCalled, tt.catalogCachePopulator.populateCacheCalled) }) } } - -type mockCatalogCache struct { - removeFuncCalled bool - removeFunc func(catalogName string) error - getFuncCalled bool - getFunc func(catalogName, resolvedRef string) (fs.FS, error) -} - -func (m *mockCatalogCache) Remove(catalogName string) error { - m.removeFuncCalled = true - if m.removeFunc != nil { - return m.removeFunc(catalogName) - } - - return nil -} - -func (m *mockCatalogCache) Get(catalogName, resolvedRef string) (fs.FS, error) { - m.getFuncCalled = true - if m.getFunc != nil { - return m.getFunc(catalogName, resolvedRef) - } - - return nil, nil -} - -type mockCatalogCachePopulator struct { - populateCacheCalled bool - populateCacheFunc func(ctx context.Context, catalog *ocv1.ClusterCatalog) (fs.FS, error) -} - -func (m *mockCatalogCachePopulator) PopulateCache(ctx context.Context, catalog *ocv1.ClusterCatalog) (fs.FS, error) { - m.populateCacheCalled = true - if m.populateCacheFunc != nil { - return m.populateCacheFunc(ctx, catalog) - } - - return nil, nil -} diff --git a/internal/operator-controller/controllers/clusterextension_controller_test.go b/internal/operator-controller/controllers/clusterextension_controller_test.go index 4f524dd9a6..cec683db8a 100644 --- a/internal/operator-controller/controllers/clusterextension_controller_test.go +++ b/internal/operator-controller/controllers/clusterextension_controller_test.go @@ -12,8 +12,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" + "go.uber.org/mock/gomock" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/storage/driver" corev1 "k8s.io/api/core/v1" @@ -29,7 +28,6 @@ import ( crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer" "sigs.k8s.io/controller-runtime/pkg/reconcile" - helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" "github.com/operator-framework/operator-registry/alpha/declcfg" ocv1 "github.com/operator-framework/operator-controller/api/v1" @@ -41,6 +39,8 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/labels" "github.com/operator-framework/operator-controller/internal/operator-controller/resolve" imageutil "github.com/operator-framework/operator-controller/internal/shared/util/image" + mockcontrollers "github.com/operator-framework/operator-controller/internal/testutil/mock/controllers" + mockhelmclient "github.com/operator-framework/operator-controller/internal/testutil/mock/helmclient" ) // Describe: ClusterExtension Controller Test @@ -58,9 +58,7 @@ func TestClusterExtensionShortCircuitsReconcileDuringDeletion(t *testing.T) { installedBundleGetterCalledErr := errors.New("revision states getter called") cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - Err: installedBundleGetterCalledErr, - } + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), nil, installedBundleGetterCalledErr) }) checkInstalledBundleGetterCalled := func(t require.TestingT, err error, args ...interface{}) { @@ -282,20 +280,18 @@ func TestClusterExtensionUpgradeShowsInstalledBundleDeprecation(t *testing.T) { }}, }, nil }) - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - Package: pkgName, - BundleMetadata: ocv1.BundleMetadata{ - Name: installedBundleName, // v1.0.0 installed - Version: "1.0.0", - }, - Image: fmt.Sprintf("quay.io/example/%s@sha256:installed100", pkgName), + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + Package: pkgName, + BundleMetadata: ocv1.BundleMetadata{ + Name: installedBundleName, // v1.0.0 installed + Version: "1.0.0", }, + Image: fmt.Sprintf("quay.io/example/%s@sha256:installed100", pkgName), }, - } - d.ImagePuller = &imageutil.MockPuller{ImageFS: fstest.MapFS{}} - d.Applier = &MockApplier{} + }, nil) + d.ImagePuller = &imageutil.FakePuller{ImageFS: fstest.MapFS{}} + d.Applier = newMockApplier(gomock.NewController(t), false, nil) }) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} @@ -384,20 +380,18 @@ func TestClusterExtensionUpgradeFromDeprecatedBundleClearsDeprecation(t *testing }}, }, nil }) - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - Package: pkgName, - BundleMetadata: ocv1.BundleMetadata{ - Name: installedBundleName, - Version: "1.0.1", - }, - Image: fmt.Sprintf("quay.io/example/%s@sha256:installed101", pkgName), + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + Package: pkgName, + BundleMetadata: ocv1.BundleMetadata{ + Name: installedBundleName, + Version: "1.0.1", }, + Image: fmt.Sprintf("quay.io/example/%s@sha256:installed101", pkgName), }, - } - d.ImagePuller = &imageutil.MockPuller{ImageFS: fstest.MapFS{}} - d.Applier = &MockApplier{installCompleted: true} + }, nil) + d.ImagePuller = &imageutil.FakePuller{ImageFS: fstest.MapFS{}} + d.Applier = newMockApplier(gomock.NewController(t), true, nil) }) extKey := types.NamespacedName{Name: fmt.Sprintf("cluster-extension-test-%s", rand.String(8))} @@ -469,18 +463,16 @@ func TestClusterExtensionResolutionFailsWithoutCatalogDeprecationData(t *testing return nil, nil, nil, fmt.Errorf("no bundles found for package %q", pkgName) }) - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - Package: pkgName, - BundleMetadata: ocv1.BundleMetadata{ - Name: installedBundleName, - Version: "1.0.0", - }, - Image: "example.com/installed@sha256:deadbeef", + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + Package: pkgName, + BundleMetadata: ocv1.BundleMetadata{ + Name: installedBundleName, + Version: "1.0.0", }, + Image: "example.com/installed@sha256:deadbeef", }, - } + }, nil) }) // Create a ClusterCatalog so CheckCatalogsExist returns true, causing retry instead of fallback @@ -588,7 +580,7 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { } cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.ImagePuller = &imageutil.MockPuller{ + d.ImagePuller = &imageutil.FakePuller{ Error: tc.pullErr, } }, @@ -667,7 +659,7 @@ func TestClusterExtensionResolutionSuccessfulUnpackFails(t *testing.T) { func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) { cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.ImagePuller = &imageutil.MockPuller{ + d.ImagePuller = &imageutil.FakePuller{ ImageFS: fstest.MapFS{}, } d.Resolver = resolve.Func(func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { @@ -680,9 +672,7 @@ func TestClusterExtensionResolutionAndUnpackSuccessfulApplierFails(t *testing.T) Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) - d.Applier = &MockApplier{ - err: errors.New("apply failure"), - } + d.Applier = newMockApplier(gomock.NewController(t), false, errors.New("apply failure")) }) ctx := context.Background() @@ -785,11 +775,9 @@ func TestClusterExtensionBoxcutterApplierFailsDoesNotLeakDeprecationErrors(t *te cl, reconciler := newClientAndReconciler(t, func(d *deps) { // Boxcutter keeps a rolling revision when apply fails. We mirror that state so the test uses // the same inputs the runtime would see. - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - RollingOut: []*controllers.RevisionMetadata{{}}, - }, - } + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + RollingOut: []*controllers.RevisionMetadata{{}}, + }, nil) d.Resolver = resolve.Func(func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { v := bundle.VersionRelease{ Version: bsemver.MustParse("1.0.0"), @@ -800,8 +788,8 @@ func TestClusterExtensionBoxcutterApplierFailsDoesNotLeakDeprecationErrors(t *te Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) - d.ImagePuller = &imageutil.MockPuller{ImageFS: fstest.MapFS{}} - d.Applier = &MockApplier{err: errors.New("boxcutter apply failure")} + d.ImagePuller = &imageutil.FakePuller{ImageFS: fstest.MapFS{}} + d.Applier = newMockApplier(gomock.NewController(t), false, errors.New("boxcutter apply failure")) }) ctx := context.Background() @@ -989,9 +977,7 @@ func TestValidateClusterExtension(t *testing.T) { ctx := context.Background() cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{}, - } + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{}, nil) d.Validators = tt.validators }) @@ -1047,11 +1033,15 @@ func TestValidateClusterExtension(t *testing.T) { } func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { - mockApplier := &MockApplier{ - installCompleted: true, - } + // This test calls Reconcile twice: first with a successful applier, + // then with a failing applier. We use gomock.InOrder to sequence the calls. + mockCtrl := gomock.NewController(t) + applier := mockcontrollers.NewMockApplier(mockCtrl) + firstCall := applier.EXPECT().Apply(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(true, "", nil) + applier.EXPECT().Apply(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(false, "", errors.New("apply failure")).After(firstCall) + cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.ImagePuller = &imageutil.MockPuller{ + d.ImagePuller = &imageutil.FakePuller{ ImageFS: fstest.MapFS{}, } d.Resolver = resolve.Func(func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { @@ -1065,15 +1055,13 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { }, &v, nil, nil }) - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - BundleMetadata: ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, - Image: "quay.io/operatorhubio/prometheus@fake1.0.0", - }, + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + BundleMetadata: ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, - } - d.Applier = mockApplier + }, nil) + d.Applier = applier }) ctx := context.Background() @@ -1114,9 +1102,6 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { require.Equal(t, ctrl.Result{}, res) require.NoError(t, err) - mockApplier.installCompleted = false - mockApplier.err = errors.New("apply failure") - res, err = reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: extKey}) require.Equal(t, ctrl.Result{}, res) require.Error(t, err) @@ -1146,7 +1131,7 @@ func TestClusterExtensionApplierFailsWithBundleInstalled(t *testing.T) { func TestClusterExtensionManagerFailed(t *testing.T) { cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.ImagePuller = &imageutil.MockPuller{ + d.ImagePuller = &imageutil.FakePuller{ ImageFS: fstest.MapFS{}, } d.Resolver = resolve.Func(func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { @@ -1159,10 +1144,7 @@ func TestClusterExtensionManagerFailed(t *testing.T) { Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) - d.Applier = &MockApplier{ - installCompleted: true, - err: errors.New("manager fail"), - } + d.Applier = newMockApplier(gomock.NewController(t), true, errors.New("manager fail")) }) ctx := context.Background() @@ -1225,7 +1207,7 @@ func TestClusterExtensionManagerFailed(t *testing.T) { func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.ImagePuller = &imageutil.MockPuller{ + d.ImagePuller = &imageutil.FakePuller{ ImageFS: fstest.MapFS{}, } d.Resolver = resolve.Func(func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { @@ -1238,10 +1220,7 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) - d.Applier = &MockApplier{ - installCompleted: true, - err: errors.New("watch error"), - } + d.Applier = newMockApplier(gomock.NewController(t), true, errors.New("watch error")) }) ctx := context.Background() @@ -1306,7 +1285,7 @@ func TestClusterExtensionManagedContentCacheWatchFail(t *testing.T) { func TestClusterExtensionInstallationSucceeds(t *testing.T) { cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.ImagePuller = &imageutil.MockPuller{ + d.ImagePuller = &imageutil.FakePuller{ ImageFS: fstest.MapFS{}, } d.Resolver = resolve.Func(func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { @@ -1319,9 +1298,7 @@ func TestClusterExtensionInstallationSucceeds(t *testing.T) { Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) - d.Applier = &MockApplier{ - installCompleted: true, - } + d.Applier = newMockApplier(gomock.NewController(t), true, nil) }) ctx := context.Background() @@ -1387,7 +1364,7 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { finalizersMessage := "still have finalizers" var rfinalizers crfinalizer.Finalizers cl, reconciler := newClientAndReconciler(t, func(d *deps) { - d.ImagePuller = &imageutil.MockPuller{ + d.ImagePuller = &imageutil.FakePuller{ ImageFS: fstest.MapFS{}, } d.Resolver = resolve.Func(func(ctx context.Context, ext *ocv1.ClusterExtension, installedBundle *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { @@ -1400,17 +1377,13 @@ func TestClusterExtensionDeleteFinalizerFails(t *testing.T) { Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, &v, nil, nil }) - d.Applier = &MockApplier{ - installCompleted: true, - } - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - BundleMetadata: ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, - Image: "quay.io/operatorhubio/prometheus@fake1.0.0", - }, + d.Applier = newMockApplier(gomock.NewController(t), true, nil) + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + BundleMetadata: ocv1.BundleMetadata{Name: "prometheus.v1.0.0", Version: "1.0.0"}, + Image: "quay.io/operatorhubio/prometheus@fake1.0.0", }, - } + }, nil) rfinalizers = d.Finalizers }) @@ -2524,75 +2497,35 @@ func filterDeprecationConditions(conditions []metav1.Condition) []metav1.Conditi return result } -type MockActionGetter struct { - description string - rels []*release.Release - err error - expectedInstalled *controllers.RevisionMetadata - expectedError error -} - -func (mag *MockActionGetter) ActionClientFor(ctx context.Context, obj client.Object) (helmclient.ActionInterface, error) { - return mag, nil -} - -func (mag *MockActionGetter) Get(name string, opts ...helmclient.GetOption) (*release.Release, error) { - return nil, nil -} - -// This is the function we are really testing -func (mag *MockActionGetter) History(name string, opts ...helmclient.HistoryOption) ([]*release.Release, error) { - return mag.rels, mag.err -} - -func (mag *MockActionGetter) Install(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.InstallOption) (*release.Release, error) { - return nil, nil -} - -func (mag *MockActionGetter) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...helmclient.UpgradeOption) (*release.Release, error) { - return nil, nil -} - -func (mag *MockActionGetter) Uninstall(name string, opts ...helmclient.UninstallOption) (*release.UninstallReleaseResponse, error) { - return nil, nil -} - -func (mag *MockActionGetter) Reconcile(rel *release.Release) error { - return nil -} - -func (mag *MockActionGetter) Config() *action.Configuration { - return nil -} - func TestGetInstalledBundleHistory(t *testing.T) { - getter := controllers.HelmRevisionStatesGetter{} - ext := ocv1.ClusterExtension{ ObjectMeta: metav1.ObjectMeta{ Name: "test-ext", }, } - mag := []MockActionGetter{ + tests := []struct { + description string + rels []*release.Release + err error + expectedInstalled *controllers.RevisionMetadata + expectedError error + }{ { - "No return", - nil, nil, - nil, nil, + description: "No return", }, { - "ErrReleaseNotFound (special case)", - nil, driver.ErrReleaseNotFound, - nil, nil, + description: "ErrReleaseNotFound (special case)", + err: driver.ErrReleaseNotFound, }, { - "Error from History", - nil, fmt.Errorf("generic error"), - nil, fmt.Errorf("generic error"), + description: "Error from History", + err: fmt.Errorf("generic error"), + expectedError: fmt.Errorf("generic error"), }, { - "One item in history", - []*release.Release{ + description: "One item in history", + rels: []*release.Release{ { Name: "test-ext", Info: &release.Info{ @@ -2606,19 +2539,18 @@ func TestGetInstalledBundleHistory(t *testing.T) { }, }, }, - nil, - &controllers.RevisionMetadata{ + expectedInstalled: &controllers.RevisionMetadata{ BundleMetadata: ocv1.BundleMetadata{ Name: "test-ext", Version: "1.0", Release: ptr.To("2"), }, Image: "bundle-ref", - }, nil, + }, }, { - "Two items in history", - []*release.Release{ + description: "Two items in history", + rels: []*release.Release{ { Name: "test-ext", Info: &release.Info{ @@ -2642,29 +2574,36 @@ func TestGetInstalledBundleHistory(t *testing.T) { }, }, }, - nil, - &controllers.RevisionMetadata{ + expectedInstalled: &controllers.RevisionMetadata{ BundleMetadata: ocv1.BundleMetadata{ Name: "test-ext", Version: "1.0", }, Image: "bundle-ref-1", - }, nil, + }, }, } - for _, tst := range mag { - t.Log(tst.description) - getter.ActionClientGetter = &tst - md, err := getter.GetRevisionStates(context.Background(), &ext) - if tst.expectedError != nil { - require.Equal(t, tst.expectedError, err) - require.Nil(t, md) - } else { - require.NoError(t, err) - require.Equal(t, tst.expectedInstalled, md.Installed) - require.Nil(t, md.RollingOut) - } + for _, tst := range tests { + t.Run(tst.description, func(t *testing.T) { + ctrl := gomock.NewController(t) + m := mockhelmclient.NewMockActionClientGetterAndInterface(ctrl) + m.EXPECT().ActionClientFor(gomock.Any(), gomock.Any()).Return(m, nil).AnyTimes() + m.EXPECT().History(gomock.Any(), gomock.Any()).Return(tst.rels, tst.err).AnyTimes() + + getter := controllers.HelmRevisionStatesGetter{ + ActionClientGetter: m, + } + md, err := getter.GetRevisionStates(context.Background(), &ext) + if tst.expectedError != nil { + require.Equal(t, tst.expectedError, err) + require.Nil(t, md) + } else { + require.NoError(t, err) + require.Equal(t, tst.expectedInstalled, md.Installed) + require.Nil(t, md.RollingOut) + } + }) } } @@ -2689,21 +2628,15 @@ func TestResolutionFallbackToInstalledBundle(t *testing.T) { return nil, nil, nil, fmt.Errorf("catalog unavailable") }) // Applier succeeds (resources maintained) - d.Applier = &MockApplier{ - installCompleted: true, - installStatus: "", - err: nil, - } - d.ImagePuller = &imageutil.MockPuller{ImageFS: fstest.MapFS{}} - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - Package: "test-pkg", - BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, - Image: "test-image:1.0.0", - }, + d.Applier = newMockApplier(gomock.NewController(t), true, nil) + d.ImagePuller = &imageutil.FakePuller{ImageFS: fstest.MapFS{}} + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + Package: "test-pkg", + BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, + Image: "test-image:1.0.0", }, - } + }, nil) }) ctx := context.Background() @@ -2781,13 +2714,11 @@ func TestResolutionFallbackToInstalledBundle(t *testing.T) { d.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { return nil, nil, nil, fmt.Errorf("catalog unavailable") }) - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, - }, + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, }, - } + }, nil) }) ctx := context.Background() @@ -2852,17 +2783,15 @@ func TestResolutionFallbackToInstalledBundle(t *testing.T) { Image: "test-image:2.0.0", }, &v, &declcfg.Deprecation{}, nil }) - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - Package: "test-pkg", - BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, - Image: "test-image:1.0.0", - }, + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + Package: "test-pkg", + BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, + Image: "test-image:1.0.0", }, - } - d.ImagePuller = &imageutil.MockPuller{ImageFS: fstest.MapFS{}} - d.Applier = &MockApplier{installCompleted: true} + }, nil) + d.ImagePuller = &imageutil.FakePuller{ImageFS: fstest.MapFS{}} + d.Applier = newMockApplier(gomock.NewController(t), true, nil) }) ctx := context.Background() @@ -2939,15 +2868,13 @@ func TestResolutionFallbackToInstalledBundle(t *testing.T) { d.Resolver = resolve.Func(func(_ context.Context, _ *ocv1.ClusterExtension, _ *ocv1.BundleMetadata) (*declcfg.Bundle, *bundle.VersionRelease, *declcfg.Deprecation, error) { return nil, nil, nil, fmt.Errorf("transient catalog issue: cache stale") }) - d.RevisionStatesGetter = &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{ - Installed: &controllers.RevisionMetadata{ - Package: "test-pkg", - BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, - Image: "test-image:1.0.0", - }, + d.RevisionStatesGetter = newMockRevisionStatesGetter(gomock.NewController(t), &controllers.RevisionStates{ + Installed: &controllers.RevisionMetadata{ + Package: "test-pkg", + BundleMetadata: ocv1.BundleMetadata{Name: "test.1.0.0", Version: "1.0.0"}, + Image: "test-image:1.0.0", }, - } + }, nil) }) ctx := context.Background() diff --git a/internal/operator-controller/controllers/clusterobjectset_controller.go b/internal/operator-controller/controllers/clusterobjectset_controller.go index bc081b6765..eb0bbc1222 100644 --- a/internal/operator-controller/controllers/clusterobjectset_controller.go +++ b/internal/operator-controller/controllers/clusterobjectset_controller.go @@ -58,6 +58,7 @@ type ClusterObjectSetReconciler struct { Clock clock.Clock } +//go:generate mockgen -source clusterobjectset_controller.go -destination mock_trackingcache_gen_test.go -package controllers -mock_names trackingCache=MockTrackingCache -exclude_interfaces Sourcoser type trackingCache interface { client.Reader Source(handler handler.EventHandler, predicates ...predicate.Predicate) source.Source diff --git a/internal/operator-controller/controllers/clusterobjectset_controller_internal_test.go b/internal/operator-controller/controllers/clusterobjectset_controller_internal_test.go index 654de33883..4ecf44c10f 100644 --- a/internal/operator-controller/controllers/clusterobjectset_controller_internal_test.go +++ b/internal/operator-controller/controllers/clusterobjectset_controller_internal_test.go @@ -10,21 +10,17 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/operator-controller/labels" @@ -143,6 +139,7 @@ func Test_ClusterObjectSetReconciler_listPreviousRevisions(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) testClient := fake.NewClientBuilder(). WithScheme(testScheme). WithObjects(tc.existingObjs()...). @@ -150,7 +147,7 @@ func Test_ClusterObjectSetReconciler_listPreviousRevisions(t *testing.T) { reconciler := &ClusterObjectSetReconciler{ Client: testClient, - TrackingCache: &mockTrackingCacheInternal{client: testClient}, + TrackingCache: newMockTrackingCacheInternal(mockCtrl, testClient), } currentRev := &ocv1.ClusterObjectSet{} @@ -220,28 +217,14 @@ func newTestClusterObjectSetInternal(t *testing.T, name string) *ocv1.ClusterObj return rev } -type mockTrackingCacheInternal struct { - client client.Client -} - -func (m *mockTrackingCacheInternal) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { - return m.client.Get(ctx, key, obj, opts...) -} - -func (m *mockTrackingCacheInternal) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { - return m.client.List(ctx, list, opts...) -} - -func (m *mockTrackingCacheInternal) Free(ctx context.Context, user client.Object) error { - return nil -} - -func (m *mockTrackingCacheInternal) Watch(ctx context.Context, user client.Object, gvks sets.Set[schema.GroupVersionKind]) error { - return nil -} - -func (m *mockTrackingCacheInternal) Source(h handler.EventHandler, predicates ...predicate.Predicate) source.Source { - return nil +func newMockTrackingCacheInternal(ctrl *gomock.Controller, cl client.Client) *MockTrackingCache { + m := NewMockTrackingCache(ctrl) + m.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(cl.Get).AnyTimes() + m.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(cl.List).AnyTimes() + m.EXPECT().Source(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + m.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + m.EXPECT().Free(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + return m } func TestComputePhaseDigest(t *testing.T) { diff --git a/internal/operator-controller/controllers/clusterobjectset_controller_test.go b/internal/operator-controller/controllers/clusterobjectset_controller_test.go index 75e7a2f8e4..146e141f1c 100644 --- a/internal/operator-controller/controllers/clusterobjectset_controller_test.go +++ b/internal/operator-controller/controllers/clusterobjectset_controller_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -15,7 +16,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/clock" clocktesting "k8s.io/utils/clock/testing" "pkg.package-operator.run/boxcutter" @@ -26,19 +26,19 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" ocv1 "github.com/operator-framework/operator-controller/api/v1" "github.com/operator-framework/operator-controller/internal/operator-controller/controllers" "github.com/operator-framework/operator-controller/internal/operator-controller/labels" + mockcontrollers "github.com/operator-framework/operator-controller/internal/testutil/mock/controllers" + mockmachinery "github.com/operator-framework/operator-controller/internal/testutil/mock/machinery" ) const clusterObjectSetName = "test-ext-1" func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing.T) { testScheme := newScheme(t) + mockCtrl := gomock.NewController(t) for _, tc := range []struct { name string @@ -51,7 +51,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing { name: "sets teardown finalizer", reconcilingRevisionName: clusterObjectSetName, - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -69,7 +69,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing { name: "Available condition is not updated on error if its not already set", reconcilingRevisionName: clusterObjectSetName, - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), revisionReconcileErr: errors.New("some error"), existingObjs: func() []client.Object { ext := newTestClusterExtension() @@ -89,7 +89,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing { name: "Available condition is updated to Unknown on error if its been already set", reconcilingRevisionName: clusterObjectSetName, - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), revisionReconcileErr: errors.New("some error"), existingObjs: func() []client.Object { ext := newTestClusterExtension() @@ -120,7 +120,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing { name: "set Available:False:RollingOut status condition during rollout when no probe failures are detected", reconcilingRevisionName: clusterObjectSetName, - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -143,24 +143,22 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing { name: "set Available:False:ProbeFailure condition when probe failures are detected and revision is in transition", reconcilingRevisionName: clusterObjectSetName, - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ inTransition: true, isComplete: false, phases: []machinery.PhaseResult{ - mockPhaseResult{ + newMockPhaseResult(mockCtrl, phaseResultConfig{ name: "somephase", isComplete: false, objects: []machinery.ObjectResult{ - mockObjectResult{ - success: true, + newMockObjectResult(mockCtrl, objectResultConfig{ probes: machinerytypes.ProbeResultContainer{ boxcutter.ProgressProbeType: { Status: machinerytypes.ProbeStatusTrue, }, }, - }, - mockObjectResult{ - success: false, + }), + newMockObjectResult(mockCtrl, objectResultConfig{ object: func() client.Object { obj := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -180,15 +178,14 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, }, }, - }, + }), }, - }, - mockPhaseResult{ + }), + newMockPhaseResult(mockCtrl, phaseResultConfig{ name: "someotherphase", isComplete: false, objects: []machinery.ObjectResult{ - mockObjectResult{ - success: false, + newMockObjectResult(mockCtrl, objectResultConfig{ object: func() client.Object { obj := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -207,11 +204,11 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, }, }, - }, + }), }, - }, + }), }, - }, + }), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -234,24 +231,22 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing { name: "set Available:False:ProbeFailure condition when probe failures are detected and revision is not in transition", reconcilingRevisionName: clusterObjectSetName, - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ inTransition: false, isComplete: false, phases: []machinery.PhaseResult{ - mockPhaseResult{ + newMockPhaseResult(mockCtrl, phaseResultConfig{ name: "somephase", isComplete: false, objects: []machinery.ObjectResult{ - mockObjectResult{ - success: true, + newMockObjectResult(mockCtrl, objectResultConfig{ probes: machinerytypes.ProbeResultContainer{ boxcutter.ProgressProbeType: machinerytypes.ProbeResult{ Status: machinerytypes.ProbeStatusTrue, }, }, - }, - mockObjectResult{ - success: false, + }), + newMockObjectResult(mockCtrl, objectResultConfig{ object: func() client.Object { obj := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -271,15 +266,14 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, }, }, - }, + }), }, - }, - mockPhaseResult{ + }), + newMockPhaseResult(mockCtrl, phaseResultConfig{ name: "someotherphase", isComplete: false, objects: []machinery.ObjectResult{ - mockObjectResult{ - success: false, + newMockObjectResult(mockCtrl, objectResultConfig{ object: func() client.Object { obj := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -298,11 +292,11 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, }, }, - }, + }), }, - }, + }), }, - }, + }), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -347,9 +341,9 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, { name: "set Progressing:True:RollingOut condition while revision is transitioning", - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ inTransition: true, - }, + }), reconcilingRevisionName: clusterObjectSetName, existingObjs: func() []client.Object { ext := newTestClusterExtension() @@ -372,10 +366,10 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, { name: "set Progressing:True:Succeeded once transition rollout is finished", - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ inTransition: false, isComplete: true, - }, + }), reconcilingRevisionName: clusterObjectSetName, existingObjs: func() []client.Object { ext := newTestClusterExtension() @@ -405,9 +399,9 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, { name: "set Available:True:ProbesSucceeded and Succeeded:True:Succeeded conditions on successful revision rollout", - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ isComplete: true, - }, + }), reconcilingRevisionName: clusterObjectSetName, existingObjs: func() []client.Object { ext := newTestClusterExtension() @@ -444,9 +438,9 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, { name: "archive previous revisions on successful rollout", - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ isComplete: true, - }, + }), reconcilingRevisionName: "test-ext-3", existingObjs: func() []client.Object { ext := newTestClusterExtension() @@ -478,6 +472,8 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing }, } { t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + // create extension and cluster extension testClient := fake.NewClientBuilder(). WithScheme(testScheme). @@ -486,15 +482,15 @@ func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing Build() // reconcile cluster extension revision - mockEngine := &mockRevisionEngine{ - reconcile: func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { return tc.revisionResult, tc.revisionReconcileErr - }, - } + }, nil, + ) result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, - TrackingCache: &mockTrackingCache{client: testClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, nil), + TrackingCache: newMockTrackingCache(mockCtrl, testClient, nil), }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ Name: tc.reconcilingRevisionName, @@ -522,6 +518,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ValidationError_Retries(t *testin ) testScheme := newScheme(t) + mockCtrl := gomock.NewController(t) for _, tc := range []struct { name string @@ -529,7 +526,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ValidationError_Retries(t *testin }{ { name: "retries on revision result validation error", - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ validationError: &validation.RevisionValidationError{ RevisionName: "test-ext-1", RevisionNumber: 1, @@ -559,13 +556,13 @@ func Test_ClusterObjectSetReconciler_Reconcile_ValidationError_Retries(t *testin }, }, }, - }, + }), }, { name: "retries on revision result phase validation error", - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ phases: []machinery.PhaseResult{ - mockPhaseResult{ + newMockPhaseResult(mockCtrl, phaseResultConfig{ validationError: &validation.PhaseValidationError{ PhaseName: "everything", PhaseError: fmt.Errorf("some error"), @@ -589,12 +586,13 @@ func Test_ClusterObjectSetReconciler_Reconcile_ValidationError_Retries(t *testin }, }, }, - }, + }), }, - }, + }), }, } { t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -606,15 +604,15 @@ func Test_ClusterObjectSetReconciler_Reconcile_ValidationError_Retries(t *testin Build() // reconcile cluster extension revision - mockEngine := &mockRevisionEngine{ - reconcile: func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { return tc.revisionResult, nil - }, - } + }, nil, + ) result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, - TrackingCache: &mockTrackingCache{client: testClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, nil), + TrackingCache: newMockTrackingCache(mockCtrl, testClient, nil), }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ Name: clusterObjectSetName, @@ -637,12 +635,13 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) testScheme := newScheme(t) require.NoError(t, corev1.AddToScheme(testScheme)) + mockCtrl := gomock.NewController(t) for _, tc := range []struct { name string existingObjs func() []client.Object revisionResult machinery.RevisionResult - revisionEngineTeardownFn func(*testing.T) func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) + revisionEngineTeardownFn func(*gomock.Controller) func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) revisionEngineFactoryErr error validate func(*testing.T, client.Client) trackingCacheFreeFn func(context.Context, client.Object) error @@ -651,7 +650,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) }{ { name: "teardown finalizer is removed", - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -668,13 +667,13 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) require.NoError(t, err) require.NotContains(t, "olm.operatorframework.io/teardown", rev.Finalizers) }, - revisionEngineTeardownFn: func(t *testing.T) func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + revisionEngineTeardownFn: func(ctrl *gomock.Controller) func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return nil }, }, { name: "set Available:Unknown:Reconciling when tracking cache free fails during deletion", - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -700,13 +699,13 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) require.Equal(t, ocv1.ClusterObjectSetReasonReconciling, cond.Reason) require.Contains(t, cond.Message, "tracking cache free failed") }, - revisionEngineTeardownFn: func(t *testing.T) func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + revisionEngineTeardownFn: func(ctrl *gomock.Controller) func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return nil }, }, { name: "set Available:Archived:Unknown and Progressing:False:Archived conditions when a revision is archived", - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -716,11 +715,11 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, - revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + revisionEngineTeardownFn: func(ctrl *gomock.Controller) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { - return &mockRevisionTeardownResult{ + return newMockRevisionTeardownResult(mockCtrl, revisionTeardownResultConfig{ isComplete: true, - }, nil + }), nil } }, validate: func(t *testing.T, c client.Client) { @@ -746,7 +745,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) }, { name: "set Progressing:True:Retrying and requeue when archived revision archival is incomplete", - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -756,11 +755,11 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, - revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + revisionEngineTeardownFn: func(ctrl *gomock.Controller) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { - return &mockRevisionTeardownResult{ + return newMockRevisionTeardownResult(mockCtrl, revisionTeardownResultConfig{ isComplete: false, - }, nil + }), nil } }, expectedResult: ctrl.Result{RequeueAfter: 5 * time.Second}, @@ -782,7 +781,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) }, { name: "return error and set retrying conditions when archived revision teardown fails", - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -792,7 +791,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, - revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + revisionEngineTeardownFn: func(ctrl *gomock.Controller) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return nil, fmt.Errorf("teardown failed: connection refused") } @@ -816,7 +815,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) }, { name: "return error and set retrying conditions when factory fails to create engine during archived teardown", - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -826,7 +825,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, - revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + revisionEngineTeardownFn: func(ctrl *gomock.Controller) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return nil }, revisionEngineFactoryErr: fmt.Errorf("token getter failed"), @@ -849,7 +848,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) }, { name: "revision is torn down when in archived state and finalizer is removed", - revisionResult: mockRevisionResult{}, + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{}), existingObjs: func() []client.Object { ext := newTestClusterExtension() rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) @@ -873,11 +872,11 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) }) return []client.Object{rev1, ext} }, - revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + revisionEngineTeardownFn: func(ctrl *gomock.Controller) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { return func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { - return &mockRevisionTeardownResult{ + return newMockRevisionTeardownResult(mockCtrl, revisionTeardownResultConfig{ isComplete: true, - }, nil + }), nil } }, validate: func(t *testing.T, c client.Client) { @@ -891,6 +890,8 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) }, } { t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + // create extension and cluster extension testClient := fake.NewClientBuilder(). WithScheme(testScheme). @@ -899,20 +900,17 @@ func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) Build() // reconcile cluster extension revision - mockEngine := &mockRevisionEngine{ - reconcile: func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { return tc.revisionResult, nil }, - teardown: tc.revisionEngineTeardownFn(t), - } - factory := &mockRevisionEngineFactory{engine: mockEngine, createErr: tc.revisionEngineFactoryErr} + tc.revisionEngineTeardownFn(mockCtrl), + ) + factory := newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, tc.revisionEngineFactoryErr) result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: factory, - TrackingCache: &mockTrackingCache{ - client: testClient, - freeFn: tc.trackingCacheFreeFn, - }, + TrackingCache: newMockTrackingCache(mockCtrl, testClient, tc.trackingCacheFreeFn), }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ Name: clusterObjectSetName, @@ -940,6 +938,7 @@ func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { testScheme := newScheme(t) require.NoError(t, corev1.AddToScheme(testScheme)) + mockCtrl := gomock.NewController(t) for _, tc := range []struct { name string @@ -961,9 +960,9 @@ func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { }, // 61sec elapsed since the creation of the revision clock: clocktesting.NewFakeClock(time.Date(2022, 1, 1, 0, 1, 1, 0, time.UTC)), - revisionResult: &mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ inTransition: true, - }, + }), validate: func(t *testing.T, c client.Client) { rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ @@ -985,9 +984,9 @@ func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { return []client.Object{rev1, ext} }, clock: clocktesting.NewFakeClock(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)), - revisionResult: &mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ inTransition: true, - }, + }), reconcileResult: ctrl.Result{RequeueAfter: 60 * time.Second}, validate: func(t *testing.T, c client.Client) { rev := &ocv1.ClusterObjectSet{} @@ -1017,9 +1016,9 @@ func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { return []client.Object{rev1, ext} }, clock: clocktesting.NewFakeClock(time.Date(2022, 1, 1, 0, 5, 0, 0, time.UTC)), - revisionResult: &mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ isComplete: true, - }, + }), validate: func(t *testing.T, c client.Client) { rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ @@ -1054,9 +1053,9 @@ func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { }) return []client.Object{rev1, ext} }, - revisionResult: &mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ inTransition: true, - }, + }), validate: func(t *testing.T, c client.Client) { rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ @@ -1070,6 +1069,8 @@ func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) + // create extension and cluster extension testClient := fake.NewClientBuilder(). WithScheme(testScheme). @@ -1078,18 +1079,16 @@ func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { Build() // reconcile cluster extension revision - mockEngine := &mockRevisionEngine{ - reconcile: func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { return tc.revisionResult, nil - }, - } + }, nil, + ) result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, - TrackingCache: &mockTrackingCache{ - client: testClient, - }, - Clock: tc.clock, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, nil), + TrackingCache: newMockTrackingCache(mockCtrl, testClient, nil), + Clock: tc.clock, }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ Name: clusterObjectSetName, @@ -1174,211 +1173,148 @@ func newTestClusterObjectSet(t *testing.T, revisionName string, ext *ocv1.Cluste return rev } -type mockRevisionEngine struct { - teardown func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) - reconcile func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) -} +// Helper constructors for gomock-generated result mocks. -func (m mockRevisionEngine) Teardown(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { - return m.teardown(ctx, rev) -} - -func (m mockRevisionEngine) Reconcile(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { - return m.reconcile(ctx, rev) -} - -// mockRevisionEngineFactory creates mock RevisionEngines for testing -type mockRevisionEngineFactory struct { - engine controllers.RevisionEngine - createErr error -} - -func (f *mockRevisionEngineFactory) CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterObjectSet) (controllers.RevisionEngine, error) { - if f.createErr != nil { - return nil, f.createErr - } - return f.engine, nil -} - -type mockRevisionResult struct { +type revisionResultConfig struct { validationError *validation.RevisionValidationError phases []machinery.PhaseResult inTransition bool isComplete bool hasProgressed bool - string string -} - -func (m mockRevisionResult) GetValidationError() *validation.RevisionValidationError { - return m.validationError -} - -func (m mockRevisionResult) GetPhases() []machinery.PhaseResult { - return m.phases -} - -func (m mockRevisionResult) InTransition() bool { - return m.inTransition + str string } -func (m mockRevisionResult) IsComplete() bool { - return m.isComplete +func newMockRevisionResult(ctrl *gomock.Controller, cfg revisionResultConfig) *mockmachinery.MockRevisionResult { + m := mockmachinery.NewMockRevisionResult(ctrl) + m.EXPECT().GetValidationError().Return(cfg.validationError).AnyTimes() + m.EXPECT().GetPhases().Return(cfg.phases).AnyTimes() + m.EXPECT().InTransition().Return(cfg.inTransition).AnyTimes() + m.EXPECT().IsComplete().Return(cfg.isComplete).AnyTimes() + m.EXPECT().HasProgressed().Return(cfg.hasProgressed).AnyTimes() + m.EXPECT().String().Return(cfg.str).AnyTimes() + return m } -func (m mockRevisionResult) HasProgressed() bool { - return m.hasProgressed -} - -func (m mockRevisionResult) String() string { - return m.string -} - -var _ machinery.PhaseResult = &mockPhaseResult{} - -type mockPhaseResult struct { +type phaseResultConfig struct { name string validationError *validation.PhaseValidationError objects []machinery.ObjectResult inTransition bool isComplete bool hasProgressed bool - string string -} - -func (m mockPhaseResult) GetName() string { - return m.name + str string } -func (m mockPhaseResult) GetValidationError() *validation.PhaseValidationError { - return m.validationError +func newMockPhaseResult(ctrl *gomock.Controller, cfg phaseResultConfig) *mockmachinery.MockPhaseResult { + m := mockmachinery.NewMockPhaseResult(ctrl) + m.EXPECT().GetName().Return(cfg.name).AnyTimes() + m.EXPECT().GetValidationError().Return(cfg.validationError).AnyTimes() + m.EXPECT().GetObjects().Return(cfg.objects).AnyTimes() + m.EXPECT().InTransition().Return(cfg.inTransition).AnyTimes() + m.EXPECT().IsComplete().Return(cfg.isComplete).AnyTimes() + m.EXPECT().HasProgressed().Return(cfg.hasProgressed).AnyTimes() + m.EXPECT().String().Return(cfg.str).AnyTimes() + return m } -func (m mockPhaseResult) GetObjects() []machinery.ObjectResult { - return m.objects -} - -func (m mockPhaseResult) InTransition() bool { - return m.inTransition -} - -func (m mockPhaseResult) IsComplete() bool { - return m.isComplete -} - -func (m mockPhaseResult) HasProgressed() bool { - return m.hasProgressed -} - -func (m mockPhaseResult) String() string { - return m.string -} - -var _ machinery.ObjectResult = &mockObjectResult{} - -type mockObjectResult struct { +type objectResultConfig struct { action machinery.Action object machinery.Object - success bool complete bool paused bool probes machinerytypes.ProbeResultContainer - string string -} - -func (m mockObjectResult) ProbeResults() machinerytypes.ProbeResultContainer { - return m.probes -} - -func (m mockObjectResult) IsComplete() bool { - return m.complete + str string } -func (m mockObjectResult) IsPaused() bool { - return m.paused +func newMockObjectResult(ctrl *gomock.Controller, cfg objectResultConfig) *mockmachinery.MockObjectResult { + m := mockmachinery.NewMockObjectResult(ctrl) + m.EXPECT().ProbeResults().Return(cfg.probes).AnyTimes() + m.EXPECT().IsComplete().Return(cfg.complete).AnyTimes() + m.EXPECT().IsPaused().Return(cfg.paused).AnyTimes() + m.EXPECT().Action().Return(cfg.action).AnyTimes() + m.EXPECT().Object().Return(cfg.object).AnyTimes() + m.EXPECT().String().Return(cfg.str).AnyTimes() + return m } -func (m mockObjectResult) Action() machinery.Action { - return m.action -} - -func (m mockObjectResult) Object() machinery.Object { - return m.object -} - -func (m mockObjectResult) Success() bool { - return m.success -} - -func (m mockObjectResult) String() string { - return m.string -} - -var _ machinery.RevisionTeardownResult = mockRevisionTeardownResult{} - -type mockRevisionTeardownResult struct { +type revisionTeardownResultConfig struct { phases []machinery.PhaseTeardownResult isComplete bool waitingPhaseNames []string activePhaseName string phaseIsActive bool gonePhaseNames []string - string string -} - -func (m mockRevisionTeardownResult) GetPhases() []machinery.PhaseTeardownResult { - return m.phases -} - -func (m mockRevisionTeardownResult) IsComplete() bool { - return m.isComplete -} - -func (m mockRevisionTeardownResult) GetWaitingPhaseNames() []string { - return m.waitingPhaseNames -} - -func (m mockRevisionTeardownResult) GetActivePhaseName() (string, bool) { - return m.activePhaseName, m.phaseIsActive -} - -func (m mockRevisionTeardownResult) GetGonePhaseNames() []string { - return m.gonePhaseNames -} - -func (m mockRevisionTeardownResult) String() string { - return m.string + str string } -type mockTrackingCache struct { - client client.Client - freeFn func(context.Context, client.Object) error +func newMockRevisionTeardownResult(ctrl *gomock.Controller, cfg revisionTeardownResultConfig) *mockmachinery.MockRevisionTeardownResult { + m := mockmachinery.NewMockRevisionTeardownResult(ctrl) + m.EXPECT().GetPhases().Return(cfg.phases).AnyTimes() + m.EXPECT().IsComplete().Return(cfg.isComplete).AnyTimes() + m.EXPECT().GetWaitingPhaseNames().Return(cfg.waitingPhaseNames).AnyTimes() + m.EXPECT().GetActivePhaseName().Return(cfg.activePhaseName, cfg.phaseIsActive).AnyTimes() + m.EXPECT().GetGonePhaseNames().Return(cfg.gonePhaseNames).AnyTimes() + m.EXPECT().String().Return(cfg.str).AnyTimes() + return m } -func (m *mockTrackingCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { - return m.client.Get(ctx, key, obj, opts...) -} - -func (m *mockTrackingCache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { - return m.client.List(ctx, list, opts...) +func newMockTrackingCache(ctrl *gomock.Controller, cl client.Client, freeFn func(ctx context.Context, user client.Object) error) *controllers.MockTrackingCache { + m := controllers.NewMockTrackingCache(ctrl) + m.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(cl.Get).AnyTimes() + m.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(cl.List).AnyTimes() + m.EXPECT().Source(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + m.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + if freeFn != nil { + m.EXPECT().Free(gomock.Any(), gomock.Any()).DoAndReturn(freeFn).AnyTimes() + } else { + m.EXPECT().Free(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + } + return m } -func (m *mockTrackingCache) Source(handler handler.EventHandler, predicates ...predicate.Predicate) source.Source { - panic("not implemented") +// newNoopMockRevisionEngine creates a MockRevisionEngine with no expectations set. +// Useful for tests where the engine is never called (e.g., error paths that fail before reaching the engine). +func newNoopMockRevisionEngine(ctrl *gomock.Controller) *mockcontrollers.MockRevisionEngine { + return mockcontrollers.NewMockRevisionEngine(ctrl) } -func (m *mockTrackingCache) Watch(ctx context.Context, user client.Object, gvks sets.Set[schema.GroupVersionKind]) error { - return nil +// newMockRevisionEngineWithReconcile creates a MockRevisionEngine with a Reconcile expectation. +// If teardownFn is non-nil, a Teardown expectation is also configured. +func newMockRevisionEngineWithReconcile( + ctrl *gomock.Controller, + reconcileFn func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error), + teardownFn func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error), +) *mockcontrollers.MockRevisionEngine { + m := mockcontrollers.NewMockRevisionEngine(ctrl) + m.EXPECT().Reconcile(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(reconcileFn).AnyTimes() + if teardownFn != nil { + m.EXPECT().Teardown(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(teardownFn).AnyTimes() + } + return m } -func (m *mockTrackingCache) Free(ctx context.Context, user client.Object) error { - if m.freeFn != nil { - return m.freeFn(ctx, user) - } - return nil +// newMockRevisionEngineFactoryWithEngine creates a MockRevisionEngineFactory +// that returns the given engine and error. +func newMockRevisionEngineFactoryWithEngine( + ctrl *gomock.Controller, + engine controllers.RevisionEngine, + createErr error, +) *mockcontrollers.MockRevisionEngineFactory { + m := mockcontrollers.NewMockRevisionEngineFactory(ctrl) + m.EXPECT().CreateRevisionEngine(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, rev *ocv1.ClusterObjectSet) (controllers.RevisionEngine, error) { + if createErr != nil { + return nil, createErr + } + return engine, nil + }, + ).AnyTimes() + return m } func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testing.T) { testScheme := newScheme(t) + mockCtrl := gomock.NewController(t) for _, tc := range []struct { name string @@ -1417,12 +1353,12 @@ func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testi cerB1 := newTestClusterObjectSet(t, "ext-B-1", extB, testScheme) return []client.Object{extA, extB, cerA2, cerB1} }, - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ phases: []machinery.PhaseResult{ - mockPhaseResult{ + newMockPhaseResult(mockCtrl, phaseResultConfig{ name: "everything", objects: []machinery.ObjectResult{ - mockObjectResult{ + newMockObjectResult(mockCtrl, objectResultConfig{ action: machinery.ActionCollision, object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -1448,11 +1384,11 @@ func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testi Status: machinerytypes.ProbeStatusTrue, }, }, - }, + }), }, - }, + }), }, - }, + }), expectCollision: true, }, { @@ -1474,12 +1410,12 @@ func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testi cerA2 := newTestClusterObjectSet(t, "ext-A-2", extA, testScheme) return []client.Object{extA, cerA1, cerA2} }, - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ phases: []machinery.PhaseResult{ - mockPhaseResult{ + newMockPhaseResult(mockCtrl, phaseResultConfig{ name: "everything", objects: []machinery.ObjectResult{ - mockObjectResult{ + newMockObjectResult(mockCtrl, objectResultConfig{ action: machinery.ActionProgressed, object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -1505,11 +1441,11 @@ func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testi Status: machinerytypes.ProbeStatusTrue, }, }, - }, + }), }, - }, + }), }, - }, + }), expectCollision: false, }, { @@ -1530,12 +1466,12 @@ func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testi cerB1 := newTestClusterObjectSet(t, "ext-B-1", extB, testScheme) return []client.Object{extB, cerB1} }, - revisionResult: mockRevisionResult{ + revisionResult: newMockRevisionResult(mockCtrl, revisionResultConfig{ phases: []machinery.PhaseResult{ - mockPhaseResult{ + newMockPhaseResult(mockCtrl, phaseResultConfig{ name: "everything", objects: []machinery.ObjectResult{ - mockObjectResult{ + newMockObjectResult(mockCtrl, objectResultConfig{ action: machinery.ActionProgressed, object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -1562,30 +1498,31 @@ func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testi Status: machinerytypes.ProbeStatusTrue, }, }, - }, + }), }, - }, + }), }, - }, + }), expectCollision: false, }, } { t.Run(tc.name, func(t *testing.T) { + mockCtrl := gomock.NewController(t) testClient := fake.NewClientBuilder(). WithScheme(testScheme). WithStatusSubresource(&ocv1.ClusterObjectSet{}). WithObjects(tc.existingObjs()...). Build() - mockEngine := &mockRevisionEngine{ - reconcile: func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { return tc.revisionResult, nil - }, - } + }, nil, + ) result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, - TrackingCache: &mockTrackingCache{client: testClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, nil), + TrackingCache: newMockTrackingCache(mockCtrl, testClient, nil), }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ Name: tc.reconcilingRevisionName, @@ -1669,6 +1606,7 @@ func Test_ClusterObjectSetReconciler_getScopedClient_Errors(t *testing.T) { testScheme := newScheme(t) t.Run("works with serviceAccount annotation and without owner label", func(t *testing.T) { + mockCtrl := gomock.NewController(t) rev := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-rev-1", @@ -1693,16 +1631,16 @@ func Test_ClusterObjectSetReconciler_getScopedClient_Errors(t *testing.T) { WithObjects(rev). Build() - mockEngine := &mockRevisionEngine{ - reconcile: func(ctx context.Context, r machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { - return &mockRevisionResult{}, nil - }, - } + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(ctx context.Context, r machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + return newMockRevisionResult(mockCtrl, revisionResultConfig{}), nil + }, nil, + ) reconciler := &controllers.ClusterObjectSetReconciler{ Client: testClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, - TrackingCache: &mockTrackingCache{client: testClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, nil), + TrackingCache: newMockTrackingCache(mockCtrl, testClient, nil), } _, err := reconciler.Reconcile(t.Context(), ctrl.Request{ @@ -1713,6 +1651,7 @@ func Test_ClusterObjectSetReconciler_getScopedClient_Errors(t *testing.T) { }) t.Run("missing serviceAccount annotation", func(t *testing.T) { + mockCtrl := gomock.NewController(t) rev := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-rev-1", @@ -1732,14 +1671,12 @@ func Test_ClusterObjectSetReconciler_getScopedClient_Errors(t *testing.T) { WithObjects(rev). Build() - failingFactory := &mockRevisionEngineFactory{ - createErr: errors.New("missing serviceAccount name annotation"), - } + failingFactory := newMockRevisionEngineFactoryWithEngine(mockCtrl, nil, errors.New("missing serviceAccount name annotation")) reconciler := &controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: failingFactory, - TrackingCache: &mockTrackingCache{client: testClient}, + TrackingCache: newMockTrackingCache(mockCtrl, testClient, nil), } _, err := reconciler.Reconcile(t.Context(), ctrl.Request{ @@ -1751,6 +1688,7 @@ func Test_ClusterObjectSetReconciler_getScopedClient_Errors(t *testing.T) { }) t.Run("factory fails to create engine", func(t *testing.T) { + mockCtrl := gomock.NewController(t) ext := newTestClusterExtension() rev := newTestClusterObjectSet(t, "test-rev", ext, testScheme) @@ -1759,14 +1697,12 @@ func Test_ClusterObjectSetReconciler_getScopedClient_Errors(t *testing.T) { WithObjects(ext, rev). Build() - failingFactory := &mockRevisionEngineFactory{ - createErr: errors.New("token getter failed"), - } + failingFactory := newMockRevisionEngineFactoryWithEngine(mockCtrl, nil, errors.New("token getter failed")) reconciler := &controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: failingFactory, - TrackingCache: &mockTrackingCache{client: testClient}, + TrackingCache: newMockTrackingCache(mockCtrl, testClient, nil), } _, err := reconciler.Reconcile(t.Context(), ctrl.Request{ diff --git a/internal/operator-controller/controllers/mock_trackingcache_gen_test.go b/internal/operator-controller/controllers/mock_trackingcache_gen_test.go new file mode 100644 index 0000000000..c15fa9cd0e --- /dev/null +++ b/internal/operator-controller/controllers/mock_trackingcache_gen_test.go @@ -0,0 +1,132 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: clusterobjectset_controller.go +// +// Generated by this command: +// +// mockgen -source clusterobjectset_controller.go -destination mock_trackingcache_gen_test.go -package controllers -mock_names trackingCache=MockTrackingCache -exclude_interfaces Sourcoser +// + +// Package controllers is a generated GoMock package. +package controllers + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + schema "k8s.io/apimachinery/pkg/runtime/schema" + sets "k8s.io/apimachinery/pkg/util/sets" + client "sigs.k8s.io/controller-runtime/pkg/client" + handler "sigs.k8s.io/controller-runtime/pkg/handler" + predicate "sigs.k8s.io/controller-runtime/pkg/predicate" + source "sigs.k8s.io/controller-runtime/pkg/source" +) + +// MockTrackingCache is a mock of trackingCache interface. +type MockTrackingCache struct { + ctrl *gomock.Controller + recorder *MockTrackingCacheMockRecorder + isgomock struct{} +} + +// MockTrackingCacheMockRecorder is the mock recorder for MockTrackingCache. +type MockTrackingCacheMockRecorder struct { + mock *MockTrackingCache +} + +// NewMockTrackingCache creates a new mock instance. +func NewMockTrackingCache(ctrl *gomock.Controller) *MockTrackingCache { + mock := &MockTrackingCache{ctrl: ctrl} + mock.recorder = &MockTrackingCacheMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockTrackingCache) EXPECT() *MockTrackingCacheMockRecorder { + return m.recorder +} + +// Free mocks base method. +func (m *MockTrackingCache) Free(ctx context.Context, user client.Object) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Free", ctx, user) + ret0, _ := ret[0].(error) + return ret0 +} + +// Free indicates an expected call of Free. +func (mr *MockTrackingCacheMockRecorder) Free(ctx, user any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Free", reflect.TypeOf((*MockTrackingCache)(nil).Free), ctx, user) +} + +// Get mocks base method. +func (m *MockTrackingCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, key, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockTrackingCacheMockRecorder) Get(ctx, key, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, key, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockTrackingCache)(nil).Get), varargs...) +} + +// List mocks base method. +func (m *MockTrackingCache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, list} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// List indicates an expected call of List. +func (mr *MockTrackingCacheMockRecorder) List(ctx, list any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, list}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockTrackingCache)(nil).List), varargs...) +} + +// Source mocks base method. +func (m *MockTrackingCache) Source(arg0 handler.EventHandler, predicates ...predicate.Predicate) source.Source { + m.ctrl.T.Helper() + varargs := []any{arg0} + for _, a := range predicates { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Source", varargs...) + ret0, _ := ret[0].(source.Source) + return ret0 +} + +// Source indicates an expected call of Source. +func (mr *MockTrackingCacheMockRecorder) Source(arg0 any, predicates ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0}, predicates...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Source", reflect.TypeOf((*MockTrackingCache)(nil).Source), varargs...) +} + +// Watch mocks base method. +func (m *MockTrackingCache) Watch(ctx context.Context, user client.Object, gvks sets.Set[schema.GroupVersionKind]) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Watch", ctx, user, gvks) + ret0, _ := ret[0].(error) + return ret0 +} + +// Watch indicates an expected call of Watch. +func (mr *MockTrackingCacheMockRecorder) Watch(ctx, user, gvks any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockTrackingCache)(nil).Watch), ctx, user, gvks) +} diff --git a/internal/operator-controller/controllers/resolve_ref_test.go b/internal/operator-controller/controllers/resolve_ref_test.go index 53e0df2c93..8a9a102743 100644 --- a/internal/operator-controller/controllers/resolve_ref_test.go +++ b/internal/operator-controller/controllers/resolve_ref_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apimachineryruntime "k8s.io/apimachinery/pkg/runtime" @@ -34,6 +35,7 @@ func newSchemeWithCoreV1(t *testing.T) *apimachineryruntime.Scheme { } func TestResolveObjectRef_PlainJSON(t *testing.T) { + mockCtrl := gomock.NewController(t) testScheme := newSchemeWithCoreV1(t) cmObj := map[string]interface{}{ @@ -70,15 +72,15 @@ func TestResolveObjectRef_PlainJSON(t *testing.T) { WithStatusSubresource(&ocv1.ClusterObjectSet{}). Build() - mockEngine := &mockRevisionEngine{ - reconcile: func(_ context.Context, _ machinerytypes.Revision, _ ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { - return mockRevisionResult{}, nil - }, - } + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(_ context.Context, _ machinerytypes.Revision, _ ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + return newMockRevisionResult(mockCtrl, revisionResultConfig{}), nil + }, nil, + ) reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, - TrackingCache: &mockTrackingCache{client: fakeClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, nil), + TrackingCache: newMockTrackingCache(mockCtrl, fakeClient, nil), Clock: clocktesting.NewFakeClock(metav1.Now().Time), } @@ -89,6 +91,7 @@ func TestResolveObjectRef_PlainJSON(t *testing.T) { } func TestResolveObjectRef_GzipCompressed(t *testing.T) { + mockCtrl := gomock.NewController(t) testScheme := newSchemeWithCoreV1(t) cmObj := map[string]interface{}{ @@ -132,15 +135,15 @@ func TestResolveObjectRef_GzipCompressed(t *testing.T) { WithStatusSubresource(&ocv1.ClusterObjectSet{}). Build() - mockEngine := &mockRevisionEngine{ - reconcile: func(_ context.Context, _ machinerytypes.Revision, _ ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { - return mockRevisionResult{}, nil - }, - } + mockEngine := newMockRevisionEngineWithReconcile(mockCtrl, + func(_ context.Context, _ machinerytypes.Revision, _ ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) { + return newMockRevisionResult(mockCtrl, revisionResultConfig{}), nil + }, nil, + ) reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, - TrackingCache: &mockTrackingCache{client: fakeClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, mockEngine, nil), + TrackingCache: newMockTrackingCache(mockCtrl, fakeClient, nil), Clock: clocktesting.NewFakeClock(metav1.Now().Time), } @@ -151,6 +154,7 @@ func TestResolveObjectRef_GzipCompressed(t *testing.T) { } func TestResolveObjectRef_SecretNotFound(t *testing.T) { + mockCtrl := gomock.NewController(t) testScheme := newSchemeWithCoreV1(t) cos := newRefTestCOS("ref-notfound-1", ocv1.ObjectSourceRef{ @@ -167,8 +171,8 @@ func TestResolveObjectRef_SecretNotFound(t *testing.T) { reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: &mockRevisionEngine{}}, - TrackingCache: &mockTrackingCache{client: fakeClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, newNoopMockRevisionEngine(mockCtrl), nil), + TrackingCache: newMockTrackingCache(mockCtrl, fakeClient, nil), Clock: clocktesting.NewFakeClock(metav1.Now().Time), } @@ -180,6 +184,7 @@ func TestResolveObjectRef_SecretNotFound(t *testing.T) { } func TestResolveObjectRef_KeyNotFound(t *testing.T) { + mockCtrl := gomock.NewController(t) testScheme := newSchemeWithCoreV1(t) secret := &corev1.Secret{ @@ -207,8 +212,8 @@ func TestResolveObjectRef_KeyNotFound(t *testing.T) { reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: &mockRevisionEngine{}}, - TrackingCache: &mockTrackingCache{client: fakeClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, newNoopMockRevisionEngine(mockCtrl), nil), + TrackingCache: newMockTrackingCache(mockCtrl, fakeClient, nil), Clock: clocktesting.NewFakeClock(metav1.Now().Time), } @@ -220,6 +225,7 @@ func TestResolveObjectRef_KeyNotFound(t *testing.T) { } func TestResolveObjectRef_InvalidJSON(t *testing.T) { + mockCtrl := gomock.NewController(t) testScheme := newSchemeWithCoreV1(t) secret := &corev1.Secret{ @@ -247,8 +253,8 @@ func TestResolveObjectRef_InvalidJSON(t *testing.T) { reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, - RevisionEngineFactory: &mockRevisionEngineFactory{engine: &mockRevisionEngine{}}, - TrackingCache: &mockTrackingCache{client: fakeClient}, + RevisionEngineFactory: newMockRevisionEngineFactoryWithEngine(mockCtrl, newNoopMockRevisionEngine(mockCtrl), nil), + TrackingCache: newMockTrackingCache(mockCtrl, fakeClient, nil), Clock: clocktesting.NewFakeClock(metav1.Now().Time), } diff --git a/internal/operator-controller/controllers/suite_test.go b/internal/operator-controller/controllers/suite_test.go index 109f0b66cb..c005757513 100644 --- a/internal/operator-controller/controllers/suite_test.go +++ b/internal/operator-controller/controllers/suite_test.go @@ -17,14 +17,13 @@ limitations under the License. package controllers_test import ( - "context" - "io/fs" "log" "os" "testing" "time" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" apimachineryruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/rest" @@ -35,6 +34,7 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/controllers" "github.com/operator-framework/operator-controller/internal/operator-controller/resolve" "github.com/operator-framework/operator-controller/internal/shared/util/image" + mockcontrollers "github.com/operator-framework/operator-controller/internal/testutil/mock/controllers" "github.com/operator-framework/operator-controller/test" ) @@ -53,30 +53,20 @@ func newClient(t *testing.T) client.Client { return cl } -var _ controllers.RevisionStatesGetter = (*MockRevisionStatesGetter)(nil) - -type MockRevisionStatesGetter struct { - *controllers.RevisionStates - Err error -} - -func (m *MockRevisionStatesGetter) GetRevisionStates(ctx context.Context, ext *ocv1.ClusterExtension) (*controllers.RevisionStates, error) { - if m.Err != nil { - return nil, m.Err - } - return m.RevisionStates, nil +// newMockRevisionStatesGetter creates a gomock-based RevisionStatesGetter +// that returns fixed values, replacing the hand-written MockRevisionStatesGetter. +func newMockRevisionStatesGetter(ctrl *gomock.Controller, revisionStates *controllers.RevisionStates, err error) *mockcontrollers.MockRevisionStatesGetter { + m := mockcontrollers.NewMockRevisionStatesGetter(ctrl) + m.EXPECT().GetRevisionStates(gomock.Any(), gomock.Any()).Return(revisionStates, err).AnyTimes() + return m } -var _ controllers.Applier = (*MockApplier)(nil) - -type MockApplier struct { - installCompleted bool - installStatus string - err error -} - -func (m *MockApplier) Apply(_ context.Context, _ fs.FS, _ *ocv1.ClusterExtension, _ map[string]string, _ map[string]string) (bool, string, error) { - return m.installCompleted, m.installStatus, m.err +// newMockApplier creates a gomock-based Applier that returns fixed values, +// replacing the hand-written MockApplier. +func newMockApplier(ctrl *gomock.Controller, installCompleted bool, err error) *mockcontrollers.MockApplier { + m := mockcontrollers.NewMockApplier(ctrl) + m.EXPECT().Apply(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(installCompleted, "", err).AnyTimes() + return m } type reconcilerOption func(*deps) @@ -94,11 +84,13 @@ type deps struct { func newClientAndReconciler(t *testing.T, opts ...reconcilerOption) (client.Client, *controllers.ClusterExtensionReconciler) { cl := newClient(t) + mockCtrl := gomock.NewController(t) + defaultRevisionStatesGetter := mockcontrollers.NewMockRevisionStatesGetter(mockCtrl) + defaultRevisionStatesGetter.EXPECT().GetRevisionStates(gomock.Any(), gomock.Any()).Return(&controllers.RevisionStates{}, nil).AnyTimes() + d := &deps{ - RevisionStatesGetter: &MockRevisionStatesGetter{ - RevisionStates: &controllers.RevisionStates{}, - }, - Finalizers: crfinalizer.NewFinalizers(), + RevisionStatesGetter: defaultRevisionStatesGetter, + Finalizers: crfinalizer.NewFinalizers(), } reconciler := &controllers.ClusterExtensionReconciler{ Client: cl, diff --git a/internal/operator-controller/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go b/internal/operator-controller/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go index 82fa203c64..0f32cf7090 100644 --- a/internal/operator-controller/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go +++ b/internal/operator-controller/rukpak/preflights/crdupgradesafety/crdupgradesafety_test.go @@ -8,11 +8,10 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "helm.sh/helm/v3/pkg/release" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextensionsv1client "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" crdifyconfig "sigs.k8s.io/crdify/pkg/config" @@ -20,24 +19,14 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/applier" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/preflights/crdupgradesafety" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util" + mockcrdclient "github.com/operator-framework/operator-controller/internal/testutil/mock/crdclient" ) -type MockCRDGetter struct { - oldCrd *apiextensionsv1.CustomResourceDefinition - getErr error - apiextensionsv1client.CustomResourceDefinitionInterface -} - -func (c *MockCRDGetter) Get(ctx context.Context, name string, options metav1.GetOptions) (*apiextensionsv1.CustomResourceDefinition, error) { - return c.oldCrd, c.getErr -} - -func newMockPreflight(crd *apiextensionsv1.CustomResourceDefinition, err error) *crdupgradesafety.Preflight { +func newMockPreflight(ctrl *gomock.Controller, crd *apiextensionsv1.CustomResourceDefinition, err error) *crdupgradesafety.Preflight { + m := mockcrdclient.NewMockCustomResourceDefinitionInterface(ctrl) + m.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(crd, err).AnyTimes() var preflightOpts []crdupgradesafety.Option - return crdupgradesafety.NewPreflight(&MockCRDGetter{ - oldCrd: crd, - getErr: err, - }, preflightOpts...) + return crdupgradesafety.NewPreflight(m, preflightOpts...) } const crdFolder string = "testdata/manifests" @@ -203,7 +192,8 @@ func TestInstall(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - preflight := newMockPreflight(getCrdFromManifestFile(t, tc.oldCrdPath), tc.wantCrdGetErr) + mockCtrl := gomock.NewController(t) + preflight := newMockPreflight(mockCtrl, getCrdFromManifestFile(t, tc.oldCrdPath), tc.wantCrdGetErr) objs, err := applier.HelmReleaseToObjectsConverter{}.GetObjectsFromRelease(tc.release) if err == nil { err = preflight.Install(context.Background(), objs) @@ -408,7 +398,8 @@ func TestUpgrade(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - preflight := newMockPreflight(getCrdFromManifestFile(t, tc.oldCrdPath), tc.wantCrdGetErr) + mockCtrl := gomock.NewController(t) + preflight := newMockPreflight(mockCtrl, getCrdFromManifestFile(t, tc.oldCrdPath), tc.wantCrdGetErr) objs, err := applier.HelmReleaseToObjectsConverter{}.GetObjectsFromRelease(tc.release) if err == nil { err = preflight.Upgrade(context.Background(), objs) @@ -424,7 +415,8 @@ func TestUpgrade(t *testing.T) { func TestUpgrade_OneOfRemoved(t *testing.T) { t.Run("removing oneOf subschemas should fail", func(t *testing.T) { - preflight := newMockPreflight(getCrdFromManifestFile(t, "crd-oneof-removed-old.json"), nil) + mockCtrl := gomock.NewController(t) + preflight := newMockPreflight(mockCtrl, getCrdFromManifestFile(t, "crd-oneof-removed-old.json"), nil) rel := &release.Release{ Name: "test-release", Manifest: getManifestString(t, "crd-oneof-removed-new.json"), @@ -439,7 +431,8 @@ func TestUpgrade_OneOfRemoved(t *testing.T) { func TestUpgrade_OneOfAdded(t *testing.T) { t.Run("adding oneOf required constraints to existing property should report oneOf error", func(t *testing.T) { - preflight := newMockPreflight(getCrdFromManifestFile(t, "crd-oneof-safe-addition-old.json"), nil) + mockCtrl := gomock.NewController(t) + preflight := newMockPreflight(mockCtrl, getCrdFromManifestFile(t, "crd-oneof-safe-addition-old.json"), nil) rel := &release.Release{ Name: "test-release", Manifest: getManifestString(t, "crd-oneof-safe-addition-new.json"), @@ -456,7 +449,8 @@ func TestUpgrade_OneOfAdded(t *testing.T) { func TestUpgrade_UnhandledChanges_InSpec_DefaultPolicy(t *testing.T) { t.Run("unhandled spec changes cause error by default", func(t *testing.T) { - preflight := newMockPreflight(getCrdFromManifestFile(t, "crd-unhandled-old.json"), nil) + mockCtrl := gomock.NewController(t) + preflight := newMockPreflight(mockCtrl, getCrdFromManifestFile(t, "crd-unhandled-old.json"), nil) rel := &release.Release{ Name: "test-release", Manifest: getManifestString(t, "crd-unhandled-new.json"), @@ -474,7 +468,10 @@ func TestUpgrade_UnhandledChanges_InSpec_DefaultPolicy(t *testing.T) { func TestUpgrade_UnhandledChanges_PolicyError(t *testing.T) { t.Run("unhandled changes error when policy is Error", func(t *testing.T) { oldCrd := getCrdFromManifestFile(t, "crd-unhandled-old.json") - preflight := crdupgradesafety.NewPreflight(&MockCRDGetter{oldCrd: oldCrd}, crdupgradesafety.WithConfig(&crdifyconfig.Config{ + ctrl := gomock.NewController(t) + mockCRD := mockcrdclient.NewMockCustomResourceDefinitionInterface(ctrl) + mockCRD.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(oldCrd, nil).AnyTimes() + preflight := crdupgradesafety.NewPreflight(mockCRD, crdupgradesafety.WithConfig(&crdifyconfig.Config{ Conversion: crdifyconfig.ConversionPolicyIgnore, UnhandledEnforcement: crdifyconfig.EnforcementPolicyError, })) diff --git a/internal/operator-controller/rukpak/render/certprovider_test.go b/internal/operator-controller/rukpak/render/certprovider_test.go index a245a4173a..514702ee06 100644 --- a/internal/operator-controller/rukpak/render/certprovider_test.go +++ b/internal/operator-controller/rukpak/render/certprovider_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -12,6 +13,7 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render" . "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing" + mockrender "github.com/operator-framework/operator-controller/internal/testutil/mock/render" ) func Test_CertificateProvisioner_WithoutCertProvider(t *testing.T) { @@ -31,29 +33,35 @@ func Test_CertificateProvisioner_WithoutCertProvider(t *testing.T) { } func Test_CertificateProvisioner_WithCertProvider(t *testing.T) { - fakeProvider := &FakeCertProvider{ - InjectCABundleFn: func(obj client.Object, cfg render.CertificateProvisionerConfig) error { + ctrl := gomock.NewController(t) + mockCert := mockrender.NewMockCertificateProvider(ctrl) + mockCert.EXPECT().InjectCABundle(gomock.Any(), gomock.Any()).DoAndReturn( + func(obj client.Object, cfg render.CertificateProvisionerConfig) error { obj.SetName("some-name") return nil }, - AdditionalObjectsFn: func(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { + ) + mockCert.EXPECT().AdditionalObjects(gomock.Any()).DoAndReturn( + func(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { return []unstructured.Unstructured{*ToUnstructuredT(t, &corev1.Secret{ TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: corev1.SchemeGroupVersion.String()}, })}, nil }, - GetCertSecretInfoFn: func(cfg render.CertificateProvisionerConfig) render.CertSecretInfo { + ) + mockCert.EXPECT().GetCertSecretInfo(gomock.Any()).DoAndReturn( + func(cfg render.CertificateProvisionerConfig) render.CertSecretInfo { return render.CertSecretInfo{ SecretName: "some-secret", PrivateKeyKey: "some-key", CertificateKey: "another-key", } }, - } + ) provisioner := &render.CertificateProvisioner{ ServiceName: "webhook", CertName: "cert", Namespace: "namespace", - CertProvider: fakeProvider, + CertProvider: mockCert, } svc := &corev1.Service{} @@ -74,19 +82,23 @@ func Test_CertificateProvisioner_WithCertProvider(t *testing.T) { } func Test_CertificateProvisioner_Errors(t *testing.T) { - fakeProvider := &FakeCertProvider{ - InjectCABundleFn: func(obj client.Object, cfg render.CertificateProvisionerConfig) error { + ctrl := gomock.NewController(t) + mockCert := mockrender.NewMockCertificateProvider(ctrl) + mockCert.EXPECT().InjectCABundle(gomock.Any(), gomock.Any()).DoAndReturn( + func(obj client.Object, cfg render.CertificateProvisionerConfig) error { return fmt.Errorf("some error") }, - AdditionalObjectsFn: func(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { + ) + mockCert.EXPECT().AdditionalObjects(gomock.Any()).DoAndReturn( + func(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { return nil, fmt.Errorf("some other error") }, - } + ) provisioner := &render.CertificateProvisioner{ ServiceName: "webhook", CertName: "cert", Namespace: "namespace", - CertProvider: fakeProvider, + CertProvider: mockCert, } err := provisioner.InjectCABundle(&corev1.Service{}) @@ -100,13 +112,14 @@ func Test_CertificateProvisioner_Errors(t *testing.T) { } func Test_CertProvisionerFor(t *testing.T) { - fakeProvider := &FakeCertProvider{} + ctrl := gomock.NewController(t) + mockCert := mockrender.NewMockCertificateProvider(ctrl) prov := render.CertProvisionerFor("my.deployment.thing", render.Options{ InstallNamespace: "my-namespace", - CertificateProvider: fakeProvider, + CertificateProvider: mockCert, }) - require.Equal(t, prov.CertProvider, fakeProvider) + require.Equal(t, prov.CertProvider, mockCert) require.Equal(t, "my-deployment-thing-service", prov.ServiceName) require.Equal(t, "my-deployment-thing-service-cert", prov.CertName) require.Equal(t, "my-namespace", prov.Namespace) diff --git a/internal/operator-controller/rukpak/render/fake.go b/internal/operator-controller/rukpak/render/fake.go deleted file mode 100644 index c8213d78a8..0000000000 --- a/internal/operator-controller/rukpak/render/fake.go +++ /dev/null @@ -1,24 +0,0 @@ -package render - -import ( - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type FakeCertProvider struct { - InjectCABundleFn func(obj client.Object, cfg CertificateProvisionerConfig) error - AdditionalObjectsFn func(cfg CertificateProvisionerConfig) ([]unstructured.Unstructured, error) - GetCertSecretInfoFn func(cfg CertificateProvisionerConfig) CertSecretInfo -} - -func (f FakeCertProvider) InjectCABundle(obj client.Object, cfg CertificateProvisionerConfig) error { - return f.InjectCABundleFn(obj, cfg) -} - -func (f FakeCertProvider) AdditionalObjects(cfg CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { - return f.AdditionalObjectsFn(cfg) -} - -func (f FakeCertProvider) GetCertSecretInfo(cfg CertificateProvisionerConfig) CertSecretInfo { - return f.GetCertSecretInfoFn(cfg) -} diff --git a/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go b/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go index 3caf006c39..26c3f4b70d 100644 --- a/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go +++ b/internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -27,6 +28,7 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render/registryv1/generators" . "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing" "github.com/operator-framework/operator-controller/internal/testing/bundle/csv" + mockrender "github.com/operator-framework/operator-controller/internal/testutil/mock/render" ) func Test_ResourceGenerators(t *testing.T) { @@ -165,15 +167,13 @@ func Test_BundleCSVDeploymentGenerator_Succeeds(t *testing.T) { } func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *testing.T) { - fakeProvider := FakeCertProvider{ - GetCertSecretInfoFn: func(cfg render.CertificateProvisionerConfig) render.CertSecretInfo { - return render.CertSecretInfo{ - SecretName: "some-secret", - CertificateKey: "some-cert-key", - PrivateKeyKey: "some-private-key-key", - } - }, - } + ctrl := gomock.NewController(t) + fakeProvider := mockrender.NewMockCertificateProvider(ctrl) + fakeProvider.EXPECT().GetCertSecretInfo(gomock.Any()).Return(render.CertSecretInfo{ + SecretName: "some-secret", + CertificateKey: "some-cert-key", + PrivateKeyKey: "some-private-key-key", + }).AnyTimes() b := &bundle.RegistryV1{ CSV: csv.Builder(). @@ -1393,14 +1393,14 @@ func Test_BundleCRDGenerator_WithConversionWebhook_Fails(t *testing.T) { } func Test_BundleCRDGenerator_WithCertProvider_Succeeds(t *testing.T) { - fakeProvider := FakeCertProvider{ - InjectCABundleFn: func(obj client.Object, cfg render.CertificateProvisionerConfig) error { - obj.SetAnnotations(map[string]string{ - "cert-provider": "annotation", - }) - return nil - }, - } + ctrl := gomock.NewController(t) + fakeProvider := mockrender.NewMockCertificateProvider(ctrl) + fakeProvider.EXPECT().InjectCABundle(gomock.Any(), gomock.Any()).DoAndReturn(func(obj client.Object, _ render.CertificateProvisionerConfig) error { + obj.SetAnnotations(map[string]string{ + "cert-provider": "annotation", + }) + return nil + }).AnyTimes() opts := render.Options{ InstallNamespace: "install-namespace", @@ -1485,14 +1485,14 @@ func Test_BundleAdditionalResourcesGenerator_FailsOnNil(t *testing.T) { } func Test_BundleValidatingWebhookResourceGenerator_Succeeds(t *testing.T) { - fakeProvider := FakeCertProvider{ - InjectCABundleFn: func(obj client.Object, cfg render.CertificateProvisionerConfig) error { - obj.SetAnnotations(map[string]string{ - "cert-provider": "annotation", - }) - return nil - }, - } + ctrl := gomock.NewController(t) + fakeProvider := mockrender.NewMockCertificateProvider(ctrl) + fakeProvider.EXPECT().InjectCABundle(gomock.Any(), gomock.Any()).DoAndReturn(func(obj client.Object, _ render.CertificateProvisionerConfig) error { + obj.SetAnnotations(map[string]string{ + "cert-provider": "annotation", + }) + return nil + }).AnyTimes() for _, tc := range []struct { name string bundle *bundle.RegistryV1 @@ -1765,14 +1765,14 @@ func Test_BundleValidatingWebhookResourceGenerator_FailsOnNil(t *testing.T) { } func Test_BundleMutatingWebhookResourceGenerator_Succeeds(t *testing.T) { - fakeProvider := FakeCertProvider{ - InjectCABundleFn: func(obj client.Object, cfg render.CertificateProvisionerConfig) error { - obj.SetAnnotations(map[string]string{ - "cert-provider": "annotation", - }) - return nil - }, - } + ctrl := gomock.NewController(t) + fakeProvider := mockrender.NewMockCertificateProvider(ctrl) + fakeProvider.EXPECT().InjectCABundle(gomock.Any(), gomock.Any()).DoAndReturn(func(obj client.Object, _ render.CertificateProvisionerConfig) error { + obj.SetAnnotations(map[string]string{ + "cert-provider": "annotation", + }) + return nil + }).AnyTimes() for _, tc := range []struct { name string bundle *bundle.RegistryV1 @@ -2049,14 +2049,14 @@ func Test_BundleMutatingWebhookResourceGenerator_FailsOnNil(t *testing.T) { } func Test_BundleDeploymentServiceResourceGenerator_Succeeds(t *testing.T) { - fakeProvider := FakeCertProvider{ - InjectCABundleFn: func(obj client.Object, cfg render.CertificateProvisionerConfig) error { - obj.SetAnnotations(map[string]string{ - "cert-provider": "annotation", - }) - return nil - }, - } + ctrl := gomock.NewController(t) + fakeProvider := mockrender.NewMockCertificateProvider(ctrl) + fakeProvider.EXPECT().InjectCABundle(gomock.Any(), gomock.Any()).DoAndReturn(func(obj client.Object, _ render.CertificateProvisionerConfig) error { + obj.SetAnnotations(map[string]string{ + "cert-provider": "annotation", + }) + return nil + }).AnyTimes() for _, tc := range []struct { name string bundle *bundle.RegistryV1 @@ -2470,16 +2470,16 @@ func Test_BundleDeploymentServiceResourceGenerator_FailsOnNil(t *testing.T) { } func Test_CertProviderResourceGenerator_Succeeds(t *testing.T) { - fakeProvider := FakeCertProvider{ - AdditionalObjectsFn: func(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { - return []unstructured.Unstructured{*ToUnstructuredT(t, &corev1.Secret{ - TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: corev1.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: cfg.CertName, - }, - })}, nil - }, - } + ctrl := gomock.NewController(t) + fakeProvider := mockrender.NewMockCertificateProvider(ctrl) + fakeProvider.EXPECT().AdditionalObjects(gomock.Any()).DoAndReturn(func(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { + return []unstructured.Unstructured{*ToUnstructuredT(t, &corev1.Secret{ + TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: corev1.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: cfg.CertName, + }, + })}, nil + }).AnyTimes() objs, err := generators.CertProviderResourceGenerator(&bundle.RegistryV1{ CSV: csv.Builder(). diff --git a/internal/operator-controller/rukpak/render/render_test.go b/internal/operator-controller/rukpak/render/render_test.go index 4af451f317..fb24b7d3b1 100644 --- a/internal/operator-controller/rukpak/render/render_test.go +++ b/internal/operator-controller/rukpak/render/render_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" @@ -16,8 +17,8 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/config" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render" - . "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing" "github.com/operator-framework/operator-controller/internal/testing/bundle/csv" + mockrender "github.com/operator-framework/operator-controller/internal/testutil/mock/render" ) func Test_BundleRenderer_NoConfig(t *testing.T) { @@ -323,8 +324,9 @@ func Test_WithUniqueNameGenerator(t *testing.T) { } func Test_WithCertificateProvide(t *testing.T) { + ctrl := gomock.NewController(t) opts := &render.Options{} - expectedCertProvider := FakeCertProvider{} + expectedCertProvider := mockrender.NewMockCertificateProvider(ctrl) render.WithCertificateProvider(expectedCertProvider)(opts) require.Equal(t, expectedCertProvider, opts.CertificateProvider) } diff --git a/internal/operator-controller/rukpak/util/testing/testing.go b/internal/operator-controller/rukpak/util/testing/testing.go index 2670091a19..5d7a449775 100644 --- a/internal/operator-controller/rukpak/util/testing/testing.go +++ b/internal/operator-controller/rukpak/util/testing/testing.go @@ -8,28 +8,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle" - "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render" "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util" ) -type FakeCertProvider struct { - InjectCABundleFn func(obj client.Object, cfg render.CertificateProvisionerConfig) error - AdditionalObjectsFn func(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) - GetCertSecretInfoFn func(cfg render.CertificateProvisionerConfig) render.CertSecretInfo -} - -func (f FakeCertProvider) InjectCABundle(obj client.Object, cfg render.CertificateProvisionerConfig) error { - return f.InjectCABundleFn(obj, cfg) -} - -func (f FakeCertProvider) AdditionalObjects(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { - return f.AdditionalObjectsFn(cfg) -} - -func (f FakeCertProvider) GetCertSecretInfo(cfg render.CertificateProvisionerConfig) render.CertSecretInfo { - return f.GetCertSecretInfoFn(cfg) -} - type FakeBundleSource func() (bundle.RegistryV1, error) func (f FakeBundleSource) GetBundle() (bundle.RegistryV1, error) { diff --git a/internal/shared/util/featuregates/logging_test.go b/internal/shared/util/featuregates/logging_test.go index 1d4b163e3a..9077af4fed 100644 --- a/internal/shared/util/featuregates/logging_test.go +++ b/internal/shared/util/featuregates/logging_test.go @@ -5,40 +5,13 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "k8s.io/component-base/featuregate" "github.com/operator-framework/operator-controller/internal/shared/util/featuregates" + mocklogrsink "github.com/operator-framework/operator-controller/internal/testutil/mock/logrsink" ) -// fakeSink implements logr.LogSink, capturing Info calls for testing -type fakeSink struct { - level int - msg string - keysAndValues []interface{} -} - -// Init is part of logr.LogSink -func (f *fakeSink) Init(info logr.RuntimeInfo) {} - -// Enabled is part of logr.LogSink -func (f *fakeSink) Enabled(level int) bool { return true } - -// Info captures the log level, message, and key/value pairs -func (f *fakeSink) Info(level int, msg string, keysAndValues ...interface{}) { - f.level = level - f.msg = msg - f.keysAndValues = append([]interface{}{}, keysAndValues...) -} - -// Error is part of logr.LogSink; not used in this test -func (f *fakeSink) Error(err error, msg string, keysAndValues ...interface{}) {} - -// WithValues returns a sink with additional values; for testing, return self -func (f *fakeSink) WithValues(keysAndValues ...interface{}) logr.LogSink { return f } - -// WithName returns a sink with a new name; for testing, return self -func (f *fakeSink) WithName(name string) logr.LogSink { return f } - // TestLogFeatureGateStates verifies that LogFeatureGateStates logs features // sorted alphabetically with their enabled state func TestLogFeatureGateStates(t *testing.T) { @@ -58,15 +31,31 @@ func TestLogFeatureGateStates(t *testing.T) { "CFeature": true, })) - // prepare a fake sink and logger - sink := &fakeSink{} + // prepare a mock sink and logger, capturing Info calls + ctrl := gomock.NewController(t) + sink := mocklogrsink.NewMockLogSink(ctrl) + + var capturedMsg string + var capturedKV []interface{} + + sink.EXPECT().Init(gomock.Any()).AnyTimes() + sink.EXPECT().Enabled(gomock.Any()).Return(true).AnyTimes() + sink.EXPECT().Info(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( + func(level int, msg string, kv ...interface{}) { + capturedMsg = msg + capturedKV = append([]interface{}{}, kv...) + }).AnyTimes() + sink.EXPECT().Error(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + sink.EXPECT().WithValues(gomock.Any()).Return(sink).AnyTimes() + sink.EXPECT().WithName(gomock.Any()).Return(sink).AnyTimes() + logger := logr.New(sink) // log the feature states featuregates.LogFeatureGateStates(logger, "feature states", gate, defs) // verify the message - require.Equal(t, "feature states", sink.msg) + require.Equal(t, "feature states", capturedMsg) // Expect keys sorted: AFeature, BFeature, CFeature want := []interface{}{ @@ -74,5 +63,5 @@ func TestLogFeatureGateStates(t *testing.T) { featuregate.Feature("BFeature"), true, featuregate.Feature("CFeature"), true, } - require.Equal(t, want, sink.keysAndValues) + require.Equal(t, want, capturedKV) } diff --git a/internal/shared/util/image/mocks.go b/internal/shared/util/image/fakes.go similarity index 63% rename from internal/shared/util/image/mocks.go rename to internal/shared/util/image/fakes.go index 82e8226e26..b995ae9b53 100644 --- a/internal/shared/util/image/mocks.go +++ b/internal/shared/util/image/fakes.go @@ -10,17 +10,17 @@ import ( "go.podman.io/image/v5/docker/reference" ) -var _ Puller = (*MockPuller)(nil) +var _ Puller = (*FakePuller)(nil) -// MockPuller is a utility for mocking out a Puller interface -type MockPuller struct { +// FakePuller is a test fake that returns preconfigured values for the Puller interface +type FakePuller struct { ImageFS fs.FS Ref reference.Canonical ModTime time.Time Error error } -func (ms *MockPuller) Pull(_ context.Context, _, _ string, _ Cache) (fs.FS, reference.Canonical, time.Time, error) { +func (ms *FakePuller) Pull(_ context.Context, _, _ string, _ Cache) (fs.FS, reference.Canonical, time.Time, error) { if ms.Error != nil { return nil, nil, time.Time{}, ms.Error } @@ -28,9 +28,9 @@ func (ms *MockPuller) Pull(_ context.Context, _, _ string, _ Cache) (fs.FS, refe return ms.ImageFS, ms.Ref, ms.ModTime, nil } -var _ Cache = (*MockCache)(nil) +var _ Cache = (*FakeCache)(nil) -type MockCache struct { +type FakeCache struct { FetchFS fs.FS FetchModTime time.Time FetchError error @@ -44,18 +44,18 @@ type MockCache struct { GarbageCollectError error } -func (m MockCache) Fetch(_ context.Context, _ string, _ reference.Canonical) (fs.FS, time.Time, error) { +func (m FakeCache) Fetch(_ context.Context, _ string, _ reference.Canonical) (fs.FS, time.Time, error) { return m.FetchFS, m.FetchModTime, m.FetchError } -func (m MockCache) Store(_ context.Context, _ string, _ reference.Named, _ reference.Canonical, _ ocispecv1.Image, _ iter.Seq[LayerData]) (fs.FS, time.Time, error) { +func (m FakeCache) Store(_ context.Context, _ string, _ reference.Named, _ reference.Canonical, _ ocispecv1.Image, _ iter.Seq[LayerData]) (fs.FS, time.Time, error) { return m.StoreFS, m.StoreModTime, m.StoreError } -func (m MockCache) Delete(_ context.Context, _ string) error { +func (m FakeCache) Delete(_ context.Context, _ string) error { return m.DeleteErr } -func (m MockCache) GarbageCollect(_ context.Context, _ string, _ reference.Canonical) error { +func (m FakeCache) GarbageCollect(_ context.Context, _ string, _ reference.Canonical) error { return m.GarbageCollectError } diff --git a/internal/shared/util/image/pull_test.go b/internal/shared/util/image/pull_test.go index 2b11718a9b..ac59ae509b 100644 --- a/internal/shared/util/image/pull_test.go +++ b/internal/shared/util/image/pull_test.go @@ -122,7 +122,7 @@ func TestContainersImagePuller_Pull(t *testing.T) { name: "return error if cache fetch fails", ownerID: myOwner, srcRef: myCanonicalRef.String(), - cache: MockCache{FetchError: errors.New("fetch error")}, + cache: FakeCache{FetchError: errors.New("fetch error")}, contextFunc: defaultContextFunc, expect: func(t *testing.T, fsys fs.FS, canonical reference.Canonical, modTime time.Time, err error) { require.ErrorContains(t, err, "fetch error") @@ -132,7 +132,7 @@ func TestContainersImagePuller_Pull(t *testing.T) { name: "return canonical ref's data from cache, if present", ownerID: myOwner, srcRef: myCanonicalRef.String(), - cache: MockCache{ + cache: FakeCache{ FetchFS: fstest.MapFS{ testFileName: &fstest.MapFile{Data: []byte(testFileContents)}, }, @@ -153,7 +153,7 @@ func TestContainersImagePuller_Pull(t *testing.T) { name: "return tag ref's data from cache, if present", ownerID: myOwner, srcRef: myTagRef.String(), - cache: MockCache{ + cache: FakeCache{ FetchFS: fstest.MapFS{ testFileName: &fstest.MapFile{Data: []byte(testFileContents)}, }, @@ -174,7 +174,7 @@ func TestContainersImagePuller_Pull(t *testing.T) { name: "returns error if failure storing content in cache", ownerID: myOwner, srcRef: myCanonicalRef.String(), - cache: MockCache{ + cache: FakeCache{ StoreError: errors.New("store error"), }, contextFunc: buildSourceContextFunc(t, myCanonicalRef), @@ -186,7 +186,7 @@ func TestContainersImagePuller_Pull(t *testing.T) { name: "returns stored data upon pull success", ownerID: myOwner, srcRef: myTagRef.String(), - cache: MockCache{ + cache: FakeCache{ StoreFS: fstest.MapFS{ testFileName: &fstest.MapFile{Data: []byte(testFileContents)}, }, @@ -208,7 +208,7 @@ func TestContainersImagePuller_Pull(t *testing.T) { name: "returns error if cache garbage collection fails", ownerID: myOwner, srcRef: myTagRef.String(), - cache: MockCache{ + cache: FakeCache{ StoreFS: fstest.MapFS{ testFileName: &fstest.MapFile{Data: []byte(testFileContents)}, }, diff --git a/internal/testutil/mock/applier/mock_applier.go b/internal/testutil/mock/applier/mock_applier.go new file mode 100644 index 0000000000..5ff9b4e08c --- /dev/null +++ b/internal/testutil/mock/applier/mock_applier.go @@ -0,0 +1,246 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/applier (interfaces: Preflight,HelmReleaseToObjectsConverterInterface,HelmChartProvider,ClusterObjectSetGenerator,ManifestProvider) +// +// Generated by this command: +// +// mockgen -destination=applier/mock_applier.go -package=applier github.com/operator-framework/operator-controller/internal/operator-controller/applier Preflight,HelmReleaseToObjectsConverterInterface,HelmChartProvider,ClusterObjectSetGenerator,ManifestProvider +// + +// Package applier is a generated GoMock package. +package applier + +import ( + context "context" + fs "io/fs" + reflect "reflect" + + v1 "github.com/operator-framework/operator-controller/api/v1" + v10 "github.com/operator-framework/operator-controller/applyconfigurations/api/v1" + gomock "go.uber.org/mock/gomock" + chart "helm.sh/helm/v3/pkg/chart" + release "helm.sh/helm/v3/pkg/release" + client "sigs.k8s.io/controller-runtime/pkg/client" +) + +// MockPreflight is a mock of Preflight interface. +type MockPreflight struct { + ctrl *gomock.Controller + recorder *MockPreflightMockRecorder + isgomock struct{} +} + +// MockPreflightMockRecorder is the mock recorder for MockPreflight. +type MockPreflightMockRecorder struct { + mock *MockPreflight +} + +// NewMockPreflight creates a new mock instance. +func NewMockPreflight(ctrl *gomock.Controller) *MockPreflight { + mock := &MockPreflight{ctrl: ctrl} + mock.recorder = &MockPreflightMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPreflight) EXPECT() *MockPreflightMockRecorder { + return m.recorder +} + +// Install mocks base method. +func (m *MockPreflight) Install(arg0 context.Context, arg1 []client.Object) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Install", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Install indicates an expected call of Install. +func (mr *MockPreflightMockRecorder) Install(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Install", reflect.TypeOf((*MockPreflight)(nil).Install), arg0, arg1) +} + +// Upgrade mocks base method. +func (m *MockPreflight) Upgrade(arg0 context.Context, arg1 []client.Object) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upgrade", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Upgrade indicates an expected call of Upgrade. +func (mr *MockPreflightMockRecorder) Upgrade(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upgrade", reflect.TypeOf((*MockPreflight)(nil).Upgrade), arg0, arg1) +} + +// MockHelmReleaseToObjectsConverterInterface is a mock of HelmReleaseToObjectsConverterInterface interface. +type MockHelmReleaseToObjectsConverterInterface struct { + ctrl *gomock.Controller + recorder *MockHelmReleaseToObjectsConverterInterfaceMockRecorder + isgomock struct{} +} + +// MockHelmReleaseToObjectsConverterInterfaceMockRecorder is the mock recorder for MockHelmReleaseToObjectsConverterInterface. +type MockHelmReleaseToObjectsConverterInterfaceMockRecorder struct { + mock *MockHelmReleaseToObjectsConverterInterface +} + +// NewMockHelmReleaseToObjectsConverterInterface creates a new mock instance. +func NewMockHelmReleaseToObjectsConverterInterface(ctrl *gomock.Controller) *MockHelmReleaseToObjectsConverterInterface { + mock := &MockHelmReleaseToObjectsConverterInterface{ctrl: ctrl} + mock.recorder = &MockHelmReleaseToObjectsConverterInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHelmReleaseToObjectsConverterInterface) EXPECT() *MockHelmReleaseToObjectsConverterInterfaceMockRecorder { + return m.recorder +} + +// GetObjectsFromRelease mocks base method. +func (m *MockHelmReleaseToObjectsConverterInterface) GetObjectsFromRelease(rel *release.Release) ([]client.Object, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetObjectsFromRelease", rel) + ret0, _ := ret[0].([]client.Object) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetObjectsFromRelease indicates an expected call of GetObjectsFromRelease. +func (mr *MockHelmReleaseToObjectsConverterInterfaceMockRecorder) GetObjectsFromRelease(rel any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectsFromRelease", reflect.TypeOf((*MockHelmReleaseToObjectsConverterInterface)(nil).GetObjectsFromRelease), rel) +} + +// MockHelmChartProvider is a mock of HelmChartProvider interface. +type MockHelmChartProvider struct { + ctrl *gomock.Controller + recorder *MockHelmChartProviderMockRecorder + isgomock struct{} +} + +// MockHelmChartProviderMockRecorder is the mock recorder for MockHelmChartProvider. +type MockHelmChartProviderMockRecorder struct { + mock *MockHelmChartProvider +} + +// NewMockHelmChartProvider creates a new mock instance. +func NewMockHelmChartProvider(ctrl *gomock.Controller) *MockHelmChartProvider { + mock := &MockHelmChartProvider{ctrl: ctrl} + mock.recorder = &MockHelmChartProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockHelmChartProvider) EXPECT() *MockHelmChartProviderMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockHelmChartProvider) Get(bundle fs.FS, clusterExtension *v1.ClusterExtension) (*chart.Chart, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", bundle, clusterExtension) + ret0, _ := ret[0].(*chart.Chart) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockHelmChartProviderMockRecorder) Get(bundle, clusterExtension any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockHelmChartProvider)(nil).Get), bundle, clusterExtension) +} + +// MockClusterObjectSetGenerator is a mock of ClusterObjectSetGenerator interface. +type MockClusterObjectSetGenerator struct { + ctrl *gomock.Controller + recorder *MockClusterObjectSetGeneratorMockRecorder + isgomock struct{} +} + +// MockClusterObjectSetGeneratorMockRecorder is the mock recorder for MockClusterObjectSetGenerator. +type MockClusterObjectSetGeneratorMockRecorder struct { + mock *MockClusterObjectSetGenerator +} + +// NewMockClusterObjectSetGenerator creates a new mock instance. +func NewMockClusterObjectSetGenerator(ctrl *gomock.Controller) *MockClusterObjectSetGenerator { + mock := &MockClusterObjectSetGenerator{ctrl: ctrl} + mock.recorder = &MockClusterObjectSetGeneratorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClusterObjectSetGenerator) EXPECT() *MockClusterObjectSetGeneratorMockRecorder { + return m.recorder +} + +// GenerateRevision mocks base method. +func (m *MockClusterObjectSetGenerator) GenerateRevision(ctx context.Context, bundleFS fs.FS, ext *v1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*v10.ClusterObjectSetApplyConfiguration, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GenerateRevision", ctx, bundleFS, ext, objectLabels, revisionAnnotations) + ret0, _ := ret[0].(*v10.ClusterObjectSetApplyConfiguration) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GenerateRevision indicates an expected call of GenerateRevision. +func (mr *MockClusterObjectSetGeneratorMockRecorder) GenerateRevision(ctx, bundleFS, ext, objectLabels, revisionAnnotations any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateRevision", reflect.TypeOf((*MockClusterObjectSetGenerator)(nil).GenerateRevision), ctx, bundleFS, ext, objectLabels, revisionAnnotations) +} + +// GenerateRevisionFromHelmRelease mocks base method. +func (m *MockClusterObjectSetGenerator) GenerateRevisionFromHelmRelease(ctx context.Context, helmRelease *release.Release, ext *v1.ClusterExtension, objectLabels map[string]string) (*v10.ClusterObjectSetApplyConfiguration, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GenerateRevisionFromHelmRelease", ctx, helmRelease, ext, objectLabels) + ret0, _ := ret[0].(*v10.ClusterObjectSetApplyConfiguration) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GenerateRevisionFromHelmRelease indicates an expected call of GenerateRevisionFromHelmRelease. +func (mr *MockClusterObjectSetGeneratorMockRecorder) GenerateRevisionFromHelmRelease(ctx, helmRelease, ext, objectLabels any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateRevisionFromHelmRelease", reflect.TypeOf((*MockClusterObjectSetGenerator)(nil).GenerateRevisionFromHelmRelease), ctx, helmRelease, ext, objectLabels) +} + +// MockManifestProvider is a mock of ManifestProvider interface. +type MockManifestProvider struct { + ctrl *gomock.Controller + recorder *MockManifestProviderMockRecorder + isgomock struct{} +} + +// MockManifestProviderMockRecorder is the mock recorder for MockManifestProvider. +type MockManifestProviderMockRecorder struct { + mock *MockManifestProvider +} + +// NewMockManifestProvider creates a new mock instance. +func NewMockManifestProvider(ctrl *gomock.Controller) *MockManifestProvider { + mock := &MockManifestProvider{ctrl: ctrl} + mock.recorder = &MockManifestProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockManifestProvider) EXPECT() *MockManifestProviderMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockManifestProvider) Get(bundle fs.FS, ext *v1.ClusterExtension) ([]client.Object, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", bundle, ext) + ret0, _ := ret[0].([]client.Object) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockManifestProviderMockRecorder) Get(bundle, ext any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockManifestProvider)(nil).Get), bundle, ext) +} diff --git a/internal/testutil/mock/authorization/mock_authorization.go b/internal/testutil/mock/authorization/mock_authorization.go new file mode 100644 index 0000000000..e46671ae39 --- /dev/null +++ b/internal/testutil/mock/authorization/mock_authorization.go @@ -0,0 +1,64 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/authorization (interfaces: PreAuthorizer) +// +// Generated by this command: +// +// mockgen -destination=authorization/mock_authorization.go -package=authorization github.com/operator-framework/operator-controller/internal/operator-controller/authorization PreAuthorizer +// + +// Package authorization is a generated GoMock package. +package authorization + +import ( + context "context" + io "io" + reflect "reflect" + + authorization "github.com/operator-framework/operator-controller/internal/operator-controller/authorization" + gomock "go.uber.org/mock/gomock" + user "k8s.io/apiserver/pkg/authentication/user" +) + +// MockPreAuthorizer is a mock of PreAuthorizer interface. +type MockPreAuthorizer struct { + ctrl *gomock.Controller + recorder *MockPreAuthorizerMockRecorder + isgomock struct{} +} + +// MockPreAuthorizerMockRecorder is the mock recorder for MockPreAuthorizer. +type MockPreAuthorizerMockRecorder struct { + mock *MockPreAuthorizer +} + +// NewMockPreAuthorizer creates a new mock instance. +func NewMockPreAuthorizer(ctrl *gomock.Controller) *MockPreAuthorizer { + mock := &MockPreAuthorizer{ctrl: ctrl} + mock.recorder = &MockPreAuthorizerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPreAuthorizer) EXPECT() *MockPreAuthorizerMockRecorder { + return m.recorder +} + +// PreAuthorize mocks base method. +func (m *MockPreAuthorizer) PreAuthorize(ctx context.Context, arg1 user.Info, manifestReader io.Reader, additionalRequiredPerms ...authorization.UserAuthorizerAttributesFactory) ([]authorization.ScopedPolicyRules, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, arg1, manifestReader} + for _, a := range additionalRequiredPerms { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "PreAuthorize", varargs...) + ret0, _ := ret[0].([]authorization.ScopedPolicyRules) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PreAuthorize indicates an expected call of PreAuthorize. +func (mr *MockPreAuthorizerMockRecorder) PreAuthorize(ctx, arg1, manifestReader any, additionalRequiredPerms ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, arg1, manifestReader}, additionalRequiredPerms...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PreAuthorize", reflect.TypeOf((*MockPreAuthorizer)(nil).PreAuthorize), varargs...) +} diff --git a/internal/testutil/mock/catalogclient/mock_cache.go b/internal/testutil/mock/catalogclient/mock_cache.go new file mode 100644 index 0000000000..5b7805158a --- /dev/null +++ b/internal/testutil/mock/catalogclient/mock_cache.go @@ -0,0 +1,72 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/catalogmetadata/client (interfaces: Cache) +// +// Generated by this command: +// +// mockgen -destination=catalogclient/mock_cache.go -package=catalogclient github.com/operator-framework/operator-controller/internal/operator-controller/catalogmetadata/client Cache +// + +// Package catalogclient is a generated GoMock package. +package catalogclient + +import ( + io "io" + fs "io/fs" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockCache is a mock of Cache interface. +type MockCache struct { + ctrl *gomock.Controller + recorder *MockCacheMockRecorder + isgomock struct{} +} + +// MockCacheMockRecorder is the mock recorder for MockCache. +type MockCacheMockRecorder struct { + mock *MockCache +} + +// NewMockCache creates a new mock instance. +func NewMockCache(ctrl *gomock.Controller) *MockCache { + mock := &MockCache{ctrl: ctrl} + mock.recorder = &MockCacheMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCache) EXPECT() *MockCacheMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockCache) Get(catalogName, resolvedRef string) (fs.FS, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", catalogName, resolvedRef) + ret0, _ := ret[0].(fs.FS) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockCacheMockRecorder) Get(catalogName, resolvedRef any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCache)(nil).Get), catalogName, resolvedRef) +} + +// Put mocks base method. +func (m *MockCache) Put(catalogName, resolvedRef string, source io.Reader, errToCache error) (fs.FS, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Put", catalogName, resolvedRef, source, errToCache) + ret0, _ := ret[0].(fs.FS) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Put indicates an expected call of Put. +func (mr *MockCacheMockRecorder) Put(catalogName, resolvedRef, source, errToCache any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockCache)(nil).Put), catalogName, resolvedRef, source, errToCache) +} diff --git a/internal/testutil/mock/catalogdserver/mock_catalogstore.go b/internal/testutil/mock/catalogdserver/mock_catalogstore.go new file mode 100644 index 0000000000..38318f6cee --- /dev/null +++ b/internal/testutil/mock/catalogdserver/mock_catalogstore.go @@ -0,0 +1,89 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/catalogd/server (interfaces: CatalogStore) +// +// Generated by this command: +// +// mockgen -destination=catalogdserver/mock_catalogstore.go -package=catalogdserver github.com/operator-framework/operator-controller/internal/catalogd/server CatalogStore +// + +// Package catalogdserver is a generated GoMock package. +package catalogdserver + +import ( + fs "io/fs" + os "os" + reflect "reflect" + + server "github.com/operator-framework/operator-controller/internal/catalogd/server" + gomock "go.uber.org/mock/gomock" +) + +// MockCatalogStore is a mock of CatalogStore interface. +type MockCatalogStore struct { + ctrl *gomock.Controller + recorder *MockCatalogStoreMockRecorder + isgomock struct{} +} + +// MockCatalogStoreMockRecorder is the mock recorder for MockCatalogStore. +type MockCatalogStoreMockRecorder struct { + mock *MockCatalogStore +} + +// NewMockCatalogStore creates a new mock instance. +func NewMockCatalogStore(ctrl *gomock.Controller) *MockCatalogStore { + mock := &MockCatalogStore{ctrl: ctrl} + mock.recorder = &MockCatalogStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCatalogStore) EXPECT() *MockCatalogStoreMockRecorder { + return m.recorder +} + +// GetCatalogData mocks base method. +func (m *MockCatalogStore) GetCatalogData(catalog string) (*os.File, os.FileInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCatalogData", catalog) + ret0, _ := ret[0].(*os.File) + ret1, _ := ret[1].(os.FileInfo) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetCatalogData indicates an expected call of GetCatalogData. +func (mr *MockCatalogStoreMockRecorder) GetCatalogData(catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCatalogData", reflect.TypeOf((*MockCatalogStore)(nil).GetCatalogData), catalog) +} + +// GetCatalogFS mocks base method. +func (m *MockCatalogStore) GetCatalogFS(catalog string) (fs.FS, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCatalogFS", catalog) + ret0, _ := ret[0].(fs.FS) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCatalogFS indicates an expected call of GetCatalogFS. +func (mr *MockCatalogStoreMockRecorder) GetCatalogFS(catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCatalogFS", reflect.TypeOf((*MockCatalogStore)(nil).GetCatalogFS), catalog) +} + +// GetIndex mocks base method. +func (m *MockCatalogStore) GetIndex(catalog string) (server.Index, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIndex", catalog) + ret0, _ := ret[0].(server.Index) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetIndex indicates an expected call of GetIndex. +func (mr *MockCatalogStoreMockRecorder) GetIndex(catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIndex", reflect.TypeOf((*MockCatalogStore)(nil).GetIndex), catalog) +} diff --git a/internal/testutil/mock/catalogdservice/mock_graphqlservice.go b/internal/testutil/mock/catalogdservice/mock_graphqlservice.go new file mode 100644 index 0000000000..ee40372a59 --- /dev/null +++ b/internal/testutil/mock/catalogdservice/mock_graphqlservice.go @@ -0,0 +1,85 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/catalogd/service (interfaces: GraphQLService) +// +// Generated by this command: +// +// mockgen -destination=catalogdservice/mock_graphqlservice.go -package=catalogdservice github.com/operator-framework/operator-controller/internal/catalogd/service GraphQLService +// + +// Package catalogdservice is a generated GoMock package. +package catalogdservice + +import ( + fs "io/fs" + reflect "reflect" + + graphql "github.com/graphql-go/graphql" + graphql0 "github.com/operator-framework/operator-controller/internal/catalogd/graphql" + gomock "go.uber.org/mock/gomock" +) + +// MockGraphQLService is a mock of GraphQLService interface. +type MockGraphQLService struct { + ctrl *gomock.Controller + recorder *MockGraphQLServiceMockRecorder + isgomock struct{} +} + +// MockGraphQLServiceMockRecorder is the mock recorder for MockGraphQLService. +type MockGraphQLServiceMockRecorder struct { + mock *MockGraphQLService +} + +// NewMockGraphQLService creates a new mock instance. +func NewMockGraphQLService(ctrl *gomock.Controller) *MockGraphQLService { + mock := &MockGraphQLService{ctrl: ctrl} + mock.recorder = &MockGraphQLServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGraphQLService) EXPECT() *MockGraphQLServiceMockRecorder { + return m.recorder +} + +// ExecuteQuery mocks base method. +func (m *MockGraphQLService) ExecuteQuery(catalog string, catalogFS fs.FS, query string) (*graphql.Result, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteQuery", catalog, catalogFS, query) + ret0, _ := ret[0].(*graphql.Result) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteQuery indicates an expected call of ExecuteQuery. +func (mr *MockGraphQLServiceMockRecorder) ExecuteQuery(catalog, catalogFS, query any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteQuery", reflect.TypeOf((*MockGraphQLService)(nil).ExecuteQuery), catalog, catalogFS, query) +} + +// GetSchema mocks base method. +func (m *MockGraphQLService) GetSchema(catalog string, catalogFS fs.FS) (*graphql0.DynamicSchema, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSchema", catalog, catalogFS) + ret0, _ := ret[0].(*graphql0.DynamicSchema) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSchema indicates an expected call of GetSchema. +func (mr *MockGraphQLServiceMockRecorder) GetSchema(catalog, catalogFS any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSchema", reflect.TypeOf((*MockGraphQLService)(nil).GetSchema), catalog, catalogFS) +} + +// InvalidateCache mocks base method. +func (m *MockGraphQLService) InvalidateCache(catalog string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "InvalidateCache", catalog) +} + +// InvalidateCache indicates an expected call of InvalidateCache. +func (mr *MockGraphQLServiceMockRecorder) InvalidateCache(catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InvalidateCache", reflect.TypeOf((*MockGraphQLService)(nil).InvalidateCache), catalog) +} diff --git a/internal/testutil/mock/cmcache/mock_cache.go b/internal/testutil/mock/cmcache/mock_cache.go new file mode 100644 index 0000000000..78315a846d --- /dev/null +++ b/internal/testutil/mock/cmcache/mock_cache.go @@ -0,0 +1,183 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache (interfaces: Cache,Watcher,CloserSyncingSource) +// +// Generated by this command: +// +// mockgen -destination=cmcache/mock_cache.go -package=cmcache github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache Cache,Watcher,CloserSyncingSource +// + +// Package cmcache is a generated GoMock package. +package cmcache + +import ( + context "context" + reflect "reflect" + + cache "github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache" + gomock "go.uber.org/mock/gomock" + workqueue "k8s.io/client-go/util/workqueue" + client "sigs.k8s.io/controller-runtime/pkg/client" + reconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" + source "sigs.k8s.io/controller-runtime/pkg/source" +) + +// MockCache is a mock of Cache interface. +type MockCache struct { + ctrl *gomock.Controller + recorder *MockCacheMockRecorder + isgomock struct{} +} + +// MockCacheMockRecorder is the mock recorder for MockCache. +type MockCacheMockRecorder struct { + mock *MockCache +} + +// NewMockCache creates a new mock instance. +func NewMockCache(ctrl *gomock.Controller) *MockCache { + mock := &MockCache{ctrl: ctrl} + mock.recorder = &MockCacheMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCache) EXPECT() *MockCacheMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockCache) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockCacheMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCache)(nil).Close)) +} + +// Watch mocks base method. +func (m *MockCache) Watch(arg0 context.Context, arg1 cache.Watcher, arg2 ...client.Object) error { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Watch", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Watch indicates an expected call of Watch. +func (mr *MockCacheMockRecorder) Watch(arg0, arg1 any, arg2 ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockCache)(nil).Watch), varargs...) +} + +// MockWatcher is a mock of Watcher interface. +type MockWatcher struct { + ctrl *gomock.Controller + recorder *MockWatcherMockRecorder + isgomock struct{} +} + +// MockWatcherMockRecorder is the mock recorder for MockWatcher. +type MockWatcherMockRecorder struct { + mock *MockWatcher +} + +// NewMockWatcher creates a new mock instance. +func NewMockWatcher(ctrl *gomock.Controller) *MockWatcher { + mock := &MockWatcher{ctrl: ctrl} + mock.recorder = &MockWatcherMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWatcher) EXPECT() *MockWatcherMockRecorder { + return m.recorder +} + +// Watch mocks base method. +func (m *MockWatcher) Watch(arg0 source.Source) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Watch", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Watch indicates an expected call of Watch. +func (mr *MockWatcherMockRecorder) Watch(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockWatcher)(nil).Watch), arg0) +} + +// MockCloserSyncingSource is a mock of CloserSyncingSource interface. +type MockCloserSyncingSource struct { + ctrl *gomock.Controller + recorder *MockCloserSyncingSourceMockRecorder + isgomock struct{} +} + +// MockCloserSyncingSourceMockRecorder is the mock recorder for MockCloserSyncingSource. +type MockCloserSyncingSourceMockRecorder struct { + mock *MockCloserSyncingSource +} + +// NewMockCloserSyncingSource creates a new mock instance. +func NewMockCloserSyncingSource(ctrl *gomock.Controller) *MockCloserSyncingSource { + mock := &MockCloserSyncingSource{ctrl: ctrl} + mock.recorder = &MockCloserSyncingSourceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCloserSyncingSource) EXPECT() *MockCloserSyncingSourceMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockCloserSyncingSource) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockCloserSyncingSourceMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCloserSyncingSource)(nil).Close)) +} + +// Start mocks base method. +func (m *MockCloserSyncingSource) Start(arg0 context.Context, arg1 workqueue.TypedRateLimitingInterface[reconcile.Request]) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockCloserSyncingSourceMockRecorder) Start(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockCloserSyncingSource)(nil).Start), arg0, arg1) +} + +// WaitForSync mocks base method. +func (m *MockCloserSyncingSource) WaitForSync(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitForSync", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitForSync indicates an expected call of WaitForSync. +func (mr *MockCloserSyncingSourceMockRecorder) WaitForSync(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForSync", reflect.TypeOf((*MockCloserSyncingSource)(nil).WaitForSync), ctx) +} diff --git a/internal/testutil/mock/config/mock_schemaprovider.go b/internal/testutil/mock/config/mock_schemaprovider.go new file mode 100644 index 0000000000..5d5fef774f --- /dev/null +++ b/internal/testutil/mock/config/mock_schemaprovider.go @@ -0,0 +1,55 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/config (interfaces: SchemaProvider) +// +// Generated by this command: +// +// mockgen -destination=config/mock_schemaprovider.go -package=config github.com/operator-framework/operator-controller/internal/operator-controller/config SchemaProvider +// + +// Package config is a generated GoMock package. +package config + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockSchemaProvider is a mock of SchemaProvider interface. +type MockSchemaProvider struct { + ctrl *gomock.Controller + recorder *MockSchemaProviderMockRecorder + isgomock struct{} +} + +// MockSchemaProviderMockRecorder is the mock recorder for MockSchemaProvider. +type MockSchemaProviderMockRecorder struct { + mock *MockSchemaProvider +} + +// NewMockSchemaProvider creates a new mock instance. +func NewMockSchemaProvider(ctrl *gomock.Controller) *MockSchemaProvider { + mock := &MockSchemaProvider{ctrl: ctrl} + mock.recorder = &MockSchemaProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSchemaProvider) EXPECT() *MockSchemaProviderMockRecorder { + return m.recorder +} + +// GetConfigSchema mocks base method. +func (m *MockSchemaProvider) GetConfigSchema() (map[string]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetConfigSchema") + ret0, _ := ret[0].(map[string]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetConfigSchema indicates an expected call of GetConfigSchema. +func (mr *MockSchemaProviderMockRecorder) GetConfigSchema() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfigSchema", reflect.TypeOf((*MockSchemaProvider)(nil).GetConfigSchema)) +} diff --git a/internal/testutil/mock/contentmanager/mock_contentmanager.go b/internal/testutil/mock/contentmanager/mock_contentmanager.go new file mode 100644 index 0000000000..677919a12c --- /dev/null +++ b/internal/testutil/mock/contentmanager/mock_contentmanager.go @@ -0,0 +1,72 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager (interfaces: Manager) +// +// Generated by this command: +// +// mockgen -destination=contentmanager/mock_contentmanager.go -package=contentmanager github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager Manager +// + +// Package contentmanager is a generated GoMock package. +package contentmanager + +import ( + context "context" + reflect "reflect" + + v1 "github.com/operator-framework/operator-controller/api/v1" + cache "github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache" + gomock "go.uber.org/mock/gomock" +) + +// MockManager is a mock of Manager interface. +type MockManager struct { + ctrl *gomock.Controller + recorder *MockManagerMockRecorder + isgomock struct{} +} + +// MockManagerMockRecorder is the mock recorder for MockManager. +type MockManagerMockRecorder struct { + mock *MockManager +} + +// NewMockManager creates a new mock instance. +func NewMockManager(ctrl *gomock.Controller) *MockManager { + mock := &MockManager{ctrl: ctrl} + mock.recorder = &MockManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockManager) EXPECT() *MockManagerMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockManager) Delete(arg0 *v1.ClusterExtension) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockManagerMockRecorder) Delete(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockManager)(nil).Delete), arg0) +} + +// Get mocks base method. +func (m *MockManager) Get(arg0 context.Context, arg1 *v1.ClusterExtension) (cache.Cache, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].(cache.Cache) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockManagerMockRecorder) Get(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockManager)(nil).Get), arg0, arg1) +} diff --git a/internal/testutil/mock/controllers/mock_controllers.go b/internal/testutil/mock/controllers/mock_controllers.go new file mode 100644 index 0000000000..4e75dd5aa0 --- /dev/null +++ b/internal/testutil/mock/controllers/mock_controllers.go @@ -0,0 +1,296 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/controllers (interfaces: CatalogCache,CatalogCachePopulator,RevisionStatesGetter,Applier,RevisionEngine,RevisionEngineFactory) +// +// Generated by this command: +// +// mockgen -destination=controllers/mock_controllers.go -package=controllers github.com/operator-framework/operator-controller/internal/operator-controller/controllers CatalogCache,CatalogCachePopulator,RevisionStatesGetter,Applier,RevisionEngine,RevisionEngineFactory +// + +// Package controllers is a generated GoMock package. +package controllers + +import ( + context "context" + fs "io/fs" + reflect "reflect" + + v1 "github.com/operator-framework/operator-controller/api/v1" + controllers "github.com/operator-framework/operator-controller/internal/operator-controller/controllers" + gomock "go.uber.org/mock/gomock" + machinery "pkg.package-operator.run/boxcutter/machinery" + types "pkg.package-operator.run/boxcutter/machinery/types" +) + +// MockCatalogCache is a mock of CatalogCache interface. +type MockCatalogCache struct { + ctrl *gomock.Controller + recorder *MockCatalogCacheMockRecorder + isgomock struct{} +} + +// MockCatalogCacheMockRecorder is the mock recorder for MockCatalogCache. +type MockCatalogCacheMockRecorder struct { + mock *MockCatalogCache +} + +// NewMockCatalogCache creates a new mock instance. +func NewMockCatalogCache(ctrl *gomock.Controller) *MockCatalogCache { + mock := &MockCatalogCache{ctrl: ctrl} + mock.recorder = &MockCatalogCacheMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCatalogCache) EXPECT() *MockCatalogCacheMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockCatalogCache) Get(catalogName, resolvedRef string) (fs.FS, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", catalogName, resolvedRef) + ret0, _ := ret[0].(fs.FS) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockCatalogCacheMockRecorder) Get(catalogName, resolvedRef any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCatalogCache)(nil).Get), catalogName, resolvedRef) +} + +// Remove mocks base method. +func (m *MockCatalogCache) Remove(catalogName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", catalogName) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockCatalogCacheMockRecorder) Remove(catalogName any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCatalogCache)(nil).Remove), catalogName) +} + +// MockCatalogCachePopulator is a mock of CatalogCachePopulator interface. +type MockCatalogCachePopulator struct { + ctrl *gomock.Controller + recorder *MockCatalogCachePopulatorMockRecorder + isgomock struct{} +} + +// MockCatalogCachePopulatorMockRecorder is the mock recorder for MockCatalogCachePopulator. +type MockCatalogCachePopulatorMockRecorder struct { + mock *MockCatalogCachePopulator +} + +// NewMockCatalogCachePopulator creates a new mock instance. +func NewMockCatalogCachePopulator(ctrl *gomock.Controller) *MockCatalogCachePopulator { + mock := &MockCatalogCachePopulator{ctrl: ctrl} + mock.recorder = &MockCatalogCachePopulatorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCatalogCachePopulator) EXPECT() *MockCatalogCachePopulatorMockRecorder { + return m.recorder +} + +// PopulateCache mocks base method. +func (m *MockCatalogCachePopulator) PopulateCache(ctx context.Context, catalog *v1.ClusterCatalog) (fs.FS, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PopulateCache", ctx, catalog) + ret0, _ := ret[0].(fs.FS) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PopulateCache indicates an expected call of PopulateCache. +func (mr *MockCatalogCachePopulatorMockRecorder) PopulateCache(ctx, catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PopulateCache", reflect.TypeOf((*MockCatalogCachePopulator)(nil).PopulateCache), ctx, catalog) +} + +// MockRevisionStatesGetter is a mock of RevisionStatesGetter interface. +type MockRevisionStatesGetter struct { + ctrl *gomock.Controller + recorder *MockRevisionStatesGetterMockRecorder + isgomock struct{} +} + +// MockRevisionStatesGetterMockRecorder is the mock recorder for MockRevisionStatesGetter. +type MockRevisionStatesGetterMockRecorder struct { + mock *MockRevisionStatesGetter +} + +// NewMockRevisionStatesGetter creates a new mock instance. +func NewMockRevisionStatesGetter(ctrl *gomock.Controller) *MockRevisionStatesGetter { + mock := &MockRevisionStatesGetter{ctrl: ctrl} + mock.recorder = &MockRevisionStatesGetterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRevisionStatesGetter) EXPECT() *MockRevisionStatesGetterMockRecorder { + return m.recorder +} + +// GetRevisionStates mocks base method. +func (m *MockRevisionStatesGetter) GetRevisionStates(ctx context.Context, ext *v1.ClusterExtension) (*controllers.RevisionStates, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRevisionStates", ctx, ext) + ret0, _ := ret[0].(*controllers.RevisionStates) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRevisionStates indicates an expected call of GetRevisionStates. +func (mr *MockRevisionStatesGetterMockRecorder) GetRevisionStates(ctx, ext any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRevisionStates", reflect.TypeOf((*MockRevisionStatesGetter)(nil).GetRevisionStates), ctx, ext) +} + +// MockApplier is a mock of Applier interface. +type MockApplier struct { + ctrl *gomock.Controller + recorder *MockApplierMockRecorder + isgomock struct{} +} + +// MockApplierMockRecorder is the mock recorder for MockApplier. +type MockApplierMockRecorder struct { + mock *MockApplier +} + +// NewMockApplier creates a new mock instance. +func NewMockApplier(ctrl *gomock.Controller) *MockApplier { + mock := &MockApplier{ctrl: ctrl} + mock.recorder = &MockApplierMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockApplier) EXPECT() *MockApplierMockRecorder { + return m.recorder +} + +// Apply mocks base method. +func (m *MockApplier) Apply(arg0 context.Context, arg1 fs.FS, arg2 *v1.ClusterExtension, arg3, arg4 map[string]string) (bool, string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Apply", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(string) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Apply indicates an expected call of Apply. +func (mr *MockApplierMockRecorder) Apply(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockApplier)(nil).Apply), arg0, arg1, arg2, arg3, arg4) +} + +// MockRevisionEngine is a mock of RevisionEngine interface. +type MockRevisionEngine struct { + ctrl *gomock.Controller + recorder *MockRevisionEngineMockRecorder + isgomock struct{} +} + +// MockRevisionEngineMockRecorder is the mock recorder for MockRevisionEngine. +type MockRevisionEngineMockRecorder struct { + mock *MockRevisionEngine +} + +// NewMockRevisionEngine creates a new mock instance. +func NewMockRevisionEngine(ctrl *gomock.Controller) *MockRevisionEngine { + mock := &MockRevisionEngine{ctrl: ctrl} + mock.recorder = &MockRevisionEngineMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRevisionEngine) EXPECT() *MockRevisionEngineMockRecorder { + return m.recorder +} + +// Reconcile mocks base method. +func (m *MockRevisionEngine) Reconcile(ctx context.Context, rev types.Revision, opts ...types.RevisionReconcileOption) (machinery.RevisionResult, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, rev} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Reconcile", varargs...) + ret0, _ := ret[0].(machinery.RevisionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Reconcile indicates an expected call of Reconcile. +func (mr *MockRevisionEngineMockRecorder) Reconcile(ctx, rev any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, rev}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reconcile", reflect.TypeOf((*MockRevisionEngine)(nil).Reconcile), varargs...) +} + +// Teardown mocks base method. +func (m *MockRevisionEngine) Teardown(ctx context.Context, rev types.Revision, opts ...types.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, rev} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Teardown", varargs...) + ret0, _ := ret[0].(machinery.RevisionTeardownResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Teardown indicates an expected call of Teardown. +func (mr *MockRevisionEngineMockRecorder) Teardown(ctx, rev any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, rev}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Teardown", reflect.TypeOf((*MockRevisionEngine)(nil).Teardown), varargs...) +} + +// MockRevisionEngineFactory is a mock of RevisionEngineFactory interface. +type MockRevisionEngineFactory struct { + ctrl *gomock.Controller + recorder *MockRevisionEngineFactoryMockRecorder + isgomock struct{} +} + +// MockRevisionEngineFactoryMockRecorder is the mock recorder for MockRevisionEngineFactory. +type MockRevisionEngineFactoryMockRecorder struct { + mock *MockRevisionEngineFactory +} + +// NewMockRevisionEngineFactory creates a new mock instance. +func NewMockRevisionEngineFactory(ctrl *gomock.Controller) *MockRevisionEngineFactory { + mock := &MockRevisionEngineFactory{ctrl: ctrl} + mock.recorder = &MockRevisionEngineFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRevisionEngineFactory) EXPECT() *MockRevisionEngineFactoryMockRecorder { + return m.recorder +} + +// CreateRevisionEngine mocks base method. +func (m *MockRevisionEngineFactory) CreateRevisionEngine(ctx context.Context, rev *v1.ClusterObjectSet) (controllers.RevisionEngine, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateRevisionEngine", ctx, rev) + ret0, _ := ret[0].(controllers.RevisionEngine) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateRevisionEngine indicates an expected call of CreateRevisionEngine. +func (mr *MockRevisionEngineFactoryMockRecorder) CreateRevisionEngine(ctx, rev any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRevisionEngine", reflect.TypeOf((*MockRevisionEngineFactory)(nil).CreateRevisionEngine), ctx, rev) +} diff --git a/internal/testutil/mock/crdclient/mock_crdinterface.go b/internal/testutil/mock/crdclient/mock_crdinterface.go new file mode 100644 index 0000000000..a17696916e --- /dev/null +++ b/internal/testutil/mock/crdclient/mock_crdinterface.go @@ -0,0 +1,214 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 (interfaces: CustomResourceDefinitionInterface) +// +// Generated by this command: +// +// mockgen -destination=crdclient/mock_crdinterface.go -package=crdclient k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 CustomResourceDefinitionInterface +// + +// Package crdclient is a generated GoMock package. +package crdclient + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v10 "k8s.io/apiextensions-apiserver/pkg/client/applyconfiguration/apiextensions/v1" + v11 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" +) + +// MockCustomResourceDefinitionInterface is a mock of CustomResourceDefinitionInterface interface. +type MockCustomResourceDefinitionInterface struct { + ctrl *gomock.Controller + recorder *MockCustomResourceDefinitionInterfaceMockRecorder + isgomock struct{} +} + +// MockCustomResourceDefinitionInterfaceMockRecorder is the mock recorder for MockCustomResourceDefinitionInterface. +type MockCustomResourceDefinitionInterfaceMockRecorder struct { + mock *MockCustomResourceDefinitionInterface +} + +// NewMockCustomResourceDefinitionInterface creates a new mock instance. +func NewMockCustomResourceDefinitionInterface(ctrl *gomock.Controller) *MockCustomResourceDefinitionInterface { + mock := &MockCustomResourceDefinitionInterface{ctrl: ctrl} + mock.recorder = &MockCustomResourceDefinitionInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCustomResourceDefinitionInterface) EXPECT() *MockCustomResourceDefinitionInterfaceMockRecorder { + return m.recorder +} + +// Apply mocks base method. +func (m *MockCustomResourceDefinitionInterface) Apply(ctx context.Context, customResourceDefinition *v10.CustomResourceDefinitionApplyConfiguration, opts v11.ApplyOptions) (*v1.CustomResourceDefinition, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Apply", ctx, customResourceDefinition, opts) + ret0, _ := ret[0].(*v1.CustomResourceDefinition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Apply indicates an expected call of Apply. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Apply(ctx, customResourceDefinition, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Apply), ctx, customResourceDefinition, opts) +} + +// ApplyStatus mocks base method. +func (m *MockCustomResourceDefinitionInterface) ApplyStatus(ctx context.Context, customResourceDefinition *v10.CustomResourceDefinitionApplyConfiguration, opts v11.ApplyOptions) (*v1.CustomResourceDefinition, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyStatus", ctx, customResourceDefinition, opts) + ret0, _ := ret[0].(*v1.CustomResourceDefinition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyStatus indicates an expected call of ApplyStatus. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) ApplyStatus(ctx, customResourceDefinition, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyStatus", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).ApplyStatus), ctx, customResourceDefinition, opts) +} + +// Create mocks base method. +func (m *MockCustomResourceDefinitionInterface) Create(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts v11.CreateOptions) (*v1.CustomResourceDefinition, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, customResourceDefinition, opts) + ret0, _ := ret[0].(*v1.CustomResourceDefinition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Create(ctx, customResourceDefinition, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Create), ctx, customResourceDefinition, opts) +} + +// Delete mocks base method. +func (m *MockCustomResourceDefinitionInterface) Delete(ctx context.Context, name string, opts v11.DeleteOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, name, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Delete(ctx, name, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Delete), ctx, name, opts) +} + +// DeleteCollection mocks base method. +func (m *MockCustomResourceDefinitionInterface) DeleteCollection(ctx context.Context, opts v11.DeleteOptions, listOpts v11.ListOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCollection", ctx, opts, listOpts) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCollection indicates an expected call of DeleteCollection. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) DeleteCollection(ctx, opts, listOpts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCollection", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).DeleteCollection), ctx, opts, listOpts) +} + +// Get mocks base method. +func (m *MockCustomResourceDefinitionInterface) Get(ctx context.Context, name string, opts v11.GetOptions) (*v1.CustomResourceDefinition, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, name, opts) + ret0, _ := ret[0].(*v1.CustomResourceDefinition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Get(ctx, name, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Get), ctx, name, opts) +} + +// List mocks base method. +func (m *MockCustomResourceDefinitionInterface) List(ctx context.Context, opts v11.ListOptions) (*v1.CustomResourceDefinitionList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, opts) + ret0, _ := ret[0].(*v1.CustomResourceDefinitionList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) List(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).List), ctx, opts) +} + +// Patch mocks base method. +func (m *MockCustomResourceDefinitionInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v11.PatchOptions, subresources ...string) (*v1.CustomResourceDefinition, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, name, pt, data, opts} + for _, a := range subresources { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(*v1.CustomResourceDefinition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Patch indicates an expected call of Patch. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Patch(ctx, name, pt, data, opts any, subresources ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, name, pt, data, opts}, subresources...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Patch), varargs...) +} + +// Update mocks base method. +func (m *MockCustomResourceDefinitionInterface) Update(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts v11.UpdateOptions) (*v1.CustomResourceDefinition, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, customResourceDefinition, opts) + ret0, _ := ret[0].(*v1.CustomResourceDefinition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Update(ctx, customResourceDefinition, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Update), ctx, customResourceDefinition, opts) +} + +// UpdateStatus mocks base method. +func (m *MockCustomResourceDefinitionInterface) UpdateStatus(ctx context.Context, customResourceDefinition *v1.CustomResourceDefinition, opts v11.UpdateOptions) (*v1.CustomResourceDefinition, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateStatus", ctx, customResourceDefinition, opts) + ret0, _ := ret[0].(*v1.CustomResourceDefinition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateStatus indicates an expected call of UpdateStatus. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) UpdateStatus(ctx, customResourceDefinition, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).UpdateStatus), ctx, customResourceDefinition, opts) +} + +// Watch mocks base method. +func (m *MockCustomResourceDefinitionInterface) Watch(ctx context.Context, opts v11.ListOptions) (watch.Interface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Watch", ctx, opts) + ret0, _ := ret[0].(watch.Interface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Watch indicates an expected call of Watch. +func (mr *MockCustomResourceDefinitionInterfaceMockRecorder) Watch(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockCustomResourceDefinitionInterface)(nil).Watch), ctx, opts) +} diff --git a/internal/testutil/mock/ctrlclient/mock_client.go b/internal/testutil/mock/ctrlclient/mock_client.go new file mode 100644 index 0000000000..5833af1a5a --- /dev/null +++ b/internal/testutil/mock/ctrlclient/mock_client.go @@ -0,0 +1,483 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: sigs.k8s.io/controller-runtime/pkg/client (interfaces: Client,StatusWriter,SubResourceWriter) +// +// Generated by this command: +// +// mockgen -destination=ctrlclient/mock_client.go -package=ctrlclient sigs.k8s.io/controller-runtime/pkg/client Client,StatusWriter,SubResourceWriter +// + +// Package ctrlclient is a generated GoMock package. +package ctrlclient + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + meta "k8s.io/apimachinery/pkg/api/meta" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + client "sigs.k8s.io/controller-runtime/pkg/client" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder + isgomock struct{} +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// Apply mocks base method. +func (m *MockClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.ApplyOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Apply", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Apply indicates an expected call of Apply. +func (mr *MockClientMockRecorder) Apply(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockClient)(nil).Apply), varargs...) +} + +// Create mocks base method. +func (m *MockClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockClientMockRecorder) Create(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockClient)(nil).Create), varargs...) +} + +// Delete mocks base method. +func (m *MockClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Delete", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockClientMockRecorder) Delete(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), varargs...) +} + +// DeleteAllOf mocks base method. +func (m *MockClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteAllOf", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAllOf indicates an expected call of DeleteAllOf. +func (mr *MockClientMockRecorder) DeleteAllOf(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllOf", reflect.TypeOf((*MockClient)(nil).DeleteAllOf), varargs...) +} + +// Get mocks base method. +func (m *MockClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, key, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockClientMockRecorder) Get(ctx, key, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, key, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), varargs...) +} + +// GroupVersionKindFor mocks base method. +func (m *MockClient) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GroupVersionKindFor", obj) + ret0, _ := ret[0].(schema.GroupVersionKind) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GroupVersionKindFor indicates an expected call of GroupVersionKindFor. +func (mr *MockClientMockRecorder) GroupVersionKindFor(obj any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupVersionKindFor", reflect.TypeOf((*MockClient)(nil).GroupVersionKindFor), obj) +} + +// IsObjectNamespaced mocks base method. +func (m *MockClient) IsObjectNamespaced(obj runtime.Object) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsObjectNamespaced", obj) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsObjectNamespaced indicates an expected call of IsObjectNamespaced. +func (mr *MockClientMockRecorder) IsObjectNamespaced(obj any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsObjectNamespaced", reflect.TypeOf((*MockClient)(nil).IsObjectNamespaced), obj) +} + +// List mocks base method. +func (m *MockClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, list} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// List indicates an expected call of List. +func (mr *MockClientMockRecorder) List(ctx, list any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, list}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClient)(nil).List), varargs...) +} + +// Patch mocks base method. +func (m *MockClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, patch} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Patch indicates an expected call of Patch. +func (mr *MockClientMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, patch}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockClient)(nil).Patch), varargs...) +} + +// RESTMapper mocks base method. +func (m *MockClient) RESTMapper() meta.RESTMapper { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RESTMapper") + ret0, _ := ret[0].(meta.RESTMapper) + return ret0 +} + +// RESTMapper indicates an expected call of RESTMapper. +func (mr *MockClientMockRecorder) RESTMapper() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RESTMapper", reflect.TypeOf((*MockClient)(nil).RESTMapper)) +} + +// Scheme mocks base method. +func (m *MockClient) Scheme() *runtime.Scheme { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Scheme") + ret0, _ := ret[0].(*runtime.Scheme) + return ret0 +} + +// Scheme indicates an expected call of Scheme. +func (mr *MockClientMockRecorder) Scheme() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scheme", reflect.TypeOf((*MockClient)(nil).Scheme)) +} + +// Status mocks base method. +func (m *MockClient) Status() client.SubResourceWriter { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Status") + ret0, _ := ret[0].(client.SubResourceWriter) + return ret0 +} + +// Status indicates an expected call of Status. +func (mr *MockClientMockRecorder) Status() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockClient)(nil).Status)) +} + +// SubResource mocks base method. +func (m *MockClient) SubResource(subResource string) client.SubResourceClient { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubResource", subResource) + ret0, _ := ret[0].(client.SubResourceClient) + return ret0 +} + +// SubResource indicates an expected call of SubResource. +func (mr *MockClientMockRecorder) SubResource(subResource any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubResource", reflect.TypeOf((*MockClient)(nil).SubResource), subResource) +} + +// Update mocks base method. +func (m *MockClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockClientMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockClient)(nil).Update), varargs...) +} + +// MockStatusWriter is a mock of StatusWriter interface. +type MockStatusWriter struct { + ctrl *gomock.Controller + recorder *MockStatusWriterMockRecorder + isgomock struct{} +} + +// MockStatusWriterMockRecorder is the mock recorder for MockStatusWriter. +type MockStatusWriterMockRecorder struct { + mock *MockStatusWriter +} + +// NewMockStatusWriter creates a new mock instance. +func NewMockStatusWriter(ctrl *gomock.Controller) *MockStatusWriter { + mock := &MockStatusWriter{ctrl: ctrl} + mock.recorder = &MockStatusWriterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStatusWriter) EXPECT() *MockStatusWriterMockRecorder { + return m.recorder +} + +// Apply mocks base method. +func (m *MockStatusWriter) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Apply", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Apply indicates an expected call of Apply. +func (mr *MockStatusWriterMockRecorder) Apply(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockStatusWriter)(nil).Apply), varargs...) +} + +// Create mocks base method. +func (m *MockStatusWriter) Create(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceCreateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, subResource} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockStatusWriterMockRecorder) Create(ctx, obj, subResource any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, subResource}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockStatusWriter)(nil).Create), varargs...) +} + +// Patch mocks base method. +func (m *MockStatusWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, patch} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Patch indicates an expected call of Patch. +func (mr *MockStatusWriterMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, patch}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockStatusWriter)(nil).Patch), varargs...) +} + +// Update mocks base method. +func (m *MockStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockStatusWriterMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockStatusWriter)(nil).Update), varargs...) +} + +// MockSubResourceWriter is a mock of SubResourceWriter interface. +type MockSubResourceWriter struct { + ctrl *gomock.Controller + recorder *MockSubResourceWriterMockRecorder + isgomock struct{} +} + +// MockSubResourceWriterMockRecorder is the mock recorder for MockSubResourceWriter. +type MockSubResourceWriterMockRecorder struct { + mock *MockSubResourceWriter +} + +// NewMockSubResourceWriter creates a new mock instance. +func NewMockSubResourceWriter(ctrl *gomock.Controller) *MockSubResourceWriter { + mock := &MockSubResourceWriter{ctrl: ctrl} + mock.recorder = &MockSubResourceWriterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSubResourceWriter) EXPECT() *MockSubResourceWriterMockRecorder { + return m.recorder +} + +// Apply mocks base method. +func (m *MockSubResourceWriter) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Apply", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Apply indicates an expected call of Apply. +func (mr *MockSubResourceWriterMockRecorder) Apply(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockSubResourceWriter)(nil).Apply), varargs...) +} + +// Create mocks base method. +func (m *MockSubResourceWriter) Create(ctx context.Context, obj, subResource client.Object, opts ...client.SubResourceCreateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, subResource} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockSubResourceWriterMockRecorder) Create(ctx, obj, subResource any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, subResource}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSubResourceWriter)(nil).Create), varargs...) +} + +// Patch mocks base method. +func (m *MockSubResourceWriter) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj, patch} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Patch indicates an expected call of Patch. +func (mr *MockSubResourceWriterMockRecorder) Patch(ctx, obj, patch any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj, patch}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockSubResourceWriter)(nil).Patch), varargs...) +} + +// Update mocks base method. +func (m *MockSubResourceWriter) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + m.ctrl.T.Helper() + varargs := []any{ctx, obj} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockSubResourceWriterMockRecorder) Update(ctx, obj any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, obj}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSubResourceWriter)(nil).Update), varargs...) +} diff --git a/internal/testutil/mock/generate.go b/internal/testutil/mock/generate.go new file mode 100644 index 0000000000..97a0b5d6a9 --- /dev/null +++ b/internal/testutil/mock/generate.go @@ -0,0 +1,54 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mock + +// External interfaces +//go:generate mockgen -destination=helmclient/mock_actionclient.go -package=helmclient github.com/operator-framework/helm-operator-plugins/pkg/client ActionInterface,ActionClientGetter +//go:generate mockgen -destination=helmclient/mock_composite.go -package=helmclient github.com/operator-framework/operator-controller/internal/testutil/mock/helmclient ActionClientGetterAndInterface +//go:generate mockgen -destination=ctrlclient/mock_client.go -package=ctrlclient sigs.k8s.io/controller-runtime/pkg/client Client,StatusWriter,SubResourceWriter +//go:generate mockgen -destination=crdclient/mock_crdinterface.go -package=crdclient k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 CustomResourceDefinitionInterface +//go:generate mockgen -destination=logrsink/mock_logsink.go -package=logrsink github.com/go-logr/logr LogSink +//go:generate mockgen -destination=machinery/mock_results.go -package=machinery pkg.package-operator.run/boxcutter/machinery RevisionResult,PhaseResult,ObjectResult,RevisionTeardownResult +//go:generate mockgen -destination=rbac/mock_rulesresolver.go -package=rbac k8s.io/kubernetes/pkg/registry/rbac/validation AuthorizationRuleResolver +//go:generate mockgen -destination=httputil/mock_roundtripper.go -package=httputil net/http RoundTripper + +// Internal interfaces — catalogd +//go:generate mockgen -destination=storage/mock_instance.go -package=storage github.com/operator-framework/operator-controller/internal/catalogd/storage Instance +//go:generate mockgen -destination=catalogdserver/mock_catalogstore.go -package=catalogdserver github.com/operator-framework/operator-controller/internal/catalogd/server CatalogStore +//go:generate mockgen -destination=catalogdservice/mock_graphqlservice.go -package=catalogdservice github.com/operator-framework/operator-controller/internal/catalogd/service GraphQLService + +// Internal interfaces — operator-controller applier +//go:generate mockgen -destination=applier/mock_applier.go -package=applier github.com/operator-framework/operator-controller/internal/operator-controller/applier Preflight,HelmReleaseToObjectsConverterInterface,HelmChartProvider,ClusterObjectSetGenerator,ManifestProvider + +// Internal interfaces — operator-controller authorization +//go:generate mockgen -destination=authorization/mock_authorization.go -package=authorization github.com/operator-framework/operator-controller/internal/operator-controller/authorization PreAuthorizer + +// Internal interfaces — operator-controller catalogmetadata +//go:generate mockgen -destination=catalogclient/mock_cache.go -package=catalogclient github.com/operator-framework/operator-controller/internal/operator-controller/catalogmetadata/client Cache + +// Internal interfaces — operator-controller config +//go:generate mockgen -destination=config/mock_schemaprovider.go -package=config github.com/operator-framework/operator-controller/internal/operator-controller/config SchemaProvider + +// Internal interfaces — operator-controller contentmanager +//go:generate mockgen -destination=contentmanager/mock_contentmanager.go -package=contentmanager github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager Manager +//go:generate mockgen -destination=cmcache/mock_cache.go -package=cmcache github.com/operator-framework/operator-controller/internal/operator-controller/contentmanager/cache Cache,Watcher,CloserSyncingSource + +// Internal interfaces — operator-controller controllers +//go:generate mockgen -destination=controllers/mock_controllers.go -package=controllers github.com/operator-framework/operator-controller/internal/operator-controller/controllers CatalogCache,CatalogCachePopulator,RevisionStatesGetter,Applier,RevisionEngine,RevisionEngineFactory + +// Internal interfaces — rukpak render +//go:generate mockgen -destination=render/mock_certprovider.go -package=render github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render CertificateProvider diff --git a/internal/testutil/mock/helmclient/interfaces.go b/internal/testutil/mock/helmclient/interfaces.go new file mode 100644 index 0000000000..30142ce494 --- /dev/null +++ b/internal/testutil/mock/helmclient/interfaces.go @@ -0,0 +1,28 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package helmclient + +import ( + helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client" +) + +// ActionClientGetterAndInterface is a composite interface for tests that need +// a single object implementing both ActionClientGetter and ActionInterface. +type ActionClientGetterAndInterface interface { + helmclient.ActionClientGetter + helmclient.ActionInterface +} diff --git a/internal/testutil/mock/helmclient/mock_actionclient.go b/internal/testutil/mock/helmclient/mock_actionclient.go new file mode 100644 index 0000000000..108f2bca91 --- /dev/null +++ b/internal/testutil/mock/helmclient/mock_actionclient.go @@ -0,0 +1,213 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/helm-operator-plugins/pkg/client (interfaces: ActionInterface,ActionClientGetter) +// +// Generated by this command: +// +// mockgen -destination=helmclient/mock_actionclient.go -package=helmclient github.com/operator-framework/helm-operator-plugins/pkg/client ActionInterface,ActionClientGetter +// + +// Package helmclient is a generated GoMock package. +package helmclient + +import ( + context "context" + reflect "reflect" + + client "github.com/operator-framework/helm-operator-plugins/pkg/client" + gomock "go.uber.org/mock/gomock" + action "helm.sh/helm/v3/pkg/action" + chart "helm.sh/helm/v3/pkg/chart" + release "helm.sh/helm/v3/pkg/release" + client0 "sigs.k8s.io/controller-runtime/pkg/client" +) + +// MockActionInterface is a mock of ActionInterface interface. +type MockActionInterface struct { + ctrl *gomock.Controller + recorder *MockActionInterfaceMockRecorder + isgomock struct{} +} + +// MockActionInterfaceMockRecorder is the mock recorder for MockActionInterface. +type MockActionInterfaceMockRecorder struct { + mock *MockActionInterface +} + +// NewMockActionInterface creates a new mock instance. +func NewMockActionInterface(ctrl *gomock.Controller) *MockActionInterface { + mock := &MockActionInterface{ctrl: ctrl} + mock.recorder = &MockActionInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockActionInterface) EXPECT() *MockActionInterfaceMockRecorder { + return m.recorder +} + +// Config mocks base method. +func (m *MockActionInterface) Config() *action.Configuration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Config") + ret0, _ := ret[0].(*action.Configuration) + return ret0 +} + +// Config indicates an expected call of Config. +func (mr *MockActionInterfaceMockRecorder) Config() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Config", reflect.TypeOf((*MockActionInterface)(nil).Config)) +} + +// Get mocks base method. +func (m *MockActionInterface) Get(name string, opts ...client.GetOption) (*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockActionInterfaceMockRecorder) Get(name any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockActionInterface)(nil).Get), varargs...) +} + +// History mocks base method. +func (m *MockActionInterface) History(name string, opts ...client.HistoryOption) ([]*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "History", varargs...) + ret0, _ := ret[0].([]*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// History indicates an expected call of History. +func (mr *MockActionInterfaceMockRecorder) History(name any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "History", reflect.TypeOf((*MockActionInterface)(nil).History), varargs...) +} + +// Install mocks base method. +func (m *MockActionInterface) Install(name, namespace string, chrt *chart.Chart, vals map[string]any, opts ...client.InstallOption) (*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name, namespace, chrt, vals} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Install", varargs...) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Install indicates an expected call of Install. +func (mr *MockActionInterfaceMockRecorder) Install(name, namespace, chrt, vals any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name, namespace, chrt, vals}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Install", reflect.TypeOf((*MockActionInterface)(nil).Install), varargs...) +} + +// Reconcile mocks base method. +func (m *MockActionInterface) Reconcile(rel *release.Release) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Reconcile", rel) + ret0, _ := ret[0].(error) + return ret0 +} + +// Reconcile indicates an expected call of Reconcile. +func (mr *MockActionInterfaceMockRecorder) Reconcile(rel any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reconcile", reflect.TypeOf((*MockActionInterface)(nil).Reconcile), rel) +} + +// Uninstall mocks base method. +func (m *MockActionInterface) Uninstall(name string, opts ...client.UninstallOption) (*release.UninstallReleaseResponse, error) { + m.ctrl.T.Helper() + varargs := []any{name} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Uninstall", varargs...) + ret0, _ := ret[0].(*release.UninstallReleaseResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Uninstall indicates an expected call of Uninstall. +func (mr *MockActionInterfaceMockRecorder) Uninstall(name any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Uninstall", reflect.TypeOf((*MockActionInterface)(nil).Uninstall), varargs...) +} + +// Upgrade mocks base method. +func (m *MockActionInterface) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]any, opts ...client.UpgradeOption) (*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name, namespace, chrt, vals} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Upgrade", varargs...) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Upgrade indicates an expected call of Upgrade. +func (mr *MockActionInterfaceMockRecorder) Upgrade(name, namespace, chrt, vals any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name, namespace, chrt, vals}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upgrade", reflect.TypeOf((*MockActionInterface)(nil).Upgrade), varargs...) +} + +// MockActionClientGetter is a mock of ActionClientGetter interface. +type MockActionClientGetter struct { + ctrl *gomock.Controller + recorder *MockActionClientGetterMockRecorder + isgomock struct{} +} + +// MockActionClientGetterMockRecorder is the mock recorder for MockActionClientGetter. +type MockActionClientGetterMockRecorder struct { + mock *MockActionClientGetter +} + +// NewMockActionClientGetter creates a new mock instance. +func NewMockActionClientGetter(ctrl *gomock.Controller) *MockActionClientGetter { + mock := &MockActionClientGetter{ctrl: ctrl} + mock.recorder = &MockActionClientGetterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockActionClientGetter) EXPECT() *MockActionClientGetterMockRecorder { + return m.recorder +} + +// ActionClientFor mocks base method. +func (m *MockActionClientGetter) ActionClientFor(ctx context.Context, obj client0.Object) (client.ActionInterface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ActionClientFor", ctx, obj) + ret0, _ := ret[0].(client.ActionInterface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ActionClientFor indicates an expected call of ActionClientFor. +func (mr *MockActionClientGetterMockRecorder) ActionClientFor(ctx, obj any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActionClientFor", reflect.TypeOf((*MockActionClientGetter)(nil).ActionClientFor), ctx, obj) +} diff --git a/internal/testutil/mock/helmclient/mock_composite.go b/internal/testutil/mock/helmclient/mock_composite.go new file mode 100644 index 0000000000..b46c40fc88 --- /dev/null +++ b/internal/testutil/mock/helmclient/mock_composite.go @@ -0,0 +1,189 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/testutil/mock/helmclient (interfaces: ActionClientGetterAndInterface) +// +// Generated by this command: +// +// mockgen -destination=helmclient/mock_composite.go -package=helmclient github.com/operator-framework/operator-controller/internal/testutil/mock/helmclient ActionClientGetterAndInterface +// + +// Package helmclient is a generated GoMock package. +package helmclient + +import ( + context "context" + reflect "reflect" + + client "github.com/operator-framework/helm-operator-plugins/pkg/client" + gomock "go.uber.org/mock/gomock" + action "helm.sh/helm/v3/pkg/action" + chart "helm.sh/helm/v3/pkg/chart" + release "helm.sh/helm/v3/pkg/release" + client0 "sigs.k8s.io/controller-runtime/pkg/client" +) + +// MockActionClientGetterAndInterface is a mock of ActionClientGetterAndInterface interface. +type MockActionClientGetterAndInterface struct { + ctrl *gomock.Controller + recorder *MockActionClientGetterAndInterfaceMockRecorder + isgomock struct{} +} + +// MockActionClientGetterAndInterfaceMockRecorder is the mock recorder for MockActionClientGetterAndInterface. +type MockActionClientGetterAndInterfaceMockRecorder struct { + mock *MockActionClientGetterAndInterface +} + +// NewMockActionClientGetterAndInterface creates a new mock instance. +func NewMockActionClientGetterAndInterface(ctrl *gomock.Controller) *MockActionClientGetterAndInterface { + mock := &MockActionClientGetterAndInterface{ctrl: ctrl} + mock.recorder = &MockActionClientGetterAndInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockActionClientGetterAndInterface) EXPECT() *MockActionClientGetterAndInterfaceMockRecorder { + return m.recorder +} + +// ActionClientFor mocks base method. +func (m *MockActionClientGetterAndInterface) ActionClientFor(ctx context.Context, obj client0.Object) (client.ActionInterface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ActionClientFor", ctx, obj) + ret0, _ := ret[0].(client.ActionInterface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ActionClientFor indicates an expected call of ActionClientFor. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) ActionClientFor(ctx, obj any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActionClientFor", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).ActionClientFor), ctx, obj) +} + +// Config mocks base method. +func (m *MockActionClientGetterAndInterface) Config() *action.Configuration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Config") + ret0, _ := ret[0].(*action.Configuration) + return ret0 +} + +// Config indicates an expected call of Config. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) Config() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Config", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).Config)) +} + +// Get mocks base method. +func (m *MockActionClientGetterAndInterface) Get(name string, opts ...client.GetOption) (*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) Get(name any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).Get), varargs...) +} + +// History mocks base method. +func (m *MockActionClientGetterAndInterface) History(name string, opts ...client.HistoryOption) ([]*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "History", varargs...) + ret0, _ := ret[0].([]*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// History indicates an expected call of History. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) History(name any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "History", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).History), varargs...) +} + +// Install mocks base method. +func (m *MockActionClientGetterAndInterface) Install(name, namespace string, chrt *chart.Chart, vals map[string]any, opts ...client.InstallOption) (*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name, namespace, chrt, vals} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Install", varargs...) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Install indicates an expected call of Install. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) Install(name, namespace, chrt, vals any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name, namespace, chrt, vals}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Install", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).Install), varargs...) +} + +// Reconcile mocks base method. +func (m *MockActionClientGetterAndInterface) Reconcile(rel *release.Release) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Reconcile", rel) + ret0, _ := ret[0].(error) + return ret0 +} + +// Reconcile indicates an expected call of Reconcile. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) Reconcile(rel any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reconcile", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).Reconcile), rel) +} + +// Uninstall mocks base method. +func (m *MockActionClientGetterAndInterface) Uninstall(name string, opts ...client.UninstallOption) (*release.UninstallReleaseResponse, error) { + m.ctrl.T.Helper() + varargs := []any{name} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Uninstall", varargs...) + ret0, _ := ret[0].(*release.UninstallReleaseResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Uninstall indicates an expected call of Uninstall. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) Uninstall(name any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Uninstall", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).Uninstall), varargs...) +} + +// Upgrade mocks base method. +func (m *MockActionClientGetterAndInterface) Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]any, opts ...client.UpgradeOption) (*release.Release, error) { + m.ctrl.T.Helper() + varargs := []any{name, namespace, chrt, vals} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Upgrade", varargs...) + ret0, _ := ret[0].(*release.Release) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Upgrade indicates an expected call of Upgrade. +func (mr *MockActionClientGetterAndInterfaceMockRecorder) Upgrade(name, namespace, chrt, vals any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name, namespace, chrt, vals}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upgrade", reflect.TypeOf((*MockActionClientGetterAndInterface)(nil).Upgrade), varargs...) +} diff --git a/internal/testutil/mock/httputil/mock_roundtripper.go b/internal/testutil/mock/httputil/mock_roundtripper.go new file mode 100644 index 0000000000..80974859c9 --- /dev/null +++ b/internal/testutil/mock/httputil/mock_roundtripper.go @@ -0,0 +1,56 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: net/http (interfaces: RoundTripper) +// +// Generated by this command: +// +// mockgen -destination=httputil/mock_roundtripper.go -package=httputil net/http RoundTripper +// + +// Package httputil is a generated GoMock package. +package httputil + +import ( + http "net/http" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockRoundTripper is a mock of RoundTripper interface. +type MockRoundTripper struct { + ctrl *gomock.Controller + recorder *MockRoundTripperMockRecorder + isgomock struct{} +} + +// MockRoundTripperMockRecorder is the mock recorder for MockRoundTripper. +type MockRoundTripperMockRecorder struct { + mock *MockRoundTripper +} + +// NewMockRoundTripper creates a new mock instance. +func NewMockRoundTripper(ctrl *gomock.Controller) *MockRoundTripper { + mock := &MockRoundTripper{ctrl: ctrl} + mock.recorder = &MockRoundTripperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRoundTripper) EXPECT() *MockRoundTripperMockRecorder { + return m.recorder +} + +// RoundTrip mocks base method. +func (m *MockRoundTripper) RoundTrip(arg0 *http.Request) (*http.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RoundTrip", arg0) + ret0, _ := ret[0].(*http.Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RoundTrip indicates an expected call of RoundTrip. +func (mr *MockRoundTripperMockRecorder) RoundTrip(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoundTrip", reflect.TypeOf((*MockRoundTripper)(nil).RoundTrip), arg0) +} diff --git a/internal/testutil/mock/logrsink/mock_logsink.go b/internal/testutil/mock/logrsink/mock_logsink.go new file mode 100644 index 0000000000..d23a8e4b68 --- /dev/null +++ b/internal/testutil/mock/logrsink/mock_logsink.go @@ -0,0 +1,133 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/go-logr/logr (interfaces: LogSink) +// +// Generated by this command: +// +// mockgen -destination=logrsink/mock_logsink.go -package=logrsink github.com/go-logr/logr LogSink +// + +// Package logrsink is a generated GoMock package. +package logrsink + +import ( + reflect "reflect" + + logr "github.com/go-logr/logr" + gomock "go.uber.org/mock/gomock" +) + +// MockLogSink is a mock of LogSink interface. +type MockLogSink struct { + ctrl *gomock.Controller + recorder *MockLogSinkMockRecorder + isgomock struct{} +} + +// MockLogSinkMockRecorder is the mock recorder for MockLogSink. +type MockLogSinkMockRecorder struct { + mock *MockLogSink +} + +// NewMockLogSink creates a new mock instance. +func NewMockLogSink(ctrl *gomock.Controller) *MockLogSink { + mock := &MockLogSink{ctrl: ctrl} + mock.recorder = &MockLogSinkMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLogSink) EXPECT() *MockLogSinkMockRecorder { + return m.recorder +} + +// Enabled mocks base method. +func (m *MockLogSink) Enabled(level int) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Enabled", level) + ret0, _ := ret[0].(bool) + return ret0 +} + +// Enabled indicates an expected call of Enabled. +func (mr *MockLogSinkMockRecorder) Enabled(level any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enabled", reflect.TypeOf((*MockLogSink)(nil).Enabled), level) +} + +// Error mocks base method. +func (m *MockLogSink) Error(err error, msg string, keysAndValues ...any) { + m.ctrl.T.Helper() + varargs := []any{err, msg} + for _, a := range keysAndValues { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Error", varargs...) +} + +// Error indicates an expected call of Error. +func (mr *MockLogSinkMockRecorder) Error(err, msg any, keysAndValues ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{err, msg}, keysAndValues...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogSink)(nil).Error), varargs...) +} + +// Info mocks base method. +func (m *MockLogSink) Info(level int, msg string, keysAndValues ...any) { + m.ctrl.T.Helper() + varargs := []any{level, msg} + for _, a := range keysAndValues { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Info", varargs...) +} + +// Info indicates an expected call of Info. +func (mr *MockLogSinkMockRecorder) Info(level, msg any, keysAndValues ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{level, msg}, keysAndValues...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogSink)(nil).Info), varargs...) +} + +// Init mocks base method. +func (m *MockLogSink) Init(info logr.RuntimeInfo) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Init", info) +} + +// Init indicates an expected call of Init. +func (mr *MockLogSinkMockRecorder) Init(info any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockLogSink)(nil).Init), info) +} + +// WithName mocks base method. +func (m *MockLogSink) WithName(name string) logr.LogSink { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WithName", name) + ret0, _ := ret[0].(logr.LogSink) + return ret0 +} + +// WithName indicates an expected call of WithName. +func (mr *MockLogSinkMockRecorder) WithName(name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithName", reflect.TypeOf((*MockLogSink)(nil).WithName), name) +} + +// WithValues mocks base method. +func (m *MockLogSink) WithValues(keysAndValues ...any) logr.LogSink { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range keysAndValues { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "WithValues", varargs...) + ret0, _ := ret[0].(logr.LogSink) + return ret0 +} + +// WithValues indicates an expected call of WithValues. +func (mr *MockLogSinkMockRecorder) WithValues(keysAndValues ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithValues", reflect.TypeOf((*MockLogSink)(nil).WithValues), keysAndValues...) +} diff --git a/internal/testutil/mock/machinery/mock_results.go b/internal/testutil/mock/machinery/mock_results.go new file mode 100644 index 0000000000..f1cbecb500 --- /dev/null +++ b/internal/testutil/mock/machinery/mock_results.go @@ -0,0 +1,466 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg.package-operator.run/boxcutter/machinery (interfaces: RevisionResult,PhaseResult,ObjectResult,RevisionTeardownResult) +// +// Generated by this command: +// +// mockgen -destination=machinery/mock_results.go -package=machinery pkg.package-operator.run/boxcutter/machinery RevisionResult,PhaseResult,ObjectResult,RevisionTeardownResult +// + +// Package machinery is a generated GoMock package. +package machinery + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + machinery "pkg.package-operator.run/boxcutter/machinery" + types "pkg.package-operator.run/boxcutter/machinery/types" + validation "pkg.package-operator.run/boxcutter/validation" +) + +// MockRevisionResult is a mock of RevisionResult interface. +type MockRevisionResult struct { + ctrl *gomock.Controller + recorder *MockRevisionResultMockRecorder + isgomock struct{} +} + +// MockRevisionResultMockRecorder is the mock recorder for MockRevisionResult. +type MockRevisionResultMockRecorder struct { + mock *MockRevisionResult +} + +// NewMockRevisionResult creates a new mock instance. +func NewMockRevisionResult(ctrl *gomock.Controller) *MockRevisionResult { + mock := &MockRevisionResult{ctrl: ctrl} + mock.recorder = &MockRevisionResultMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRevisionResult) EXPECT() *MockRevisionResultMockRecorder { + return m.recorder +} + +// GetPhases mocks base method. +func (m *MockRevisionResult) GetPhases() []machinery.PhaseResult { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPhases") + ret0, _ := ret[0].([]machinery.PhaseResult) + return ret0 +} + +// GetPhases indicates an expected call of GetPhases. +func (mr *MockRevisionResultMockRecorder) GetPhases() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhases", reflect.TypeOf((*MockRevisionResult)(nil).GetPhases)) +} + +// GetValidationError mocks base method. +func (m *MockRevisionResult) GetValidationError() *validation.RevisionValidationError { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidationError") + ret0, _ := ret[0].(*validation.RevisionValidationError) + return ret0 +} + +// GetValidationError indicates an expected call of GetValidationError. +func (mr *MockRevisionResultMockRecorder) GetValidationError() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidationError", reflect.TypeOf((*MockRevisionResult)(nil).GetValidationError)) +} + +// HasProgressed mocks base method. +func (m *MockRevisionResult) HasProgressed() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasProgressed") + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasProgressed indicates an expected call of HasProgressed. +func (mr *MockRevisionResultMockRecorder) HasProgressed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasProgressed", reflect.TypeOf((*MockRevisionResult)(nil).HasProgressed)) +} + +// InTransition mocks base method. +func (m *MockRevisionResult) InTransition() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InTransition") + ret0, _ := ret[0].(bool) + return ret0 +} + +// InTransition indicates an expected call of InTransition. +func (mr *MockRevisionResultMockRecorder) InTransition() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InTransition", reflect.TypeOf((*MockRevisionResult)(nil).InTransition)) +} + +// IsComplete mocks base method. +func (m *MockRevisionResult) IsComplete() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsComplete") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsComplete indicates an expected call of IsComplete. +func (mr *MockRevisionResultMockRecorder) IsComplete() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsComplete", reflect.TypeOf((*MockRevisionResult)(nil).IsComplete)) +} + +// String mocks base method. +func (m *MockRevisionResult) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String. +func (mr *MockRevisionResultMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockRevisionResult)(nil).String)) +} + +// MockPhaseResult is a mock of PhaseResult interface. +type MockPhaseResult struct { + ctrl *gomock.Controller + recorder *MockPhaseResultMockRecorder + isgomock struct{} +} + +// MockPhaseResultMockRecorder is the mock recorder for MockPhaseResult. +type MockPhaseResultMockRecorder struct { + mock *MockPhaseResult +} + +// NewMockPhaseResult creates a new mock instance. +func NewMockPhaseResult(ctrl *gomock.Controller) *MockPhaseResult { + mock := &MockPhaseResult{ctrl: ctrl} + mock.recorder = &MockPhaseResultMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPhaseResult) EXPECT() *MockPhaseResultMockRecorder { + return m.recorder +} + +// GetName mocks base method. +func (m *MockPhaseResult) GetName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetName") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetName indicates an expected call of GetName. +func (mr *MockPhaseResultMockRecorder) GetName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockPhaseResult)(nil).GetName)) +} + +// GetObjects mocks base method. +func (m *MockPhaseResult) GetObjects() []machinery.ObjectResult { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetObjects") + ret0, _ := ret[0].([]machinery.ObjectResult) + return ret0 +} + +// GetObjects indicates an expected call of GetObjects. +func (mr *MockPhaseResultMockRecorder) GetObjects() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjects", reflect.TypeOf((*MockPhaseResult)(nil).GetObjects)) +} + +// GetValidationError mocks base method. +func (m *MockPhaseResult) GetValidationError() *validation.PhaseValidationError { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidationError") + ret0, _ := ret[0].(*validation.PhaseValidationError) + return ret0 +} + +// GetValidationError indicates an expected call of GetValidationError. +func (mr *MockPhaseResultMockRecorder) GetValidationError() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidationError", reflect.TypeOf((*MockPhaseResult)(nil).GetValidationError)) +} + +// HasProgressed mocks base method. +func (m *MockPhaseResult) HasProgressed() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasProgressed") + ret0, _ := ret[0].(bool) + return ret0 +} + +// HasProgressed indicates an expected call of HasProgressed. +func (mr *MockPhaseResultMockRecorder) HasProgressed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasProgressed", reflect.TypeOf((*MockPhaseResult)(nil).HasProgressed)) +} + +// InTransition mocks base method. +func (m *MockPhaseResult) InTransition() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InTransition") + ret0, _ := ret[0].(bool) + return ret0 +} + +// InTransition indicates an expected call of InTransition. +func (mr *MockPhaseResultMockRecorder) InTransition() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InTransition", reflect.TypeOf((*MockPhaseResult)(nil).InTransition)) +} + +// IsComplete mocks base method. +func (m *MockPhaseResult) IsComplete() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsComplete") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsComplete indicates an expected call of IsComplete. +func (mr *MockPhaseResultMockRecorder) IsComplete() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsComplete", reflect.TypeOf((*MockPhaseResult)(nil).IsComplete)) +} + +// String mocks base method. +func (m *MockPhaseResult) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String. +func (mr *MockPhaseResultMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockPhaseResult)(nil).String)) +} + +// MockObjectResult is a mock of ObjectResult interface. +type MockObjectResult struct { + ctrl *gomock.Controller + recorder *MockObjectResultMockRecorder + isgomock struct{} +} + +// MockObjectResultMockRecorder is the mock recorder for MockObjectResult. +type MockObjectResultMockRecorder struct { + mock *MockObjectResult +} + +// NewMockObjectResult creates a new mock instance. +func NewMockObjectResult(ctrl *gomock.Controller) *MockObjectResult { + mock := &MockObjectResult{ctrl: ctrl} + mock.recorder = &MockObjectResultMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockObjectResult) EXPECT() *MockObjectResultMockRecorder { + return m.recorder +} + +// Action mocks base method. +func (m *MockObjectResult) Action() machinery.Action { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Action") + ret0, _ := ret[0].(machinery.Action) + return ret0 +} + +// Action indicates an expected call of Action. +func (mr *MockObjectResultMockRecorder) Action() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Action", reflect.TypeOf((*MockObjectResult)(nil).Action)) +} + +// IsComplete mocks base method. +func (m *MockObjectResult) IsComplete() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsComplete") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsComplete indicates an expected call of IsComplete. +func (mr *MockObjectResultMockRecorder) IsComplete() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsComplete", reflect.TypeOf((*MockObjectResult)(nil).IsComplete)) +} + +// IsPaused mocks base method. +func (m *MockObjectResult) IsPaused() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsPaused") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsPaused indicates an expected call of IsPaused. +func (mr *MockObjectResultMockRecorder) IsPaused() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPaused", reflect.TypeOf((*MockObjectResult)(nil).IsPaused)) +} + +// Object mocks base method. +func (m *MockObjectResult) Object() machinery.Object { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Object") + ret0, _ := ret[0].(machinery.Object) + return ret0 +} + +// Object indicates an expected call of Object. +func (mr *MockObjectResultMockRecorder) Object() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Object", reflect.TypeOf((*MockObjectResult)(nil).Object)) +} + +// ProbeResults mocks base method. +func (m *MockObjectResult) ProbeResults() types.ProbeResultContainer { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProbeResults") + ret0, _ := ret[0].(types.ProbeResultContainer) + return ret0 +} + +// ProbeResults indicates an expected call of ProbeResults. +func (mr *MockObjectResultMockRecorder) ProbeResults() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProbeResults", reflect.TypeOf((*MockObjectResult)(nil).ProbeResults)) +} + +// String mocks base method. +func (m *MockObjectResult) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String. +func (mr *MockObjectResultMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockObjectResult)(nil).String)) +} + +// MockRevisionTeardownResult is a mock of RevisionTeardownResult interface. +type MockRevisionTeardownResult struct { + ctrl *gomock.Controller + recorder *MockRevisionTeardownResultMockRecorder + isgomock struct{} +} + +// MockRevisionTeardownResultMockRecorder is the mock recorder for MockRevisionTeardownResult. +type MockRevisionTeardownResultMockRecorder struct { + mock *MockRevisionTeardownResult +} + +// NewMockRevisionTeardownResult creates a new mock instance. +func NewMockRevisionTeardownResult(ctrl *gomock.Controller) *MockRevisionTeardownResult { + mock := &MockRevisionTeardownResult{ctrl: ctrl} + mock.recorder = &MockRevisionTeardownResultMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRevisionTeardownResult) EXPECT() *MockRevisionTeardownResultMockRecorder { + return m.recorder +} + +// GetActivePhaseName mocks base method. +func (m *MockRevisionTeardownResult) GetActivePhaseName() (string, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetActivePhaseName") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetActivePhaseName indicates an expected call of GetActivePhaseName. +func (mr *MockRevisionTeardownResultMockRecorder) GetActivePhaseName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActivePhaseName", reflect.TypeOf((*MockRevisionTeardownResult)(nil).GetActivePhaseName)) +} + +// GetGonePhaseNames mocks base method. +func (m *MockRevisionTeardownResult) GetGonePhaseNames() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGonePhaseNames") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetGonePhaseNames indicates an expected call of GetGonePhaseNames. +func (mr *MockRevisionTeardownResultMockRecorder) GetGonePhaseNames() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGonePhaseNames", reflect.TypeOf((*MockRevisionTeardownResult)(nil).GetGonePhaseNames)) +} + +// GetPhases mocks base method. +func (m *MockRevisionTeardownResult) GetPhases() []machinery.PhaseTeardownResult { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPhases") + ret0, _ := ret[0].([]machinery.PhaseTeardownResult) + return ret0 +} + +// GetPhases indicates an expected call of GetPhases. +func (mr *MockRevisionTeardownResultMockRecorder) GetPhases() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPhases", reflect.TypeOf((*MockRevisionTeardownResult)(nil).GetPhases)) +} + +// GetWaitingPhaseNames mocks base method. +func (m *MockRevisionTeardownResult) GetWaitingPhaseNames() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetWaitingPhaseNames") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetWaitingPhaseNames indicates an expected call of GetWaitingPhaseNames. +func (mr *MockRevisionTeardownResultMockRecorder) GetWaitingPhaseNames() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWaitingPhaseNames", reflect.TypeOf((*MockRevisionTeardownResult)(nil).GetWaitingPhaseNames)) +} + +// IsComplete mocks base method. +func (m *MockRevisionTeardownResult) IsComplete() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsComplete") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsComplete indicates an expected call of IsComplete. +func (mr *MockRevisionTeardownResultMockRecorder) IsComplete() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsComplete", reflect.TypeOf((*MockRevisionTeardownResult)(nil).IsComplete)) +} + +// String mocks base method. +func (m *MockRevisionTeardownResult) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String. +func (mr *MockRevisionTeardownResultMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockRevisionTeardownResult)(nil).String)) +} diff --git a/internal/testutil/mock/rbac/mock_rulesresolver.go b/internal/testutil/mock/rbac/mock_rulesresolver.go new file mode 100644 index 0000000000..091e0b662d --- /dev/null +++ b/internal/testutil/mock/rbac/mock_rulesresolver.go @@ -0,0 +1,86 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: k8s.io/kubernetes/pkg/registry/rbac/validation (interfaces: AuthorizationRuleResolver) +// +// Generated by this command: +// +// mockgen -destination=rbac/mock_rulesresolver.go -package=rbac k8s.io/kubernetes/pkg/registry/rbac/validation AuthorizationRuleResolver +// + +// Package rbac is a generated GoMock package. +package rbac + +import ( + context "context" + fmt "fmt" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + v1 "k8s.io/api/rbac/v1" + user "k8s.io/apiserver/pkg/authentication/user" +) + +// MockAuthorizationRuleResolver is a mock of AuthorizationRuleResolver interface. +type MockAuthorizationRuleResolver struct { + ctrl *gomock.Controller + recorder *MockAuthorizationRuleResolverMockRecorder + isgomock struct{} +} + +// MockAuthorizationRuleResolverMockRecorder is the mock recorder for MockAuthorizationRuleResolver. +type MockAuthorizationRuleResolverMockRecorder struct { + mock *MockAuthorizationRuleResolver +} + +// NewMockAuthorizationRuleResolver creates a new mock instance. +func NewMockAuthorizationRuleResolver(ctrl *gomock.Controller) *MockAuthorizationRuleResolver { + mock := &MockAuthorizationRuleResolver{ctrl: ctrl} + mock.recorder = &MockAuthorizationRuleResolverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAuthorizationRuleResolver) EXPECT() *MockAuthorizationRuleResolverMockRecorder { + return m.recorder +} + +// GetRoleReferenceRules mocks base method. +func (m *MockAuthorizationRuleResolver) GetRoleReferenceRules(ctx context.Context, roleRef v1.RoleRef, namespace string) ([]v1.PolicyRule, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRoleReferenceRules", ctx, roleRef, namespace) + ret0, _ := ret[0].([]v1.PolicyRule) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRoleReferenceRules indicates an expected call of GetRoleReferenceRules. +func (mr *MockAuthorizationRuleResolverMockRecorder) GetRoleReferenceRules(ctx, roleRef, namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRoleReferenceRules", reflect.TypeOf((*MockAuthorizationRuleResolver)(nil).GetRoleReferenceRules), ctx, roleRef, namespace) +} + +// RulesFor mocks base method. +func (m *MockAuthorizationRuleResolver) RulesFor(ctx context.Context, arg1 user.Info, namespace string) ([]v1.PolicyRule, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RulesFor", ctx, arg1, namespace) + ret0, _ := ret[0].([]v1.PolicyRule) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RulesFor indicates an expected call of RulesFor. +func (mr *MockAuthorizationRuleResolverMockRecorder) RulesFor(ctx, arg1, namespace any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RulesFor", reflect.TypeOf((*MockAuthorizationRuleResolver)(nil).RulesFor), ctx, arg1, namespace) +} + +// VisitRulesFor mocks base method. +func (m *MockAuthorizationRuleResolver) VisitRulesFor(ctx context.Context, arg1 user.Info, namespace string, visitor func(fmt.Stringer, *v1.PolicyRule, error) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "VisitRulesFor", ctx, arg1, namespace, visitor) +} + +// VisitRulesFor indicates an expected call of VisitRulesFor. +func (mr *MockAuthorizationRuleResolverMockRecorder) VisitRulesFor(ctx, arg1, namespace, visitor any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VisitRulesFor", reflect.TypeOf((*MockAuthorizationRuleResolver)(nil).VisitRulesFor), ctx, arg1, namespace, visitor) +} diff --git a/internal/testutil/mock/render/mock_certprovider.go b/internal/testutil/mock/render/mock_certprovider.go new file mode 100644 index 0000000000..696abcd71d --- /dev/null +++ b/internal/testutil/mock/render/mock_certprovider.go @@ -0,0 +1,86 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render (interfaces: CertificateProvider) +// +// Generated by this command: +// +// mockgen -destination=render/mock_certprovider.go -package=render github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render CertificateProvider +// + +// Package render is a generated GoMock package. +package render + +import ( + reflect "reflect" + + render "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render" + gomock "go.uber.org/mock/gomock" + unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + client "sigs.k8s.io/controller-runtime/pkg/client" +) + +// MockCertificateProvider is a mock of CertificateProvider interface. +type MockCertificateProvider struct { + ctrl *gomock.Controller + recorder *MockCertificateProviderMockRecorder + isgomock struct{} +} + +// MockCertificateProviderMockRecorder is the mock recorder for MockCertificateProvider. +type MockCertificateProviderMockRecorder struct { + mock *MockCertificateProvider +} + +// NewMockCertificateProvider creates a new mock instance. +func NewMockCertificateProvider(ctrl *gomock.Controller) *MockCertificateProvider { + mock := &MockCertificateProvider{ctrl: ctrl} + mock.recorder = &MockCertificateProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCertificateProvider) EXPECT() *MockCertificateProviderMockRecorder { + return m.recorder +} + +// AdditionalObjects mocks base method. +func (m *MockCertificateProvider) AdditionalObjects(cfg render.CertificateProvisionerConfig) ([]unstructured.Unstructured, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AdditionalObjects", cfg) + ret0, _ := ret[0].([]unstructured.Unstructured) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AdditionalObjects indicates an expected call of AdditionalObjects. +func (mr *MockCertificateProviderMockRecorder) AdditionalObjects(cfg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdditionalObjects", reflect.TypeOf((*MockCertificateProvider)(nil).AdditionalObjects), cfg) +} + +// GetCertSecretInfo mocks base method. +func (m *MockCertificateProvider) GetCertSecretInfo(cfg render.CertificateProvisionerConfig) render.CertSecretInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCertSecretInfo", cfg) + ret0, _ := ret[0].(render.CertSecretInfo) + return ret0 +} + +// GetCertSecretInfo indicates an expected call of GetCertSecretInfo. +func (mr *MockCertificateProviderMockRecorder) GetCertSecretInfo(cfg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertSecretInfo", reflect.TypeOf((*MockCertificateProvider)(nil).GetCertSecretInfo), cfg) +} + +// InjectCABundle mocks base method. +func (m *MockCertificateProvider) InjectCABundle(obj client.Object, cfg render.CertificateProvisionerConfig) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InjectCABundle", obj, cfg) + ret0, _ := ret[0].(error) + return ret0 +} + +// InjectCABundle indicates an expected call of InjectCABundle. +func (mr *MockCertificateProviderMockRecorder) InjectCABundle(obj, cfg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InjectCABundle", reflect.TypeOf((*MockCertificateProvider)(nil).InjectCABundle), obj, cfg) +} diff --git a/internal/testutil/mock/storage/mock_instance.go b/internal/testutil/mock/storage/mock_instance.go new file mode 100644 index 0000000000..2aa9613e3b --- /dev/null +++ b/internal/testutil/mock/storage/mock_instance.go @@ -0,0 +1,113 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/operator-framework/operator-controller/internal/catalogd/storage (interfaces: Instance) +// +// Generated by this command: +// +// mockgen -destination=storage/mock_instance.go -package=storage github.com/operator-framework/operator-controller/internal/catalogd/storage Instance +// + +// Package storage is a generated GoMock package. +package storage + +import ( + context "context" + fs "io/fs" + http "net/http" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockInstance is a mock of Instance interface. +type MockInstance struct { + ctrl *gomock.Controller + recorder *MockInstanceMockRecorder + isgomock struct{} +} + +// MockInstanceMockRecorder is the mock recorder for MockInstance. +type MockInstanceMockRecorder struct { + mock *MockInstance +} + +// NewMockInstance creates a new mock instance. +func NewMockInstance(ctrl *gomock.Controller) *MockInstance { + mock := &MockInstance{ctrl: ctrl} + mock.recorder = &MockInstanceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockInstance) EXPECT() *MockInstanceMockRecorder { + return m.recorder +} + +// BaseURL mocks base method. +func (m *MockInstance) BaseURL(catalog string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BaseURL", catalog) + ret0, _ := ret[0].(string) + return ret0 +} + +// BaseURL indicates an expected call of BaseURL. +func (mr *MockInstanceMockRecorder) BaseURL(catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BaseURL", reflect.TypeOf((*MockInstance)(nil).BaseURL), catalog) +} + +// ContentExists mocks base method. +func (m *MockInstance) ContentExists(catalog string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ContentExists", catalog) + ret0, _ := ret[0].(bool) + return ret0 +} + +// ContentExists indicates an expected call of ContentExists. +func (mr *MockInstanceMockRecorder) ContentExists(catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentExists", reflect.TypeOf((*MockInstance)(nil).ContentExists), catalog) +} + +// Delete mocks base method. +func (m *MockInstance) Delete(catalog string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", catalog) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockInstanceMockRecorder) Delete(catalog any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockInstance)(nil).Delete), catalog) +} + +// StorageServerHandler mocks base method. +func (m *MockInstance) StorageServerHandler() http.Handler { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StorageServerHandler") + ret0, _ := ret[0].(http.Handler) + return ret0 +} + +// StorageServerHandler indicates an expected call of StorageServerHandler. +func (mr *MockInstanceMockRecorder) StorageServerHandler() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageServerHandler", reflect.TypeOf((*MockInstance)(nil).StorageServerHandler)) +} + +// Store mocks base method. +func (m *MockInstance) Store(ctx context.Context, catalog string, fsys fs.FS) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Store", ctx, catalog, fsys) + ret0, _ := ret[0].(error) + return ret0 +} + +// Store indicates an expected call of Store. +func (mr *MockInstanceMockRecorder) Store(ctx, catalog, fsys any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Store", reflect.TypeOf((*MockInstance)(nil).Store), ctx, catalog, fsys) +} diff --git a/openshift/tests-extension/go.sum b/openshift/tests-extension/go.sum index 73abc81fba..cb9e5004a8 100644 --- a/openshift/tests-extension/go.sum +++ b/openshift/tests-extension/go.sum @@ -186,8 +186,8 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= diff --git a/vendor/github.com/stretchr/objx/.codeclimate.yml b/vendor/github.com/stretchr/objx/.codeclimate.yml deleted file mode 100644 index 559fa399c1..0000000000 --- a/vendor/github.com/stretchr/objx/.codeclimate.yml +++ /dev/null @@ -1,21 +0,0 @@ -engines: - gofmt: - enabled: true - golint: - enabled: true - govet: - enabled: true - -exclude_patterns: -- ".github/" -- "vendor/" -- "codegen/" -- "*.yml" -- ".*.yml" -- "*.md" -- "Gopkg.*" -- "doc.go" -- "type_specific_codegen_test.go" -- "type_specific_codegen.go" -- ".gitignore" -- "LICENSE" diff --git a/vendor/github.com/stretchr/objx/.gitignore b/vendor/github.com/stretchr/objx/.gitignore deleted file mode 100644 index ea58090bd2..0000000000 --- a/vendor/github.com/stretchr/objx/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.dll -*.so -*.dylib - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out diff --git a/vendor/github.com/stretchr/objx/LICENSE b/vendor/github.com/stretchr/objx/LICENSE deleted file mode 100644 index 44d4d9d5a7..0000000000 --- a/vendor/github.com/stretchr/objx/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2014 Stretchr, Inc. -Copyright (c) 2017-2018 objx contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md deleted file mode 100644 index e9ba830735..0000000000 --- a/vendor/github.com/stretchr/objx/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Objx -[![Build Status](https://travis-ci.org/stretchr/objx.svg?branch=master)](https://travis-ci.org/stretchr/objx) -[![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/objx)](https://goreportcard.com/report/github.com/stretchr/objx) -[![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx) -[![GoDoc](https://pkg.go.dev/badge/github.com/stretchr/objx?utm_source=godoc)](https://pkg.go.dev/github.com/stretchr/objx) - -Objx - Go package for dealing with maps, slices, JSON and other data. - -Get started: - -- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) -- Check out the API Documentation http://pkg.go.dev/github.com/stretchr/objx - -## Overview -Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. - -### Pattern -Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: - -```go -m, err := objx.FromJSON(json) -``` - -NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. - -Use `Get` to access the value you're interested in. You can use dot and array -notation too: - -```go -m.Get("places[0].latlng") -``` - -Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. - -```go -if m.Get("code").IsStr() { // Your code... } -``` - -Or you can just assume the type, and use one of the strong type methods to extract the real value: - -```go -m.Get("code").Int() -``` - -If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. - -```go -Get("code").Int(-1) -``` -If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. - -### Reading data -A simple example of how to use Objx: - -```go -// Use MustFromJSON to make an objx.Map from some JSON -m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) - -// Get the details -name := m.Get("name").Str() -age := m.Get("age").Int() - -// Get their nickname (or use their name if they don't have one) -nickname := m.Get("nickname").Str(name) -``` - -### Ranging -Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: - -```go -m := objx.MustFromJSON(json) -for key, value := range m { - // Your code... -} -``` - -## Installation -To install Objx, use go get: - - go get github.com/stretchr/objx - -### Staying up to date -To update Objx to the latest version, run: - - go get -u github.com/stretchr/objx - -### Supported go versions -We currently support the three recent major Go versions. - -## Contributing -Please feel free to submit issues, fork the repository and send pull requests! diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml deleted file mode 100644 index 8a79e8d674..0000000000 --- a/vendor/github.com/stretchr/objx/Taskfile.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: '3' - -tasks: - default: - deps: [test] - - lint: - desc: Checks code style - cmds: - - gofmt -d -s *.go - - go vet ./... - silent: true - - lint-fix: - desc: Fixes code style - cmds: - - gofmt -w -s *.go - - test: - desc: Runs go tests - cmds: - - go test -race ./... - - test-coverage: - desc: Runs go tests and calculates test coverage - cmds: - - go test -race -coverprofile=c.out ./... diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go deleted file mode 100644 index 72f1d1c1ce..0000000000 --- a/vendor/github.com/stretchr/objx/accessors.go +++ /dev/null @@ -1,197 +0,0 @@ -package objx - -import ( - "reflect" - "regexp" - "strconv" - "strings" -) - -const ( - // PathSeparator is the character used to separate the elements - // of the keypath. - // - // For example, `location.address.city` - PathSeparator string = "." - - // arrayAccessRegexString is the regex used to extract the array number - // from the access path - arrayAccessRegexString = `^(.+)\[([0-9]+)\]$` - - // mapAccessRegexString is the regex used to extract the map key - // from the access path - mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$` -) - -// arrayAccessRegex is the compiled arrayAccessRegexString -var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString) - -// mapAccessRegex is the compiled mapAccessRegexString -var mapAccessRegex = regexp.MustCompile(mapAccessRegexString) - -// Get gets the value using the specified selector and -// returns it inside a new Obj object. -// -// If it cannot find the value, Get will return a nil -// value inside an instance of Obj. -// -// Get can only operate directly on map[string]interface{} and []interface. -// -// # Example -// -// To access the title of the third chapter of the second book, do: -// -// o.Get("books[1].chapters[2].title") -func (m Map) Get(selector string) *Value { - rawObj := access(m, selector, nil, false) - return &Value{data: rawObj} -} - -// Set sets the value using the specified selector and -// returns the object on which Set was called. -// -// Set can only operate directly on map[string]interface{} and []interface -// -// # Example -// -// To set the title of the third chapter of the second book, do: -// -// o.Set("books[1].chapters[2].title","Time to Go") -func (m Map) Set(selector string, value interface{}) Map { - access(m, selector, value, true) - return m -} - -// getIndex returns the index, which is hold in s by two branches. -// It also returns s without the index part, e.g. name[1] will return (1, name). -// If no index is found, -1 is returned -func getIndex(s string) (int, string) { - arrayMatches := arrayAccessRegex.FindStringSubmatch(s) - if len(arrayMatches) > 0 { - // Get the key into the map - selector := arrayMatches[1] - // Get the index into the array at the key - // We know this can't fail because arrayMatches[2] is an int for sure - index, _ := strconv.Atoi(arrayMatches[2]) - return index, selector - } - return -1, s -} - -// getKey returns the key which is held in s by two brackets. -// It also returns the next selector. -func getKey(s string) (string, string) { - selSegs := strings.SplitN(s, PathSeparator, 2) - thisSel := selSegs[0] - nextSel := "" - - if len(selSegs) > 1 { - nextSel = selSegs[1] - } - - mapMatches := mapAccessRegex.FindStringSubmatch(s) - if len(mapMatches) > 0 { - if _, err := strconv.Atoi(mapMatches[2]); err != nil { - thisSel = mapMatches[1] - nextSel = "[" + mapMatches[2] + "]" + mapMatches[3] - - if thisSel == "" { - thisSel = mapMatches[2] - nextSel = mapMatches[3] - } - - if nextSel == "" { - selSegs = []string{"", ""} - } else if nextSel[0] == '.' { - nextSel = nextSel[1:] - } - } - } - - return thisSel, nextSel -} - -// access accesses the object using the selector and performs the -// appropriate action. -func access(current interface{}, selector string, value interface{}, isSet bool) interface{} { - thisSel, nextSel := getKey(selector) - - indexes := []int{} - for strings.Contains(thisSel, "[") { - prevSel := thisSel - index := -1 - index, thisSel = getIndex(thisSel) - indexes = append(indexes, index) - if prevSel == thisSel { - break - } - } - - if curMap, ok := current.(Map); ok { - current = map[string]interface{}(curMap) - } - // get the object in question - switch current.(type) { - case map[string]interface{}: - curMSI := current.(map[string]interface{}) - if nextSel == "" && isSet { - curMSI[thisSel] = value - return nil - } - - _, ok := curMSI[thisSel].(map[string]interface{}) - if !ok { - _, ok = curMSI[thisSel].(Map) - } - - if (curMSI[thisSel] == nil || !ok) && len(indexes) == 0 && isSet { - curMSI[thisSel] = map[string]interface{}{} - } - - current = curMSI[thisSel] - default: - current = nil - } - - // do we need to access the item of an array? - if len(indexes) > 0 { - num := len(indexes) - for num > 0 { - num-- - index := indexes[num] - indexes = indexes[:num] - if array, ok := interSlice(current); ok { - if index < len(array) { - current = array[index] - } else { - current = nil - break - } - } - } - } - - if nextSel != "" { - current = access(current, nextSel, value, isSet) - } - return current -} - -func interSlice(slice interface{}) ([]interface{}, bool) { - if array, ok := slice.([]interface{}); ok { - return array, ok - } - - s := reflect.ValueOf(slice) - if s.Kind() != reflect.Slice { - return nil, false - } - - ret := make([]interface{}, s.Len()) - - for i := 0; i < s.Len(); i++ { - ret[i] = s.Index(i).Interface() - } - - return ret, true -} diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go deleted file mode 100644 index 01c63d7d3b..0000000000 --- a/vendor/github.com/stretchr/objx/conversions.go +++ /dev/null @@ -1,280 +0,0 @@ -package objx - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "net/url" - "strconv" -) - -// SignatureSeparator is the character that is used to -// separate the Base64 string from the security signature. -const SignatureSeparator = "_" - -// URLValuesSliceKeySuffix is the character that is used to -// specify a suffix for slices parsed by URLValues. -// If the suffix is set to "[i]", then the index of the slice -// is used in place of i -// Ex: Suffix "[]" would have the form a[]=b&a[]=c -// OR Suffix "[i]" would have the form a[0]=b&a[1]=c -// OR Suffix "" would have the form a=b&a=c -var urlValuesSliceKeySuffix = "[]" - -const ( - URLValuesSliceKeySuffixEmpty = "" - URLValuesSliceKeySuffixArray = "[]" - URLValuesSliceKeySuffixIndex = "[i]" -) - -// SetURLValuesSliceKeySuffix sets the character that is used to -// specify a suffix for slices parsed by URLValues. -// If the suffix is set to "[i]", then the index of the slice -// is used in place of i -// Ex: Suffix "[]" would have the form a[]=b&a[]=c -// OR Suffix "[i]" would have the form a[0]=b&a[1]=c -// OR Suffix "" would have the form a=b&a=c -func SetURLValuesSliceKeySuffix(s string) error { - if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex { - urlValuesSliceKeySuffix = s - return nil - } - - return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.") -} - -// JSON converts the contained object to a JSON string -// representation -func (m Map) JSON() (string, error) { - for k, v := range m { - m[k] = cleanUp(v) - } - - result, err := json.Marshal(m) - if err != nil { - err = errors.New("objx: JSON encode failed with: " + err.Error()) - } - return string(result), err -} - -func cleanUpInterfaceArray(in []interface{}) []interface{} { - result := make([]interface{}, len(in)) - for i, v := range in { - result[i] = cleanUp(v) - } - return result -} - -func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { - result := Map{} - for k, v := range in { - result[fmt.Sprintf("%v", k)] = cleanUp(v) - } - return result -} - -func cleanUpStringMap(in map[string]interface{}) Map { - result := Map{} - for k, v := range in { - result[k] = cleanUp(v) - } - return result -} - -func cleanUpMSIArray(in []map[string]interface{}) []Map { - result := make([]Map, len(in)) - for i, v := range in { - result[i] = cleanUpStringMap(v) - } - return result -} - -func cleanUpMapArray(in []Map) []Map { - result := make([]Map, len(in)) - for i, v := range in { - result[i] = cleanUpStringMap(v) - } - return result -} - -func cleanUp(v interface{}) interface{} { - switch v := v.(type) { - case []interface{}: - return cleanUpInterfaceArray(v) - case []map[string]interface{}: - return cleanUpMSIArray(v) - case map[interface{}]interface{}: - return cleanUpInterfaceMap(v) - case Map: - return cleanUpStringMap(v) - case []Map: - return cleanUpMapArray(v) - default: - return v - } -} - -// MustJSON converts the contained object to a JSON string -// representation and panics if there is an error -func (m Map) MustJSON() string { - result, err := m.JSON() - if err != nil { - panic(err.Error()) - } - return result -} - -// Base64 converts the contained object to a Base64 string -// representation of the JSON string representation -func (m Map) Base64() (string, error) { - var buf bytes.Buffer - - jsonData, err := m.JSON() - if err != nil { - return "", err - } - - encoder := base64.NewEncoder(base64.StdEncoding, &buf) - _, _ = encoder.Write([]byte(jsonData)) - _ = encoder.Close() - - return buf.String(), nil -} - -// MustBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and panics -// if there is an error -func (m Map) MustBase64() string { - result, err := m.Base64() - if err != nil { - panic(err.Error()) - } - return result -} - -// SignedBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and signs it -// using the provided key. -func (m Map) SignedBase64(key string) (string, error) { - base64, err := m.Base64() - if err != nil { - return "", err - } - - sig := HashWithKey(base64, key) - return base64 + SignatureSeparator + sig, nil -} - -// MustSignedBase64 converts the contained object to a Base64 string -// representation of the JSON string representation and signs it -// using the provided key and panics if there is an error -func (m Map) MustSignedBase64(key string) string { - result, err := m.SignedBase64(key) - if err != nil { - panic(err.Error()) - } - return result -} - -/* - URL Query - ------------------------------------------------ -*/ - -// URLValues creates a url.Values object from an Obj. This -// function requires that the wrapped object be a map[string]interface{} -func (m Map) URLValues() url.Values { - vals := make(url.Values) - - m.parseURLValues(m, vals, "") - - return vals -} - -func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) { - useSliceIndex := false - if urlValuesSliceKeySuffix == "[i]" { - useSliceIndex = true - } - - for k, v := range queryMap { - val := &Value{data: v} - switch { - case val.IsObjxMap(): - if key == "" { - m.parseURLValues(val.ObjxMap(), vals, k) - } else { - m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]") - } - case val.IsObjxMapSlice(): - sliceKey := k - if key != "" { - sliceKey = key + "[" + k + "]" - } - - if useSliceIndex { - for i, sv := range val.MustObjxMapSlice() { - sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" - m.parseURLValues(sv, vals, sk) - } - } else { - sliceKey = sliceKey + urlValuesSliceKeySuffix - for _, sv := range val.MustObjxMapSlice() { - m.parseURLValues(sv, vals, sliceKey) - } - } - case val.IsMSISlice(): - sliceKey := k - if key != "" { - sliceKey = key + "[" + k + "]" - } - - if useSliceIndex { - for i, sv := range val.MustMSISlice() { - sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" - m.parseURLValues(New(sv), vals, sk) - } - } else { - sliceKey = sliceKey + urlValuesSliceKeySuffix - for _, sv := range val.MustMSISlice() { - m.parseURLValues(New(sv), vals, sliceKey) - } - } - case val.IsStrSlice(), val.IsBoolSlice(), - val.IsFloat32Slice(), val.IsFloat64Slice(), - val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(), - val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice(): - - sliceKey := k - if key != "" { - sliceKey = key + "[" + k + "]" - } - - if useSliceIndex { - for i, sv := range val.StringSlice() { - sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" - vals.Set(sk, sv) - } - } else { - sliceKey = sliceKey + urlValuesSliceKeySuffix - vals[sliceKey] = val.StringSlice() - } - - default: - if key == "" { - vals.Set(k, val.String()) - } else { - vals.Set(key+"["+k+"]", val.String()) - } - } - } -} - -// URLQuery gets an encoded URL query representing the given -// Obj. This function requires that the wrapped object be a -// map[string]interface{} -func (m Map) URLQuery() (string, error) { - return m.URLValues().Encode(), nil -} diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go deleted file mode 100644 index b170af74b3..0000000000 --- a/vendor/github.com/stretchr/objx/doc.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Package objx provides utilities for dealing with maps, slices, JSON and other data. - -# Overview - -Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes -a powerful `Get` method (among others) that allows you to easily and quickly get -access to data within the map, without having to worry too much about type assertions, -missing data, default values etc. - -# Pattern - -Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. -Call one of the `objx.` functions to create your `objx.Map` to get going: - - m, err := objx.FromJSON(json) - -NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, -the rest will be optimistic and try to figure things out without panicking. - -Use `Get` to access the value you're interested in. You can use dot and array -notation too: - - m.Get("places[0].latlng") - -Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. - - if m.Get("code").IsStr() { // Your code... } - -Or you can just assume the type, and use one of the strong type methods to extract the real value: - - m.Get("code").Int() - -If there's no value there (or if it's the wrong type) then a default value will be returned, -or you can be explicit about the default value. - - Get("code").Int(-1) - -If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, -manipulating and selecting that data. You can find out more by exploring the index below. - -# Reading data - -A simple example of how to use Objx: - - // Use MustFromJSON to make an objx.Map from some JSON - m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) - - // Get the details - name := m.Get("name").Str() - age := m.Get("age").Int() - - // Get their nickname (or use their name if they don't have one) - nickname := m.Get("nickname").Str(name) - -# Ranging - -Since `objx.Map` is a `map[string]interface{}` you can treat it as such. -For example, to `range` the data, do what you would expect: - - m := objx.MustFromJSON(json) - for key, value := range m { - // Your code... - } -*/ -package objx diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go deleted file mode 100644 index ab9f9ae67c..0000000000 --- a/vendor/github.com/stretchr/objx/map.go +++ /dev/null @@ -1,214 +0,0 @@ -package objx - -import ( - "encoding/base64" - "encoding/json" - "errors" - "io/ioutil" - "net/url" - "strings" -) - -// MSIConvertable is an interface that defines methods for converting your -// custom types to a map[string]interface{} representation. -type MSIConvertable interface { - // MSI gets a map[string]interface{} (msi) representing the - // object. - MSI() map[string]interface{} -} - -// Map provides extended functionality for working with -// untyped data, in particular map[string]interface (msi). -type Map map[string]interface{} - -// Value returns the internal value instance -func (m Map) Value() *Value { - return &Value{data: m} -} - -// Nil represents a nil Map. -var Nil = New(nil) - -// New creates a new Map containing the map[string]interface{} in the data argument. -// If the data argument is not a map[string]interface, New attempts to call the -// MSI() method on the MSIConvertable interface to create one. -func New(data interface{}) Map { - if _, ok := data.(map[string]interface{}); !ok { - if converter, ok := data.(MSIConvertable); ok { - data = converter.MSI() - } else { - return nil - } - } - return Map(data.(map[string]interface{})) -} - -// MSI creates a map[string]interface{} and puts it inside a new Map. -// -// The arguments follow a key, value pattern. -// -// Returns nil if any key argument is non-string or if there are an odd number of arguments. -// -// # Example -// -// To easily create Maps: -// -// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) -// -// // creates an Map equivalent to -// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} -func MSI(keyAndValuePairs ...interface{}) Map { - newMap := Map{} - keyAndValuePairsLen := len(keyAndValuePairs) - if keyAndValuePairsLen%2 != 0 { - return nil - } - for i := 0; i < keyAndValuePairsLen; i = i + 2 { - key := keyAndValuePairs[i] - value := keyAndValuePairs[i+1] - - // make sure the key is a string - keyString, keyStringOK := key.(string) - if !keyStringOK { - return nil - } - newMap[keyString] = value - } - return newMap -} - -// ****** Conversion Constructors - -// MustFromJSON creates a new Map containing the data specified in the -// jsonString. -// -// Panics if the JSON is invalid. -func MustFromJSON(jsonString string) Map { - o, err := FromJSON(jsonString) - if err != nil { - panic("objx: MustFromJSON failed with error: " + err.Error()) - } - return o -} - -// MustFromJSONSlice creates a new slice of Map containing the data specified in the -// jsonString. Works with jsons with a top level array -// -// Panics if the JSON is invalid. -func MustFromJSONSlice(jsonString string) []Map { - slice, err := FromJSONSlice(jsonString) - if err != nil { - panic("objx: MustFromJSONSlice failed with error: " + err.Error()) - } - return slice -} - -// FromJSON creates a new Map containing the data specified in the -// jsonString. -// -// Returns an error if the JSON is invalid. -func FromJSON(jsonString string) (Map, error) { - var m Map - err := json.Unmarshal([]byte(jsonString), &m) - if err != nil { - return Nil, err - } - return m, nil -} - -// FromJSONSlice creates a new slice of Map containing the data specified in the -// jsonString. Works with jsons with a top level array -// -// Returns an error if the JSON is invalid. -func FromJSONSlice(jsonString string) ([]Map, error) { - var slice []Map - err := json.Unmarshal([]byte(jsonString), &slice) - if err != nil { - return nil, err - } - return slice, nil -} - -// FromBase64 creates a new Obj containing the data specified -// in the Base64 string. -// -// The string is an encoded JSON string returned by Base64 -func FromBase64(base64String string) (Map, error) { - decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) - decoded, err := ioutil.ReadAll(decoder) - if err != nil { - return nil, err - } - return FromJSON(string(decoded)) -} - -// MustFromBase64 creates a new Obj containing the data specified -// in the Base64 string and panics if there is an error. -// -// The string is an encoded JSON string returned by Base64 -func MustFromBase64(base64String string) Map { - result, err := FromBase64(base64String) - if err != nil { - panic("objx: MustFromBase64 failed with error: " + err.Error()) - } - return result -} - -// FromSignedBase64 creates a new Obj containing the data specified -// in the Base64 string. -// -// The string is an encoded JSON string returned by SignedBase64 -func FromSignedBase64(base64String, key string) (Map, error) { - parts := strings.Split(base64String, SignatureSeparator) - if len(parts) != 2 { - return nil, errors.New("objx: Signed base64 string is malformed") - } - - sig := HashWithKey(parts[0], key) - if parts[1] != sig { - return nil, errors.New("objx: Signature for base64 data does not match") - } - return FromBase64(parts[0]) -} - -// MustFromSignedBase64 creates a new Obj containing the data specified -// in the Base64 string and panics if there is an error. -// -// The string is an encoded JSON string returned by Base64 -func MustFromSignedBase64(base64String, key string) Map { - result, err := FromSignedBase64(base64String, key) - if err != nil { - panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) - } - return result -} - -// FromURLQuery generates a new Obj by parsing the specified -// query. -// -// For queries with multiple values, the first value is selected. -func FromURLQuery(query string) (Map, error) { - vals, err := url.ParseQuery(query) - if err != nil { - return nil, err - } - m := Map{} - for k, vals := range vals { - m[k] = vals[0] - } - return m, nil -} - -// MustFromURLQuery generates a new Obj by parsing the specified -// query. -// -// For queries with multiple values, the first value is selected. -// -// Panics if it encounters an error -func MustFromURLQuery(query string) Map { - o, err := FromURLQuery(query) - if err != nil { - panic("objx: MustFromURLQuery failed with error: " + err.Error()) - } - return o -} diff --git a/vendor/github.com/stretchr/objx/mutations.go b/vendor/github.com/stretchr/objx/mutations.go deleted file mode 100644 index c3400a3f70..0000000000 --- a/vendor/github.com/stretchr/objx/mutations.go +++ /dev/null @@ -1,77 +0,0 @@ -package objx - -// Exclude returns a new Map with the keys in the specified []string -// excluded. -func (m Map) Exclude(exclude []string) Map { - excluded := make(Map) - for k, v := range m { - if !contains(exclude, k) { - excluded[k] = v - } - } - return excluded -} - -// Copy creates a shallow copy of the Obj. -func (m Map) Copy() Map { - copied := Map{} - for k, v := range m { - copied[k] = v - } - return copied -} - -// Merge blends the specified map with a copy of this map and returns the result. -// -// Keys that appear in both will be selected from the specified map. -// This method requires that the wrapped object be a map[string]interface{} -func (m Map) Merge(merge Map) Map { - return m.Copy().MergeHere(merge) -} - -// MergeHere blends the specified map with this map and returns the current map. -// -// Keys that appear in both will be selected from the specified map. The original map -// will be modified. This method requires that -// the wrapped object be a map[string]interface{} -func (m Map) MergeHere(merge Map) Map { - for k, v := range merge { - m[k] = v - } - return m -} - -// Transform builds a new Obj giving the transformer a chance -// to change the keys and values as it goes. This method requires that -// the wrapped object be a map[string]interface{} -func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { - newMap := Map{} - for k, v := range m { - modifiedKey, modifiedVal := transformer(k, v) - newMap[modifiedKey] = modifiedVal - } - return newMap -} - -// TransformKeys builds a new map using the specified key mapping. -// -// Unspecified keys will be unaltered. -// This method requires that the wrapped object be a map[string]interface{} -func (m Map) TransformKeys(mapping map[string]string) Map { - return m.Transform(func(key string, value interface{}) (string, interface{}) { - if newKey, ok := mapping[key]; ok { - return newKey, value - } - return key, value - }) -} - -// Checks if a string slice contains a string -func contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} diff --git a/vendor/github.com/stretchr/objx/security.go b/vendor/github.com/stretchr/objx/security.go deleted file mode 100644 index 692be8e2a9..0000000000 --- a/vendor/github.com/stretchr/objx/security.go +++ /dev/null @@ -1,12 +0,0 @@ -package objx - -import ( - "crypto/sha1" - "encoding/hex" -) - -// HashWithKey hashes the specified string using the security key -func HashWithKey(data, key string) string { - d := sha1.Sum([]byte(data + ":" + key)) - return hex.EncodeToString(d[:]) -} diff --git a/vendor/github.com/stretchr/objx/tests.go b/vendor/github.com/stretchr/objx/tests.go deleted file mode 100644 index d9e0b479a4..0000000000 --- a/vendor/github.com/stretchr/objx/tests.go +++ /dev/null @@ -1,17 +0,0 @@ -package objx - -// Has gets whether there is something at the specified selector -// or not. -// -// If m is nil, Has will always return false. -func (m Map) Has(selector string) bool { - if m == nil { - return false - } - return !m.Get(selector).IsNil() -} - -// IsNil gets whether the data is nil or not. -func (v *Value) IsNil() bool { - return v == nil || v.data == nil -} diff --git a/vendor/github.com/stretchr/objx/type_specific.go b/vendor/github.com/stretchr/objx/type_specific.go deleted file mode 100644 index 80f88d9fa2..0000000000 --- a/vendor/github.com/stretchr/objx/type_specific.go +++ /dev/null @@ -1,346 +0,0 @@ -package objx - -/* - MSI (map[string]interface{} and []map[string]interface{}) -*/ - -// MSI gets the value as a map[string]interface{}, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} { - if s, ok := v.data.(map[string]interface{}); ok { - return s - } - if s, ok := v.data.(Map); ok { - return map[string]interface{}(s) - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustMSI gets the value as a map[string]interface{}. -// -// Panics if the object is not a map[string]interface{}. -func (v *Value) MustMSI() map[string]interface{} { - if s, ok := v.data.(Map); ok { - return map[string]interface{}(s) - } - return v.data.(map[string]interface{}) -} - -// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault -// value or nil if the value is not a []map[string]interface{}. -func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} { - if s, ok := v.data.([]map[string]interface{}); ok { - return s - } - - s := v.ObjxMapSlice() - if s == nil { - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil - } - - result := make([]map[string]interface{}, len(s)) - for i := range s { - result[i] = s[i].Value().MSI() - } - return result -} - -// MustMSISlice gets the value as a []map[string]interface{}. -// -// Panics if the object is not a []map[string]interface{}. -func (v *Value) MustMSISlice() []map[string]interface{} { - if s := v.MSISlice(); s != nil { - return s - } - - return v.data.([]map[string]interface{}) -} - -// IsMSI gets whether the object contained is a map[string]interface{} or not. -func (v *Value) IsMSI() bool { - _, ok := v.data.(map[string]interface{}) - if !ok { - _, ok = v.data.(Map) - } - return ok -} - -// IsMSISlice gets whether the object contained is a []map[string]interface{} or not. -func (v *Value) IsMSISlice() bool { - _, ok := v.data.([]map[string]interface{}) - if !ok { - _, ok = v.data.([]Map) - if !ok { - s, ok := v.data.([]interface{}) - if ok { - for i := range s { - switch s[i].(type) { - case Map: - case map[string]interface{}: - default: - return false - } - } - return true - } - } - } - return ok -} - -// EachMSI calls the specified callback for each object -// in the []map[string]interface{}. -// -// Panics if the object is the wrong type. -func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value { - for index, val := range v.MustMSISlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereMSI uses the specified decider function to select items -// from the []map[string]interface{}. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value { - var selected []map[string]interface{} - v.EachMSI(func(index int, val map[string]interface{}) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupMSI uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]map[string]interface{}. -func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value { - groups := make(map[string][]map[string]interface{}) - v.EachMSI(func(index int, val map[string]interface{}) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]map[string]interface{}, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceMSI uses the specified function to replace each map[string]interface{}s -// by iterating each item. The data in the returned result will be a -// []map[string]interface{} containing the replaced items. -func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value { - arr := v.MustMSISlice() - replaced := make([]map[string]interface{}, len(arr)) - v.EachMSI(func(index int, val map[string]interface{}) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectMSI uses the specified collector function to collect a value -// for each of the map[string]interface{}s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value { - arr := v.MustMSISlice() - collected := make([]interface{}, len(arr)) - v.EachMSI(func(index int, val map[string]interface{}) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - ObjxMap ((Map) and [](Map)) -*/ - -// ObjxMap gets the value as a (Map), returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) ObjxMap(optionalDefault ...(Map)) Map { - if s, ok := v.data.((Map)); ok { - return s - } - if s, ok := v.data.(map[string]interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return New(nil) -} - -// MustObjxMap gets the value as a (Map). -// -// Panics if the object is not a (Map). -func (v *Value) MustObjxMap() Map { - if s, ok := v.data.(map[string]interface{}); ok { - return s - } - return v.data.((Map)) -} - -// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault -// value or nil if the value is not a [](Map). -func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) { - if s, ok := v.data.([]Map); ok { - return s - } - - if s, ok := v.data.([]map[string]interface{}); ok { - result := make([]Map, len(s)) - for i := range s { - result[i] = s[i] - } - return result - } - - s, ok := v.data.([]interface{}) - if !ok { - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil - } - - result := make([]Map, len(s)) - for i := range s { - switch s[i].(type) { - case Map: - result[i] = s[i].(Map) - case map[string]interface{}: - result[i] = New(s[i]) - default: - return nil - } - } - return result -} - -// MustObjxMapSlice gets the value as a [](Map). -// -// Panics if the object is not a [](Map). -func (v *Value) MustObjxMapSlice() [](Map) { - if s := v.ObjxMapSlice(); s != nil { - return s - } - return v.data.([](Map)) -} - -// IsObjxMap gets whether the object contained is a (Map) or not. -func (v *Value) IsObjxMap() bool { - _, ok := v.data.((Map)) - if !ok { - _, ok = v.data.(map[string]interface{}) - } - return ok -} - -// IsObjxMapSlice gets whether the object contained is a [](Map) or not. -func (v *Value) IsObjxMapSlice() bool { - _, ok := v.data.([](Map)) - if !ok { - _, ok = v.data.([]map[string]interface{}) - if !ok { - s, ok := v.data.([]interface{}) - if ok { - for i := range s { - switch s[i].(type) { - case Map: - case map[string]interface{}: - default: - return false - } - } - return true - } - } - } - - return ok -} - -// EachObjxMap calls the specified callback for each object -// in the [](Map). -// -// Panics if the object is the wrong type. -func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value { - for index, val := range v.MustObjxMapSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereObjxMap uses the specified decider function to select items -// from the [](Map). The object contained in the result will contain -// only the selected items. -func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value { - var selected [](Map) - v.EachObjxMap(func(index int, val Map) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupObjxMap uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][](Map). -func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value { - groups := make(map[string][](Map)) - v.EachObjxMap(func(index int, val Map) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([](Map), 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceObjxMap uses the specified function to replace each (Map)s -// by iterating each item. The data in the returned result will be a -// [](Map) containing the replaced items. -func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value { - arr := v.MustObjxMapSlice() - replaced := make([](Map), len(arr)) - v.EachObjxMap(func(index int, val Map) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectObjxMap uses the specified collector function to collect a value -// for each of the (Map)s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value { - arr := v.MustObjxMapSlice() - collected := make([]interface{}, len(arr)) - v.EachObjxMap(func(index int, val Map) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen.go b/vendor/github.com/stretchr/objx/type_specific_codegen.go deleted file mode 100644 index 45850456e1..0000000000 --- a/vendor/github.com/stretchr/objx/type_specific_codegen.go +++ /dev/null @@ -1,2261 +0,0 @@ -package objx - -/* - Inter (interface{} and []interface{}) -*/ - -// Inter gets the value as a interface{}, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Inter(optionalDefault ...interface{}) interface{} { - if s, ok := v.data.(interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInter gets the value as a interface{}. -// -// Panics if the object is not a interface{}. -func (v *Value) MustInter() interface{} { - return v.data.(interface{}) -} - -// InterSlice gets the value as a []interface{}, returns the optionalDefault -// value or nil if the value is not a []interface{}. -func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} { - if s, ok := v.data.([]interface{}); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInterSlice gets the value as a []interface{}. -// -// Panics if the object is not a []interface{}. -func (v *Value) MustInterSlice() []interface{} { - return v.data.([]interface{}) -} - -// IsInter gets whether the object contained is a interface{} or not. -func (v *Value) IsInter() bool { - _, ok := v.data.(interface{}) - return ok -} - -// IsInterSlice gets whether the object contained is a []interface{} or not. -func (v *Value) IsInterSlice() bool { - _, ok := v.data.([]interface{}) - return ok -} - -// EachInter calls the specified callback for each object -// in the []interface{}. -// -// Panics if the object is the wrong type. -func (v *Value) EachInter(callback func(int, interface{}) bool) *Value { - for index, val := range v.MustInterSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInter uses the specified decider function to select items -// from the []interface{}. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value { - var selected []interface{} - v.EachInter(func(index int, val interface{}) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInter uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]interface{}. -func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value { - groups := make(map[string][]interface{}) - v.EachInter(func(index int, val interface{}) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]interface{}, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInter uses the specified function to replace each interface{}s -// by iterating each item. The data in the returned result will be a -// []interface{} containing the replaced items. -func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value { - arr := v.MustInterSlice() - replaced := make([]interface{}, len(arr)) - v.EachInter(func(index int, val interface{}) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInter uses the specified collector function to collect a value -// for each of the interface{}s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value { - arr := v.MustInterSlice() - collected := make([]interface{}, len(arr)) - v.EachInter(func(index int, val interface{}) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Bool (bool and []bool) -*/ - -// Bool gets the value as a bool, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Bool(optionalDefault ...bool) bool { - if s, ok := v.data.(bool); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return false -} - -// MustBool gets the value as a bool. -// -// Panics if the object is not a bool. -func (v *Value) MustBool() bool { - return v.data.(bool) -} - -// BoolSlice gets the value as a []bool, returns the optionalDefault -// value or nil if the value is not a []bool. -func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool { - if s, ok := v.data.([]bool); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustBoolSlice gets the value as a []bool. -// -// Panics if the object is not a []bool. -func (v *Value) MustBoolSlice() []bool { - return v.data.([]bool) -} - -// IsBool gets whether the object contained is a bool or not. -func (v *Value) IsBool() bool { - _, ok := v.data.(bool) - return ok -} - -// IsBoolSlice gets whether the object contained is a []bool or not. -func (v *Value) IsBoolSlice() bool { - _, ok := v.data.([]bool) - return ok -} - -// EachBool calls the specified callback for each object -// in the []bool. -// -// Panics if the object is the wrong type. -func (v *Value) EachBool(callback func(int, bool) bool) *Value { - for index, val := range v.MustBoolSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereBool uses the specified decider function to select items -// from the []bool. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereBool(decider func(int, bool) bool) *Value { - var selected []bool - v.EachBool(func(index int, val bool) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupBool uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]bool. -func (v *Value) GroupBool(grouper func(int, bool) string) *Value { - groups := make(map[string][]bool) - v.EachBool(func(index int, val bool) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]bool, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceBool uses the specified function to replace each bools -// by iterating each item. The data in the returned result will be a -// []bool containing the replaced items. -func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value { - arr := v.MustBoolSlice() - replaced := make([]bool, len(arr)) - v.EachBool(func(index int, val bool) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectBool uses the specified collector function to collect a value -// for each of the bools in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value { - arr := v.MustBoolSlice() - collected := make([]interface{}, len(arr)) - v.EachBool(func(index int, val bool) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Str (string and []string) -*/ - -// Str gets the value as a string, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Str(optionalDefault ...string) string { - if s, ok := v.data.(string); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return "" -} - -// MustStr gets the value as a string. -// -// Panics if the object is not a string. -func (v *Value) MustStr() string { - return v.data.(string) -} - -// StrSlice gets the value as a []string, returns the optionalDefault -// value or nil if the value is not a []string. -func (v *Value) StrSlice(optionalDefault ...[]string) []string { - if s, ok := v.data.([]string); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustStrSlice gets the value as a []string. -// -// Panics if the object is not a []string. -func (v *Value) MustStrSlice() []string { - return v.data.([]string) -} - -// IsStr gets whether the object contained is a string or not. -func (v *Value) IsStr() bool { - _, ok := v.data.(string) - return ok -} - -// IsStrSlice gets whether the object contained is a []string or not. -func (v *Value) IsStrSlice() bool { - _, ok := v.data.([]string) - return ok -} - -// EachStr calls the specified callback for each object -// in the []string. -// -// Panics if the object is the wrong type. -func (v *Value) EachStr(callback func(int, string) bool) *Value { - for index, val := range v.MustStrSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereStr uses the specified decider function to select items -// from the []string. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereStr(decider func(int, string) bool) *Value { - var selected []string - v.EachStr(func(index int, val string) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupStr uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]string. -func (v *Value) GroupStr(grouper func(int, string) string) *Value { - groups := make(map[string][]string) - v.EachStr(func(index int, val string) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]string, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceStr uses the specified function to replace each strings -// by iterating each item. The data in the returned result will be a -// []string containing the replaced items. -func (v *Value) ReplaceStr(replacer func(int, string) string) *Value { - arr := v.MustStrSlice() - replaced := make([]string, len(arr)) - v.EachStr(func(index int, val string) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectStr uses the specified collector function to collect a value -// for each of the strings in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectStr(collector func(int, string) interface{}) *Value { - arr := v.MustStrSlice() - collected := make([]interface{}, len(arr)) - v.EachStr(func(index int, val string) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int (int and []int) -*/ - -// Int gets the value as a int, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int(optionalDefault ...int) int { - if s, ok := v.data.(int); ok { - return s - } - if s, ok := v.data.(float64); ok { - if float64(int(s)) == s { - return int(s) - } - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt gets the value as a int. -// -// Panics if the object is not a int. -func (v *Value) MustInt() int { - if s, ok := v.data.(float64); ok { - if float64(int(s)) == s { - return int(s) - } - } - return v.data.(int) -} - -// IntSlice gets the value as a []int, returns the optionalDefault -// value or nil if the value is not a []int. -func (v *Value) IntSlice(optionalDefault ...[]int) []int { - if s, ok := v.data.([]int); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustIntSlice gets the value as a []int. -// -// Panics if the object is not a []int. -func (v *Value) MustIntSlice() []int { - return v.data.([]int) -} - -// IsInt gets whether the object contained is a int or not. -func (v *Value) IsInt() bool { - _, ok := v.data.(int) - return ok -} - -// IsIntSlice gets whether the object contained is a []int or not. -func (v *Value) IsIntSlice() bool { - _, ok := v.data.([]int) - return ok -} - -// EachInt calls the specified callback for each object -// in the []int. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt(callback func(int, int) bool) *Value { - for index, val := range v.MustIntSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt uses the specified decider function to select items -// from the []int. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt(decider func(int, int) bool) *Value { - var selected []int - v.EachInt(func(index int, val int) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int. -func (v *Value) GroupInt(grouper func(int, int) string) *Value { - groups := make(map[string][]int) - v.EachInt(func(index int, val int) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt uses the specified function to replace each ints -// by iterating each item. The data in the returned result will be a -// []int containing the replaced items. -func (v *Value) ReplaceInt(replacer func(int, int) int) *Value { - arr := v.MustIntSlice() - replaced := make([]int, len(arr)) - v.EachInt(func(index int, val int) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt uses the specified collector function to collect a value -// for each of the ints in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt(collector func(int, int) interface{}) *Value { - arr := v.MustIntSlice() - collected := make([]interface{}, len(arr)) - v.EachInt(func(index int, val int) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int8 (int8 and []int8) -*/ - -// Int8 gets the value as a int8, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int8(optionalDefault ...int8) int8 { - if s, ok := v.data.(int8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt8 gets the value as a int8. -// -// Panics if the object is not a int8. -func (v *Value) MustInt8() int8 { - return v.data.(int8) -} - -// Int8Slice gets the value as a []int8, returns the optionalDefault -// value or nil if the value is not a []int8. -func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 { - if s, ok := v.data.([]int8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt8Slice gets the value as a []int8. -// -// Panics if the object is not a []int8. -func (v *Value) MustInt8Slice() []int8 { - return v.data.([]int8) -} - -// IsInt8 gets whether the object contained is a int8 or not. -func (v *Value) IsInt8() bool { - _, ok := v.data.(int8) - return ok -} - -// IsInt8Slice gets whether the object contained is a []int8 or not. -func (v *Value) IsInt8Slice() bool { - _, ok := v.data.([]int8) - return ok -} - -// EachInt8 calls the specified callback for each object -// in the []int8. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt8(callback func(int, int8) bool) *Value { - for index, val := range v.MustInt8Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt8 uses the specified decider function to select items -// from the []int8. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt8(decider func(int, int8) bool) *Value { - var selected []int8 - v.EachInt8(func(index int, val int8) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt8 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int8. -func (v *Value) GroupInt8(grouper func(int, int8) string) *Value { - groups := make(map[string][]int8) - v.EachInt8(func(index int, val int8) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int8, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt8 uses the specified function to replace each int8s -// by iterating each item. The data in the returned result will be a -// []int8 containing the replaced items. -func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value { - arr := v.MustInt8Slice() - replaced := make([]int8, len(arr)) - v.EachInt8(func(index int, val int8) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt8 uses the specified collector function to collect a value -// for each of the int8s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value { - arr := v.MustInt8Slice() - collected := make([]interface{}, len(arr)) - v.EachInt8(func(index int, val int8) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int16 (int16 and []int16) -*/ - -// Int16 gets the value as a int16, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int16(optionalDefault ...int16) int16 { - if s, ok := v.data.(int16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt16 gets the value as a int16. -// -// Panics if the object is not a int16. -func (v *Value) MustInt16() int16 { - return v.data.(int16) -} - -// Int16Slice gets the value as a []int16, returns the optionalDefault -// value or nil if the value is not a []int16. -func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 { - if s, ok := v.data.([]int16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt16Slice gets the value as a []int16. -// -// Panics if the object is not a []int16. -func (v *Value) MustInt16Slice() []int16 { - return v.data.([]int16) -} - -// IsInt16 gets whether the object contained is a int16 or not. -func (v *Value) IsInt16() bool { - _, ok := v.data.(int16) - return ok -} - -// IsInt16Slice gets whether the object contained is a []int16 or not. -func (v *Value) IsInt16Slice() bool { - _, ok := v.data.([]int16) - return ok -} - -// EachInt16 calls the specified callback for each object -// in the []int16. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt16(callback func(int, int16) bool) *Value { - for index, val := range v.MustInt16Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt16 uses the specified decider function to select items -// from the []int16. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt16(decider func(int, int16) bool) *Value { - var selected []int16 - v.EachInt16(func(index int, val int16) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt16 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int16. -func (v *Value) GroupInt16(grouper func(int, int16) string) *Value { - groups := make(map[string][]int16) - v.EachInt16(func(index int, val int16) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int16, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt16 uses the specified function to replace each int16s -// by iterating each item. The data in the returned result will be a -// []int16 containing the replaced items. -func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value { - arr := v.MustInt16Slice() - replaced := make([]int16, len(arr)) - v.EachInt16(func(index int, val int16) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt16 uses the specified collector function to collect a value -// for each of the int16s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value { - arr := v.MustInt16Slice() - collected := make([]interface{}, len(arr)) - v.EachInt16(func(index int, val int16) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int32 (int32 and []int32) -*/ - -// Int32 gets the value as a int32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int32(optionalDefault ...int32) int32 { - if s, ok := v.data.(int32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt32 gets the value as a int32. -// -// Panics if the object is not a int32. -func (v *Value) MustInt32() int32 { - return v.data.(int32) -} - -// Int32Slice gets the value as a []int32, returns the optionalDefault -// value or nil if the value is not a []int32. -func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 { - if s, ok := v.data.([]int32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt32Slice gets the value as a []int32. -// -// Panics if the object is not a []int32. -func (v *Value) MustInt32Slice() []int32 { - return v.data.([]int32) -} - -// IsInt32 gets whether the object contained is a int32 or not. -func (v *Value) IsInt32() bool { - _, ok := v.data.(int32) - return ok -} - -// IsInt32Slice gets whether the object contained is a []int32 or not. -func (v *Value) IsInt32Slice() bool { - _, ok := v.data.([]int32) - return ok -} - -// EachInt32 calls the specified callback for each object -// in the []int32. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt32(callback func(int, int32) bool) *Value { - for index, val := range v.MustInt32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt32 uses the specified decider function to select items -// from the []int32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt32(decider func(int, int32) bool) *Value { - var selected []int32 - v.EachInt32(func(index int, val int32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int32. -func (v *Value) GroupInt32(grouper func(int, int32) string) *Value { - groups := make(map[string][]int32) - v.EachInt32(func(index int, val int32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt32 uses the specified function to replace each int32s -// by iterating each item. The data in the returned result will be a -// []int32 containing the replaced items. -func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value { - arr := v.MustInt32Slice() - replaced := make([]int32, len(arr)) - v.EachInt32(func(index int, val int32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt32 uses the specified collector function to collect a value -// for each of the int32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value { - arr := v.MustInt32Slice() - collected := make([]interface{}, len(arr)) - v.EachInt32(func(index int, val int32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Int64 (int64 and []int64) -*/ - -// Int64 gets the value as a int64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Int64(optionalDefault ...int64) int64 { - if s, ok := v.data.(int64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustInt64 gets the value as a int64. -// -// Panics if the object is not a int64. -func (v *Value) MustInt64() int64 { - return v.data.(int64) -} - -// Int64Slice gets the value as a []int64, returns the optionalDefault -// value or nil if the value is not a []int64. -func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 { - if s, ok := v.data.([]int64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustInt64Slice gets the value as a []int64. -// -// Panics if the object is not a []int64. -func (v *Value) MustInt64Slice() []int64 { - return v.data.([]int64) -} - -// IsInt64 gets whether the object contained is a int64 or not. -func (v *Value) IsInt64() bool { - _, ok := v.data.(int64) - return ok -} - -// IsInt64Slice gets whether the object contained is a []int64 or not. -func (v *Value) IsInt64Slice() bool { - _, ok := v.data.([]int64) - return ok -} - -// EachInt64 calls the specified callback for each object -// in the []int64. -// -// Panics if the object is the wrong type. -func (v *Value) EachInt64(callback func(int, int64) bool) *Value { - for index, val := range v.MustInt64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereInt64 uses the specified decider function to select items -// from the []int64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereInt64(decider func(int, int64) bool) *Value { - var selected []int64 - v.EachInt64(func(index int, val int64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupInt64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]int64. -func (v *Value) GroupInt64(grouper func(int, int64) string) *Value { - groups := make(map[string][]int64) - v.EachInt64(func(index int, val int64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]int64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceInt64 uses the specified function to replace each int64s -// by iterating each item. The data in the returned result will be a -// []int64 containing the replaced items. -func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value { - arr := v.MustInt64Slice() - replaced := make([]int64, len(arr)) - v.EachInt64(func(index int, val int64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectInt64 uses the specified collector function to collect a value -// for each of the int64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value { - arr := v.MustInt64Slice() - collected := make([]interface{}, len(arr)) - v.EachInt64(func(index int, val int64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint (uint and []uint) -*/ - -// Uint gets the value as a uint, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint(optionalDefault ...uint) uint { - if s, ok := v.data.(uint); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint gets the value as a uint. -// -// Panics if the object is not a uint. -func (v *Value) MustUint() uint { - return v.data.(uint) -} - -// UintSlice gets the value as a []uint, returns the optionalDefault -// value or nil if the value is not a []uint. -func (v *Value) UintSlice(optionalDefault ...[]uint) []uint { - if s, ok := v.data.([]uint); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUintSlice gets the value as a []uint. -// -// Panics if the object is not a []uint. -func (v *Value) MustUintSlice() []uint { - return v.data.([]uint) -} - -// IsUint gets whether the object contained is a uint or not. -func (v *Value) IsUint() bool { - _, ok := v.data.(uint) - return ok -} - -// IsUintSlice gets whether the object contained is a []uint or not. -func (v *Value) IsUintSlice() bool { - _, ok := v.data.([]uint) - return ok -} - -// EachUint calls the specified callback for each object -// in the []uint. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint(callback func(int, uint) bool) *Value { - for index, val := range v.MustUintSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint uses the specified decider function to select items -// from the []uint. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint(decider func(int, uint) bool) *Value { - var selected []uint - v.EachUint(func(index int, val uint) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint. -func (v *Value) GroupUint(grouper func(int, uint) string) *Value { - groups := make(map[string][]uint) - v.EachUint(func(index int, val uint) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint uses the specified function to replace each uints -// by iterating each item. The data in the returned result will be a -// []uint containing the replaced items. -func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value { - arr := v.MustUintSlice() - replaced := make([]uint, len(arr)) - v.EachUint(func(index int, val uint) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint uses the specified collector function to collect a value -// for each of the uints in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value { - arr := v.MustUintSlice() - collected := make([]interface{}, len(arr)) - v.EachUint(func(index int, val uint) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint8 (uint8 and []uint8) -*/ - -// Uint8 gets the value as a uint8, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint8(optionalDefault ...uint8) uint8 { - if s, ok := v.data.(uint8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint8 gets the value as a uint8. -// -// Panics if the object is not a uint8. -func (v *Value) MustUint8() uint8 { - return v.data.(uint8) -} - -// Uint8Slice gets the value as a []uint8, returns the optionalDefault -// value or nil if the value is not a []uint8. -func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 { - if s, ok := v.data.([]uint8); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint8Slice gets the value as a []uint8. -// -// Panics if the object is not a []uint8. -func (v *Value) MustUint8Slice() []uint8 { - return v.data.([]uint8) -} - -// IsUint8 gets whether the object contained is a uint8 or not. -func (v *Value) IsUint8() bool { - _, ok := v.data.(uint8) - return ok -} - -// IsUint8Slice gets whether the object contained is a []uint8 or not. -func (v *Value) IsUint8Slice() bool { - _, ok := v.data.([]uint8) - return ok -} - -// EachUint8 calls the specified callback for each object -// in the []uint8. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint8(callback func(int, uint8) bool) *Value { - for index, val := range v.MustUint8Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint8 uses the specified decider function to select items -// from the []uint8. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value { - var selected []uint8 - v.EachUint8(func(index int, val uint8) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint8 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint8. -func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value { - groups := make(map[string][]uint8) - v.EachUint8(func(index int, val uint8) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint8, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint8 uses the specified function to replace each uint8s -// by iterating each item. The data in the returned result will be a -// []uint8 containing the replaced items. -func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value { - arr := v.MustUint8Slice() - replaced := make([]uint8, len(arr)) - v.EachUint8(func(index int, val uint8) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint8 uses the specified collector function to collect a value -// for each of the uint8s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value { - arr := v.MustUint8Slice() - collected := make([]interface{}, len(arr)) - v.EachUint8(func(index int, val uint8) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint16 (uint16 and []uint16) -*/ - -// Uint16 gets the value as a uint16, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint16(optionalDefault ...uint16) uint16 { - if s, ok := v.data.(uint16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint16 gets the value as a uint16. -// -// Panics if the object is not a uint16. -func (v *Value) MustUint16() uint16 { - return v.data.(uint16) -} - -// Uint16Slice gets the value as a []uint16, returns the optionalDefault -// value or nil if the value is not a []uint16. -func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 { - if s, ok := v.data.([]uint16); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint16Slice gets the value as a []uint16. -// -// Panics if the object is not a []uint16. -func (v *Value) MustUint16Slice() []uint16 { - return v.data.([]uint16) -} - -// IsUint16 gets whether the object contained is a uint16 or not. -func (v *Value) IsUint16() bool { - _, ok := v.data.(uint16) - return ok -} - -// IsUint16Slice gets whether the object contained is a []uint16 or not. -func (v *Value) IsUint16Slice() bool { - _, ok := v.data.([]uint16) - return ok -} - -// EachUint16 calls the specified callback for each object -// in the []uint16. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint16(callback func(int, uint16) bool) *Value { - for index, val := range v.MustUint16Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint16 uses the specified decider function to select items -// from the []uint16. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value { - var selected []uint16 - v.EachUint16(func(index int, val uint16) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint16 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint16. -func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value { - groups := make(map[string][]uint16) - v.EachUint16(func(index int, val uint16) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint16, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint16 uses the specified function to replace each uint16s -// by iterating each item. The data in the returned result will be a -// []uint16 containing the replaced items. -func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value { - arr := v.MustUint16Slice() - replaced := make([]uint16, len(arr)) - v.EachUint16(func(index int, val uint16) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint16 uses the specified collector function to collect a value -// for each of the uint16s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value { - arr := v.MustUint16Slice() - collected := make([]interface{}, len(arr)) - v.EachUint16(func(index int, val uint16) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint32 (uint32 and []uint32) -*/ - -// Uint32 gets the value as a uint32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint32(optionalDefault ...uint32) uint32 { - if s, ok := v.data.(uint32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint32 gets the value as a uint32. -// -// Panics if the object is not a uint32. -func (v *Value) MustUint32() uint32 { - return v.data.(uint32) -} - -// Uint32Slice gets the value as a []uint32, returns the optionalDefault -// value or nil if the value is not a []uint32. -func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 { - if s, ok := v.data.([]uint32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint32Slice gets the value as a []uint32. -// -// Panics if the object is not a []uint32. -func (v *Value) MustUint32Slice() []uint32 { - return v.data.([]uint32) -} - -// IsUint32 gets whether the object contained is a uint32 or not. -func (v *Value) IsUint32() bool { - _, ok := v.data.(uint32) - return ok -} - -// IsUint32Slice gets whether the object contained is a []uint32 or not. -func (v *Value) IsUint32Slice() bool { - _, ok := v.data.([]uint32) - return ok -} - -// EachUint32 calls the specified callback for each object -// in the []uint32. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint32(callback func(int, uint32) bool) *Value { - for index, val := range v.MustUint32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint32 uses the specified decider function to select items -// from the []uint32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value { - var selected []uint32 - v.EachUint32(func(index int, val uint32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint32. -func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value { - groups := make(map[string][]uint32) - v.EachUint32(func(index int, val uint32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint32 uses the specified function to replace each uint32s -// by iterating each item. The data in the returned result will be a -// []uint32 containing the replaced items. -func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value { - arr := v.MustUint32Slice() - replaced := make([]uint32, len(arr)) - v.EachUint32(func(index int, val uint32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint32 uses the specified collector function to collect a value -// for each of the uint32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value { - arr := v.MustUint32Slice() - collected := make([]interface{}, len(arr)) - v.EachUint32(func(index int, val uint32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uint64 (uint64 and []uint64) -*/ - -// Uint64 gets the value as a uint64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uint64(optionalDefault ...uint64) uint64 { - if s, ok := v.data.(uint64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUint64 gets the value as a uint64. -// -// Panics if the object is not a uint64. -func (v *Value) MustUint64() uint64 { - return v.data.(uint64) -} - -// Uint64Slice gets the value as a []uint64, returns the optionalDefault -// value or nil if the value is not a []uint64. -func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 { - if s, ok := v.data.([]uint64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUint64Slice gets the value as a []uint64. -// -// Panics if the object is not a []uint64. -func (v *Value) MustUint64Slice() []uint64 { - return v.data.([]uint64) -} - -// IsUint64 gets whether the object contained is a uint64 or not. -func (v *Value) IsUint64() bool { - _, ok := v.data.(uint64) - return ok -} - -// IsUint64Slice gets whether the object contained is a []uint64 or not. -func (v *Value) IsUint64Slice() bool { - _, ok := v.data.([]uint64) - return ok -} - -// EachUint64 calls the specified callback for each object -// in the []uint64. -// -// Panics if the object is the wrong type. -func (v *Value) EachUint64(callback func(int, uint64) bool) *Value { - for index, val := range v.MustUint64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUint64 uses the specified decider function to select items -// from the []uint64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value { - var selected []uint64 - v.EachUint64(func(index int, val uint64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUint64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uint64. -func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value { - groups := make(map[string][]uint64) - v.EachUint64(func(index int, val uint64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uint64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUint64 uses the specified function to replace each uint64s -// by iterating each item. The data in the returned result will be a -// []uint64 containing the replaced items. -func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value { - arr := v.MustUint64Slice() - replaced := make([]uint64, len(arr)) - v.EachUint64(func(index int, val uint64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUint64 uses the specified collector function to collect a value -// for each of the uint64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value { - arr := v.MustUint64Slice() - collected := make([]interface{}, len(arr)) - v.EachUint64(func(index int, val uint64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Uintptr (uintptr and []uintptr) -*/ - -// Uintptr gets the value as a uintptr, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr { - if s, ok := v.data.(uintptr); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustUintptr gets the value as a uintptr. -// -// Panics if the object is not a uintptr. -func (v *Value) MustUintptr() uintptr { - return v.data.(uintptr) -} - -// UintptrSlice gets the value as a []uintptr, returns the optionalDefault -// value or nil if the value is not a []uintptr. -func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr { - if s, ok := v.data.([]uintptr); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustUintptrSlice gets the value as a []uintptr. -// -// Panics if the object is not a []uintptr. -func (v *Value) MustUintptrSlice() []uintptr { - return v.data.([]uintptr) -} - -// IsUintptr gets whether the object contained is a uintptr or not. -func (v *Value) IsUintptr() bool { - _, ok := v.data.(uintptr) - return ok -} - -// IsUintptrSlice gets whether the object contained is a []uintptr or not. -func (v *Value) IsUintptrSlice() bool { - _, ok := v.data.([]uintptr) - return ok -} - -// EachUintptr calls the specified callback for each object -// in the []uintptr. -// -// Panics if the object is the wrong type. -func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value { - for index, val := range v.MustUintptrSlice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereUintptr uses the specified decider function to select items -// from the []uintptr. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value { - var selected []uintptr - v.EachUintptr(func(index int, val uintptr) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupUintptr uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]uintptr. -func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value { - groups := make(map[string][]uintptr) - v.EachUintptr(func(index int, val uintptr) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]uintptr, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceUintptr uses the specified function to replace each uintptrs -// by iterating each item. The data in the returned result will be a -// []uintptr containing the replaced items. -func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value { - arr := v.MustUintptrSlice() - replaced := make([]uintptr, len(arr)) - v.EachUintptr(func(index int, val uintptr) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectUintptr uses the specified collector function to collect a value -// for each of the uintptrs in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value { - arr := v.MustUintptrSlice() - collected := make([]interface{}, len(arr)) - v.EachUintptr(func(index int, val uintptr) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Float32 (float32 and []float32) -*/ - -// Float32 gets the value as a float32, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Float32(optionalDefault ...float32) float32 { - if s, ok := v.data.(float32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustFloat32 gets the value as a float32. -// -// Panics if the object is not a float32. -func (v *Value) MustFloat32() float32 { - return v.data.(float32) -} - -// Float32Slice gets the value as a []float32, returns the optionalDefault -// value or nil if the value is not a []float32. -func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 { - if s, ok := v.data.([]float32); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustFloat32Slice gets the value as a []float32. -// -// Panics if the object is not a []float32. -func (v *Value) MustFloat32Slice() []float32 { - return v.data.([]float32) -} - -// IsFloat32 gets whether the object contained is a float32 or not. -func (v *Value) IsFloat32() bool { - _, ok := v.data.(float32) - return ok -} - -// IsFloat32Slice gets whether the object contained is a []float32 or not. -func (v *Value) IsFloat32Slice() bool { - _, ok := v.data.([]float32) - return ok -} - -// EachFloat32 calls the specified callback for each object -// in the []float32. -// -// Panics if the object is the wrong type. -func (v *Value) EachFloat32(callback func(int, float32) bool) *Value { - for index, val := range v.MustFloat32Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereFloat32 uses the specified decider function to select items -// from the []float32. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value { - var selected []float32 - v.EachFloat32(func(index int, val float32) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupFloat32 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]float32. -func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value { - groups := make(map[string][]float32) - v.EachFloat32(func(index int, val float32) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]float32, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceFloat32 uses the specified function to replace each float32s -// by iterating each item. The data in the returned result will be a -// []float32 containing the replaced items. -func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value { - arr := v.MustFloat32Slice() - replaced := make([]float32, len(arr)) - v.EachFloat32(func(index int, val float32) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectFloat32 uses the specified collector function to collect a value -// for each of the float32s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value { - arr := v.MustFloat32Slice() - collected := make([]interface{}, len(arr)) - v.EachFloat32(func(index int, val float32) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Float64 (float64 and []float64) -*/ - -// Float64 gets the value as a float64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Float64(optionalDefault ...float64) float64 { - if s, ok := v.data.(float64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustFloat64 gets the value as a float64. -// -// Panics if the object is not a float64. -func (v *Value) MustFloat64() float64 { - return v.data.(float64) -} - -// Float64Slice gets the value as a []float64, returns the optionalDefault -// value or nil if the value is not a []float64. -func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 { - if s, ok := v.data.([]float64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustFloat64Slice gets the value as a []float64. -// -// Panics if the object is not a []float64. -func (v *Value) MustFloat64Slice() []float64 { - return v.data.([]float64) -} - -// IsFloat64 gets whether the object contained is a float64 or not. -func (v *Value) IsFloat64() bool { - _, ok := v.data.(float64) - return ok -} - -// IsFloat64Slice gets whether the object contained is a []float64 or not. -func (v *Value) IsFloat64Slice() bool { - _, ok := v.data.([]float64) - return ok -} - -// EachFloat64 calls the specified callback for each object -// in the []float64. -// -// Panics if the object is the wrong type. -func (v *Value) EachFloat64(callback func(int, float64) bool) *Value { - for index, val := range v.MustFloat64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereFloat64 uses the specified decider function to select items -// from the []float64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value { - var selected []float64 - v.EachFloat64(func(index int, val float64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupFloat64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]float64. -func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value { - groups := make(map[string][]float64) - v.EachFloat64(func(index int, val float64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]float64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceFloat64 uses the specified function to replace each float64s -// by iterating each item. The data in the returned result will be a -// []float64 containing the replaced items. -func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value { - arr := v.MustFloat64Slice() - replaced := make([]float64, len(arr)) - v.EachFloat64(func(index int, val float64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectFloat64 uses the specified collector function to collect a value -// for each of the float64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value { - arr := v.MustFloat64Slice() - collected := make([]interface{}, len(arr)) - v.EachFloat64(func(index int, val float64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Complex64 (complex64 and []complex64) -*/ - -// Complex64 gets the value as a complex64, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Complex64(optionalDefault ...complex64) complex64 { - if s, ok := v.data.(complex64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustComplex64 gets the value as a complex64. -// -// Panics if the object is not a complex64. -func (v *Value) MustComplex64() complex64 { - return v.data.(complex64) -} - -// Complex64Slice gets the value as a []complex64, returns the optionalDefault -// value or nil if the value is not a []complex64. -func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 { - if s, ok := v.data.([]complex64); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustComplex64Slice gets the value as a []complex64. -// -// Panics if the object is not a []complex64. -func (v *Value) MustComplex64Slice() []complex64 { - return v.data.([]complex64) -} - -// IsComplex64 gets whether the object contained is a complex64 or not. -func (v *Value) IsComplex64() bool { - _, ok := v.data.(complex64) - return ok -} - -// IsComplex64Slice gets whether the object contained is a []complex64 or not. -func (v *Value) IsComplex64Slice() bool { - _, ok := v.data.([]complex64) - return ok -} - -// EachComplex64 calls the specified callback for each object -// in the []complex64. -// -// Panics if the object is the wrong type. -func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value { - for index, val := range v.MustComplex64Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereComplex64 uses the specified decider function to select items -// from the []complex64. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value { - var selected []complex64 - v.EachComplex64(func(index int, val complex64) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupComplex64 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]complex64. -func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value { - groups := make(map[string][]complex64) - v.EachComplex64(func(index int, val complex64) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]complex64, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceComplex64 uses the specified function to replace each complex64s -// by iterating each item. The data in the returned result will be a -// []complex64 containing the replaced items. -func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value { - arr := v.MustComplex64Slice() - replaced := make([]complex64, len(arr)) - v.EachComplex64(func(index int, val complex64) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectComplex64 uses the specified collector function to collect a value -// for each of the complex64s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value { - arr := v.MustComplex64Slice() - collected := make([]interface{}, len(arr)) - v.EachComplex64(func(index int, val complex64) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} - -/* - Complex128 (complex128 and []complex128) -*/ - -// Complex128 gets the value as a complex128, returns the optionalDefault -// value or a system default object if the value is the wrong type. -func (v *Value) Complex128(optionalDefault ...complex128) complex128 { - if s, ok := v.data.(complex128); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return 0 -} - -// MustComplex128 gets the value as a complex128. -// -// Panics if the object is not a complex128. -func (v *Value) MustComplex128() complex128 { - return v.data.(complex128) -} - -// Complex128Slice gets the value as a []complex128, returns the optionalDefault -// value or nil if the value is not a []complex128. -func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 { - if s, ok := v.data.([]complex128); ok { - return s - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - return nil -} - -// MustComplex128Slice gets the value as a []complex128. -// -// Panics if the object is not a []complex128. -func (v *Value) MustComplex128Slice() []complex128 { - return v.data.([]complex128) -} - -// IsComplex128 gets whether the object contained is a complex128 or not. -func (v *Value) IsComplex128() bool { - _, ok := v.data.(complex128) - return ok -} - -// IsComplex128Slice gets whether the object contained is a []complex128 or not. -func (v *Value) IsComplex128Slice() bool { - _, ok := v.data.([]complex128) - return ok -} - -// EachComplex128 calls the specified callback for each object -// in the []complex128. -// -// Panics if the object is the wrong type. -func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value { - for index, val := range v.MustComplex128Slice() { - carryon := callback(index, val) - if !carryon { - break - } - } - return v -} - -// WhereComplex128 uses the specified decider function to select items -// from the []complex128. The object contained in the result will contain -// only the selected items. -func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value { - var selected []complex128 - v.EachComplex128(func(index int, val complex128) bool { - shouldSelect := decider(index, val) - if !shouldSelect { - selected = append(selected, val) - } - return true - }) - return &Value{data: selected} -} - -// GroupComplex128 uses the specified grouper function to group the items -// keyed by the return of the grouper. The object contained in the -// result will contain a map[string][]complex128. -func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value { - groups := make(map[string][]complex128) - v.EachComplex128(func(index int, val complex128) bool { - group := grouper(index, val) - if _, ok := groups[group]; !ok { - groups[group] = make([]complex128, 0) - } - groups[group] = append(groups[group], val) - return true - }) - return &Value{data: groups} -} - -// ReplaceComplex128 uses the specified function to replace each complex128s -// by iterating each item. The data in the returned result will be a -// []complex128 containing the replaced items. -func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value { - arr := v.MustComplex128Slice() - replaced := make([]complex128, len(arr)) - v.EachComplex128(func(index int, val complex128) bool { - replaced[index] = replacer(index, val) - return true - }) - return &Value{data: replaced} -} - -// CollectComplex128 uses the specified collector function to collect a value -// for each of the complex128s in the slice. The data returned will be a -// []interface{}. -func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value { - arr := v.MustComplex128Slice() - collected := make([]interface{}, len(arr)) - v.EachComplex128(func(index int, val complex128) bool { - collected[index] = collector(index, val) - return true - }) - return &Value{data: collected} -} diff --git a/vendor/github.com/stretchr/objx/value.go b/vendor/github.com/stretchr/objx/value.go deleted file mode 100644 index 4e5f9b77e6..0000000000 --- a/vendor/github.com/stretchr/objx/value.go +++ /dev/null @@ -1,159 +0,0 @@ -package objx - -import ( - "fmt" - "strconv" -) - -// Value provides methods for extracting interface{} data in various -// types. -type Value struct { - // data contains the raw data being managed by this Value - data interface{} -} - -// Data returns the raw data contained by this Value -func (v *Value) Data() interface{} { - return v.data -} - -// String returns the value always as a string -func (v *Value) String() string { - switch { - case v.IsNil(): - return "" - case v.IsStr(): - return v.Str() - case v.IsBool(): - return strconv.FormatBool(v.Bool()) - case v.IsFloat32(): - return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) - case v.IsFloat64(): - return strconv.FormatFloat(v.Float64(), 'f', -1, 64) - case v.IsInt(): - return strconv.FormatInt(int64(v.Int()), 10) - case v.IsInt8(): - return strconv.FormatInt(int64(v.Int8()), 10) - case v.IsInt16(): - return strconv.FormatInt(int64(v.Int16()), 10) - case v.IsInt32(): - return strconv.FormatInt(int64(v.Int32()), 10) - case v.IsInt64(): - return strconv.FormatInt(v.Int64(), 10) - case v.IsUint(): - return strconv.FormatUint(uint64(v.Uint()), 10) - case v.IsUint8(): - return strconv.FormatUint(uint64(v.Uint8()), 10) - case v.IsUint16(): - return strconv.FormatUint(uint64(v.Uint16()), 10) - case v.IsUint32(): - return strconv.FormatUint(uint64(v.Uint32()), 10) - case v.IsUint64(): - return strconv.FormatUint(v.Uint64(), 10) - } - return fmt.Sprintf("%#v", v.Data()) -} - -// StringSlice returns the value always as a []string -func (v *Value) StringSlice(optionalDefault ...[]string) []string { - switch { - case v.IsStrSlice(): - return v.MustStrSlice() - case v.IsBoolSlice(): - slice := v.MustBoolSlice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatBool(iv) - } - return vals - case v.IsFloat32Slice(): - slice := v.MustFloat32Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatFloat(float64(iv), 'f', -1, 32) - } - return vals - case v.IsFloat64Slice(): - slice := v.MustFloat64Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatFloat(iv, 'f', -1, 64) - } - return vals - case v.IsIntSlice(): - slice := v.MustIntSlice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt8Slice(): - slice := v.MustInt8Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt16Slice(): - slice := v.MustInt16Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt32Slice(): - slice := v.MustInt32Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(int64(iv), 10) - } - return vals - case v.IsInt64Slice(): - slice := v.MustInt64Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatInt(iv, 10) - } - return vals - case v.IsUintSlice(): - slice := v.MustUintSlice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint8Slice(): - slice := v.MustUint8Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint16Slice(): - slice := v.MustUint16Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint32Slice(): - slice := v.MustUint32Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(uint64(iv), 10) - } - return vals - case v.IsUint64Slice(): - slice := v.MustUint64Slice() - vals := make([]string, len(slice)) - for i, iv := range slice { - vals[i] = strconv.FormatUint(iv, 10) - } - return vals - } - if len(optionalDefault) == 1 { - return optionalDefault[0] - } - - return []string{} -} diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go deleted file mode 100644 index d6b3c844cc..0000000000 --- a/vendor/github.com/stretchr/testify/mock/doc.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package mock provides a system by which it is possible to mock your objects -// and verify calls are happening as expected. -// -// # Example Usage -// -// The mock package provides an object, Mock, that tracks activity on another object. It is usually -// embedded into a test object as shown below: -// -// type MyTestObject struct { -// // add a Mock object instance -// mock.Mock -// -// // other fields go here as normal -// } -// -// When implementing the methods of an interface, you wire your functions up -// to call the Mock.Called(args...) method, and return the appropriate values. -// -// For example, to mock a method that saves the name and age of a person and returns -// the year of their birth or an error, you might write this: -// -// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { -// args := o.Called(firstname, lastname, age) -// return args.Int(0), args.Error(1) -// } -// -// The Int, Error and Bool methods are examples of strongly typed getters that take the argument -// index position. Given this argument list: -// -// (12, true, "Something") -// -// You could read them out strongly typed like this: -// -// args.Int(0) -// args.Bool(1) -// args.String(2) -// -// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: -// -// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) -// -// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those -// cases you should check for nil first. -package mock diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go deleted file mode 100644 index efc89deff3..0000000000 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ /dev/null @@ -1,1298 +0,0 @@ -package mock - -import ( - "errors" - "fmt" - "path" - "reflect" - "regexp" - "runtime" - "strings" - "sync" - "time" - - "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" - "github.com/stretchr/objx" - - "github.com/stretchr/testify/assert" -) - -// regex for GCCGO functions -var gccgoRE = regexp.MustCompile(`\.pN\d+_`) - -// TestingT is an interface wrapper around *testing.T -type TestingT interface { - Logf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - FailNow() -} - -/* - Call -*/ - -// Call represents a method call and is used for setting expectations, -// as well as recording activity. -type Call struct { - Parent *Mock - - // The name of the method that was or will be called. - Method string - - // Holds the arguments of the method. - Arguments Arguments - - // Holds the arguments that should be returned when - // this method is called. - ReturnArguments Arguments - - // Holds the caller info for the On() call - callerInfo []string - - // The number of times to return the return arguments when setting - // expectations. 0 means to always return the value. - Repeatability int - - // Amount of times this call has been called - totalCalls int - - // Call to this method can be optional - optional bool - - // Holds a channel that will be used to block the Return until it either - // receives a message or is closed. nil means it returns immediately. - WaitFor <-chan time.Time - - waitTime time.Duration - - // Holds a handler used to manipulate arguments content that are passed by - // reference. It's useful when mocking methods such as unmarshalers or - // decoders. - RunFn func(Arguments) - - // PanicMsg holds msg to be used to mock panic on the function call - // if the PanicMsg is set to a non nil string the function call will panic - // irrespective of other settings - PanicMsg *string - - // Calls which must be satisfied before this call can be - requires []*Call -} - -func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments Arguments, returnArguments Arguments) *Call { - return &Call{ - Parent: parent, - Method: methodName, - Arguments: methodArguments, - ReturnArguments: returnArguments, - callerInfo: callerInfo, - Repeatability: 0, - WaitFor: nil, - RunFn: nil, - PanicMsg: nil, - } -} - -func (c *Call) lock() { - c.Parent.mutex.Lock() -} - -func (c *Call) unlock() { - c.Parent.mutex.Unlock() -} - -// Return specifies the return arguments for the expectation. -// -// Mock.On("DoSomething").Return(errors.New("failed")) -func (c *Call) Return(returnArguments ...interface{}) *Call { - c.lock() - defer c.unlock() - - c.ReturnArguments = returnArguments - - return c -} - -// Panic specifies if the function call should fail and the panic message -// -// Mock.On("DoSomething").Panic("test panic") -func (c *Call) Panic(msg string) *Call { - c.lock() - defer c.unlock() - - c.PanicMsg = &msg - - return c -} - -// Once indicates that the mock should only return the value once. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() -func (c *Call) Once() *Call { - return c.Times(1) -} - -// Twice indicates that the mock should only return the value twice. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() -func (c *Call) Twice() *Call { - return c.Times(2) -} - -// Times indicates that the mock should only return the indicated number -// of times. -// -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) -func (c *Call) Times(i int) *Call { - c.lock() - defer c.unlock() - c.Repeatability = i - return c -} - -// WaitUntil sets the channel that will block the mock's return until its closed -// or a message is received. -// -// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) -func (c *Call) WaitUntil(w <-chan time.Time) *Call { - c.lock() - defer c.unlock() - c.WaitFor = w - return c -} - -// After sets how long to block until the call returns -// -// Mock.On("MyMethod", arg1, arg2).After(time.Second) -func (c *Call) After(d time.Duration) *Call { - c.lock() - defer c.unlock() - c.waitTime = d - return c -} - -// Run sets a handler to be called before returning. It can be used when -// mocking a method (such as an unmarshaler) that takes a pointer to a struct and -// sets properties in such struct -// -// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { -// arg := args.Get(0).(*map[string]interface{}) -// arg["foo"] = "bar" -// }) -func (c *Call) Run(fn func(args Arguments)) *Call { - c.lock() - defer c.unlock() - c.RunFn = fn - return c -} - -// Maybe allows the method call to be optional. Not calling an optional method -// will not cause an error while asserting expectations -func (c *Call) Maybe() *Call { - c.lock() - defer c.unlock() - c.optional = true - return c -} - -// On chains a new expectation description onto the mocked interface. This -// allows syntax like. -// -// Mock. -// On("MyMethod", 1).Return(nil). -// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) -// -//go:noinline -func (c *Call) On(methodName string, arguments ...interface{}) *Call { - return c.Parent.On(methodName, arguments...) -} - -// Unset removes all mock handlers that satisfy the call instance arguments from being -// called. Only supported on call instances with static input arguments. -// -// For example, the only handler remaining after the following would be "MyMethod(2, 2)": -// -// Mock. -// On("MyMethod", 2, 2).Return(0). -// On("MyMethod", 3, 3).Return(0). -// On("MyMethod", Anything, Anything).Return(0) -// Mock.On("MyMethod", 3, 3).Unset() -func (c *Call) Unset() *Call { - var unlockOnce sync.Once - - for _, arg := range c.Arguments { - if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { - panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) - } - } - - c.lock() - defer unlockOnce.Do(c.unlock) - - foundMatchingCall := false - - // in-place filter slice for calls to be removed - iterate from 0'th to last skipping unnecessary ones - var index int // write index - for _, call := range c.Parent.ExpectedCalls { - if call.Method == c.Method { - _, diffCount := call.Arguments.Diff(c.Arguments) - if diffCount == 0 { - foundMatchingCall = true - // Remove from ExpectedCalls - just skip it - continue - } - } - c.Parent.ExpectedCalls[index] = call - index++ - } - // trim slice up to last copied index - c.Parent.ExpectedCalls = c.Parent.ExpectedCalls[:index] - - if !foundMatchingCall { - unlockOnce.Do(c.unlock) - c.Parent.fail("\n\nmock: Could not find expected call\n-----------------------------\n\n%s\n\n", - callString(c.Method, c.Arguments, true), - ) - } - - return c -} - -// NotBefore indicates that the mock should only be called after the referenced -// calls have been called as expected. The referenced calls may be from the -// same mock instance and/or other mock instances. -// -// Mock.On("Do").Return(nil).NotBefore( -// Mock.On("Init").Return(nil) -// ) -func (c *Call) NotBefore(calls ...*Call) *Call { - c.lock() - defer c.unlock() - - for _, call := range calls { - if call.Parent == nil { - panic("not before calls must be created with Mock.On()") - } - } - - c.requires = append(c.requires, calls...) - return c -} - -// InOrder defines the order in which the calls should be made -// -// For example: -// -// InOrder( -// Mock.On("init").Return(nil), -// Mock.On("Do").Return(nil), -// ) -func InOrder(calls ...*Call) { - for i := 1; i < len(calls); i++ { - calls[i].NotBefore(calls[i-1]) - } -} - -// Mock is the workhorse used to track activity on another object. -// For an example of its usage, refer to the "Example Usage" section at the top -// of this document. -type Mock struct { - // Represents the calls that are expected of - // an object. - ExpectedCalls []*Call - - // Holds the calls that were made to this mocked object. - Calls []Call - - // test is An optional variable that holds the test struct, to be used when an - // invalid mock call was made. - test TestingT - - // TestData holds any data that might be useful for testing. Testify ignores - // this data completely allowing you to do whatever you like with it. - testData objx.Map - - mutex sync.Mutex -} - -// String provides a %v format string for Mock. -// Note: this is used implicitly by Arguments.Diff if a Mock is passed. -// It exists because go's default %v formatting traverses the struct -// without acquiring the mutex, which is detected by go test -race. -func (m *Mock) String() string { - return fmt.Sprintf("%[1]T<%[1]p>", m) -} - -// TestData holds any data that might be useful for testing. Testify ignores -// this data completely allowing you to do whatever you like with it. -func (m *Mock) TestData() objx.Map { - if m.testData == nil { - m.testData = make(objx.Map) - } - - return m.testData -} - -/* - Setting expectations -*/ - -// Test sets the [TestingT] on which errors will be reported, otherwise errors -// will cause a panic. -// Test should not be called on an object that is going to be used in a -// goroutine other than the one running the test function. -func (m *Mock) Test(t TestingT) { - m.mutex.Lock() - defer m.mutex.Unlock() - m.test = t -} - -// fail fails the current test with the given formatted format and args. -// In case that a test was defined, it uses the test APIs for failing a test, -// otherwise it uses panic. -func (m *Mock) fail(format string, args ...interface{}) { - m.mutex.Lock() - defer m.mutex.Unlock() - - if m.test == nil { - panic(fmt.Sprintf(format, args...)) - } - m.test.Errorf(format, args...) - m.test.FailNow() -} - -// On starts a description of an expectation of the specified method -// being called. -// -// Mock.On("MyMethod", arg1, arg2) -func (m *Mock) On(methodName string, arguments ...interface{}) *Call { - for _, arg := range arguments { - if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { - panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) - } - } - - m.mutex.Lock() - defer m.mutex.Unlock() - - c := newCall(m, methodName, assert.CallerInfo(), arguments, make([]interface{}, 0)) - m.ExpectedCalls = append(m.ExpectedCalls, c) - return c -} - -// /* -// Recording and responding to activity -// */ - -func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { - var expectedCall *Call - - for i, call := range m.ExpectedCalls { - if call.Method == method { - _, diffCount := call.Arguments.Diff(arguments) - if diffCount == 0 { - expectedCall = call - if call.Repeatability > -1 { - return i, call - } - } - } - } - - return -1, expectedCall -} - -type matchCandidate struct { - call *Call - mismatch string - diffCount int -} - -func (c matchCandidate) isBetterMatchThan(other matchCandidate) bool { - if c.call == nil { - return false - } - if other.call == nil { - return true - } - - if c.diffCount > other.diffCount { - return false - } - if c.diffCount < other.diffCount { - return true - } - - if c.call.Repeatability > 0 && other.call.Repeatability <= 0 { - return true - } - return false -} - -func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { - var bestMatch matchCandidate - - for _, call := range m.expectedCalls() { - if call.Method == method { - - errInfo, tempDiffCount := call.Arguments.Diff(arguments) - tempCandidate := matchCandidate{ - call: call, - mismatch: errInfo, - diffCount: tempDiffCount, - } - if tempCandidate.isBetterMatchThan(bestMatch) { - bestMatch = tempCandidate - } - } - } - - return bestMatch.call, bestMatch.mismatch -} - -func callString(method string, arguments Arguments, includeArgumentValues bool) string { - var argValsString string - if includeArgumentValues { - var argVals []string - for argIndex, arg := range arguments { - if _, ok := arg.(*FunctionalOptionsArgument); ok { - argVals = append(argVals, fmt.Sprintf("%d: %s", argIndex, arg)) - continue - } - argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) - } - argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) - } - - return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString) -} - -// Called tells the mock object that a method has been called, and gets an array -// of arguments to return. Panics if the call is unexpected (i.e. not preceded by -// appropriate .On .Return() calls) -// If Call.WaitFor is set, blocks until the channel is closed or receives a message. -func (m *Mock) Called(arguments ...interface{}) Arguments { - // get the calling function's name - pc, _, _, ok := runtime.Caller(1) - if !ok { - panic("Couldn't get the caller information") - } - functionPath := runtime.FuncForPC(pc).Name() - // Next four lines are required to use GCCGO function naming conventions. - // For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock - // uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree - // With GCCGO we need to remove interface information starting from pN
. - if gccgoRE.MatchString(functionPath) { - functionPath = gccgoRE.Split(functionPath, -1)[0] - } - parts := strings.Split(functionPath, ".") - functionName := parts[len(parts)-1] - return m.MethodCalled(functionName, arguments...) -} - -// MethodCalled tells the mock object that the given method has been called, and gets -// an array of arguments to return. Panics if the call is unexpected (i.e. not preceded -// by appropriate .On .Return() calls) -// If Call.WaitFor is set, blocks until the channel is closed or receives a message. -func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { - m.mutex.Lock() - // TODO: could combine expected and closes in single loop - found, call := m.findExpectedCall(methodName, arguments...) - - if found < 0 { - // expected call found, but it has already been called with repeatable times - if call != nil { - m.mutex.Unlock() - m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(%#v).Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) - } - // we have to fail here - because we don't know what to do - // as the return arguments. This is because: - // - // a) this is a totally unexpected call to this method, - // b) the arguments are not what was expected, or - // c) the developer has forgotten to add an accompanying On...Return pair. - closestCall, mismatch := m.findClosestCall(methodName, arguments...) - m.mutex.Unlock() - - if closestCall != nil { - m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s\nat: %s\n", - callString(methodName, arguments, true), - callString(methodName, closestCall.Arguments, true), - diffArguments(closestCall.Arguments, arguments), - strings.TrimSpace(mismatch), - assert.CallerInfo(), - ) - } else { - m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(%#v).Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) - } - } - - for _, requirement := range call.requires { - if satisfied, _ := requirement.Parent.checkExpectation(requirement); !satisfied { - m.mutex.Unlock() - m.fail("mock: Unexpected Method Call\n-----------------------------\n\n%s\n\nMust not be called before%s:\n\n%s", - callString(call.Method, call.Arguments, true), - func() (s string) { - if requirement.totalCalls > 0 { - s = " another call of" - } - if call.Parent != requirement.Parent { - s += " method from another mock instance" - } - return - }(), - callString(requirement.Method, requirement.Arguments, true), - ) - } - } - - if call.Repeatability == 1 { - call.Repeatability = -1 - } else if call.Repeatability > 1 { - call.Repeatability-- - } - call.totalCalls++ - - // add the call - m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments, call.ReturnArguments)) - m.mutex.Unlock() - - // block if specified - if call.WaitFor != nil { - <-call.WaitFor - } else { - time.Sleep(call.waitTime) - } - - m.mutex.Lock() - panicMsg := call.PanicMsg - m.mutex.Unlock() - if panicMsg != nil { - panic(*panicMsg) - } - - m.mutex.Lock() - runFn := call.RunFn - m.mutex.Unlock() - - if runFn != nil { - runFn(arguments) - } - - m.mutex.Lock() - returnArgs := call.ReturnArguments - m.mutex.Unlock() - - return returnArgs -} - -/* - Assertions -*/ - -type assertExpectationiser interface { - AssertExpectations(TestingT) bool -} - -// AssertExpectationsForObjects asserts that everything specified with On and Return -// of the specified objects was in fact called as expected. -// -// Calls may have occurred in any order. -func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - for _, obj := range testObjects { - if m, ok := obj.(*Mock); ok { - t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") - obj = m - } - m := obj.(assertExpectationiser) - if !m.AssertExpectations(t) { - t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) - return false - } - } - return true -} - -// AssertExpectations asserts that everything specified with On and Return was -// in fact called as expected. Calls may have occurred in any order. -func (m *Mock) AssertExpectations(t TestingT) bool { - if s, ok := t.(interface{ Skipped() bool }); ok && s.Skipped() { - return true - } - if h, ok := t.(tHelper); ok { - h.Helper() - } - - m.mutex.Lock() - defer m.mutex.Unlock() - var failedExpectations int - - // iterate through each expectation - expectedCalls := m.expectedCalls() - for _, expectedCall := range expectedCalls { - satisfied, reason := m.checkExpectation(expectedCall) - if !satisfied { - failedExpectations++ - t.Logf(reason) - } - } - - if failedExpectations != 0 { - t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo()) - } - - return failedExpectations == 0 -} - -func (m *Mock) checkExpectation(call *Call) (bool, string) { - if !call.optional && !m.methodWasCalled(call.Method, call.Arguments) && call.totalCalls == 0 { - return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) - } - if call.Repeatability > 0 { - return false, fmt.Sprintf("FAIL:\t%s(%s)\n\t\tat: %s", call.Method, call.Arguments.String(), call.callerInfo) - } - return true, fmt.Sprintf("PASS:\t%s(%s)", call.Method, call.Arguments.String()) -} - -// AssertNumberOfCalls asserts that the method was called expectedCalls times. -func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - var actualCalls int - for _, call := range m.calls() { - if call.Method == methodName { - actualCalls++ - } - } - return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) of method %s does not match the actual number of calls (%d).", expectedCalls, methodName, actualCalls)) -} - -// AssertCalled asserts that the method was called. -// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. -func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - if !m.methodWasCalled(methodName, arguments) { - var calledWithArgs []string - for _, call := range m.calls() { - calledWithArgs = append(calledWithArgs, fmt.Sprintf("%v", call.Arguments)) - } - if len(calledWithArgs) == 0 { - return assert.Fail(t, "Should have called with given arguments", - fmt.Sprintf("Expected %q to have been called with:\n%v\nbut no actual calls happened", methodName, arguments)) - } - return assert.Fail(t, "Should have called with given arguments", - fmt.Sprintf("Expected %q to have been called with:\n%v\nbut actual calls were:\n %v", methodName, arguments, strings.Join(calledWithArgs, "\n"))) - } - return true -} - -// AssertNotCalled asserts that the method was not called. -// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. -func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - if m.methodWasCalled(methodName, arguments) { - return assert.Fail(t, "Should not have called with given arguments", - fmt.Sprintf("Expected %q to not have been called with:\n%v\nbut actually it was.", methodName, arguments)) - } - return true -} - -// IsMethodCallable checking that the method can be called -// If the method was called more than `Repeatability` return false -func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - m.mutex.Lock() - defer m.mutex.Unlock() - - for _, v := range m.ExpectedCalls { - if v.Method != methodName { - continue - } - if len(arguments) != len(v.Arguments) { - continue - } - if v.Repeatability < v.totalCalls { - continue - } - if isArgsEqual(v.Arguments, arguments) { - return true - } - } - return false -} - -// isArgsEqual compares arguments -func isArgsEqual(expected Arguments, args []interface{}) bool { - if len(expected) != len(args) { - return false - } - for i, v := range args { - if !reflect.DeepEqual(expected[i], v) { - return false - } - } - return true -} - -func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool { - for _, call := range m.calls() { - if call.Method == methodName { - - _, differences := Arguments(expected).Diff(call.Arguments) - - if differences == 0 { - // found the expected call - return true - } - - } - } - // we didn't find the expected call - return false -} - -func (m *Mock) expectedCalls() []*Call { - return append([]*Call{}, m.ExpectedCalls...) -} - -func (m *Mock) calls() []Call { - return append([]Call{}, m.Calls...) -} - -/* - Arguments -*/ - -// Arguments holds an array of method arguments or return values. -type Arguments []interface{} - -const ( - // Anything is used in Diff and Assert when the argument being tested - // shouldn't be taken into consideration. - Anything = "mock.Anything" -) - -// AnythingOfTypeArgument contains the type of an argument -// for use when type checking. Used in [Arguments.Diff] and [Arguments.Assert]. -// -// Deprecated: this is an implementation detail that must not be used. Use the [AnythingOfType] constructor instead, example: -// -// m.On("Do", mock.AnythingOfType("string")) -// -// All explicit type declarations can be replaced with interface{} as is expected by [Mock.On], example: -// -// func anyString interface{} { -// return mock.AnythingOfType("string") -// } -type AnythingOfTypeArgument = anythingOfTypeArgument - -// anythingOfTypeArgument is a string that contains the type of an argument -// for use when type checking. Used in Diff and Assert. -type anythingOfTypeArgument string - -// AnythingOfType returns a special value containing the -// name of the type to check for. The type name will be matched against the type name returned by [reflect.Type.String]. -// -// Used in Diff and Assert. -// -// For example: -// -// args.Assert(t, AnythingOfType("string"), AnythingOfType("int")) -func AnythingOfType(t string) AnythingOfTypeArgument { - return anythingOfTypeArgument(t) -} - -// IsTypeArgument is a struct that contains the type of an argument -// for use when type checking. This is an alternative to [AnythingOfType]. -// Used in [Arguments.Diff] and [Arguments.Assert]. -type IsTypeArgument struct { - t reflect.Type -} - -// IsType returns an IsTypeArgument object containing the type to check for. -// You can provide a zero-value of the type to check. This is an -// alternative to [AnythingOfType]. Used in [Arguments.Diff] and [Arguments.Assert]. -// -// For example: -// -// args.Assert(t, IsType(""), IsType(0)) -func IsType(t interface{}) *IsTypeArgument { - return &IsTypeArgument{t: reflect.TypeOf(t)} -} - -// FunctionalOptionsArgument contains a list of functional options arguments -// expected for use when matching a list of arguments. -type FunctionalOptionsArgument struct { - values []interface{} -} - -// String returns the string representation of FunctionalOptionsArgument -func (f *FunctionalOptionsArgument) String() string { - var name string - if len(f.values) > 0 { - name = "[]" + reflect.TypeOf(f.values[0]).String() - } - - return strings.Replace(fmt.Sprintf("%#v", f.values), "[]interface {}", name, 1) -} - -// FunctionalOptions returns an [FunctionalOptionsArgument] object containing -// the expected functional-options to check for. -// -// For example: -// -// args.Assert(t, FunctionalOptions(foo.Opt1("strValue"), foo.Opt2(613))) -func FunctionalOptions(values ...interface{}) *FunctionalOptionsArgument { - return &FunctionalOptionsArgument{ - values: values, - } -} - -// argumentMatcher performs custom argument matching, returning whether or -// not the argument is matched by the expectation fixture function. -type argumentMatcher struct { - // fn is a function which accepts one argument, and returns a bool. - fn reflect.Value -} - -func (f argumentMatcher) Matches(argument interface{}) bool { - expectType := f.fn.Type().In(0) - expectTypeNilSupported := false - switch expectType.Kind() { - case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Ptr: - expectTypeNilSupported = true - } - - argType := reflect.TypeOf(argument) - var arg reflect.Value - if argType == nil { - arg = reflect.New(expectType).Elem() - } else { - arg = reflect.ValueOf(argument) - } - - if argType == nil && !expectTypeNilSupported { - panic(errors.New("attempting to call matcher with nil for non-nil expected type")) - } - if argType == nil || argType.AssignableTo(expectType) { - result := f.fn.Call([]reflect.Value{arg}) - return result[0].Bool() - } - return false -} - -func (f argumentMatcher) String() string { - return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String()) -} - -// MatchedBy can be used to match a mock call based on only certain properties -// from a complex struct or some calculation. It takes a function that will be -// evaluated with the called argument and will return true when there's a match -// and false otherwise. -// -// Example: -// -// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" })) -// -// fn must be a function accepting a single argument (of the expected type) -// which returns a bool. If fn doesn't match the required signature, -// MatchedBy() panics. -func MatchedBy(fn interface{}) argumentMatcher { - fnType := reflect.TypeOf(fn) - - if fnType.Kind() != reflect.Func { - panic(fmt.Sprintf("assert: arguments: %s is not a func", fn)) - } - if fnType.NumIn() != 1 { - panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn)) - } - if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool { - panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn)) - } - - return argumentMatcher{fn: reflect.ValueOf(fn)} -} - -// Get Returns the argument at the specified index. -func (args Arguments) Get(index int) interface{} { - if index+1 > len(args) { - panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args))) - } - return args[index] -} - -// Is gets whether the objects match the arguments specified. -func (args Arguments) Is(objects ...interface{}) bool { - for i, obj := range args { - if obj != objects[i] { - return false - } - } - return true -} - -// Diff gets a string describing the differences between the arguments -// and the specified objects. -// -// Returns the diff string and number of differences found. -func (args Arguments) Diff(objects []interface{}) (string, int) { - // TODO: could return string as error and nil for No difference - - output := "\n" - var differences int - - maxArgCount := len(args) - if len(objects) > maxArgCount { - maxArgCount = len(objects) - } - - for i := 0; i < maxArgCount; i++ { - var actual, expected interface{} - var actualFmt, expectedFmt string - - if len(objects) <= i { - actual = "(Missing)" - actualFmt = "(Missing)" - } else { - actual = objects[i] - actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual) - } - - if len(args) <= i { - expected = "(Missing)" - expectedFmt = "(Missing)" - } else { - expected = args[i] - expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected) - } - - if matcher, ok := expected.(argumentMatcher); ok { - var matches bool - func() { - defer func() { - if r := recover(); r != nil { - actualFmt = fmt.Sprintf("panic in argument matcher: %v", r) - } - }() - matches = matcher.Matches(actual) - }() - if matches { - output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) - } else { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) - } - } else { - switch expected := expected.(type) { - case anythingOfTypeArgument: - // type checking - if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) - } - case *IsTypeArgument: - actualT := reflect.TypeOf(actual) - if actualT != expected.t { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) - } - case *FunctionalOptionsArgument: - var name string - if len(expected.values) > 0 { - name = "[]" + reflect.TypeOf(expected.values[0]).String() - } - - const tName = "[]interface{}" - if name != reflect.TypeOf(actual).String() && len(expected.values) != 0 { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) - } else { - if ef, af := assertOpts(expected.values, actual); ef == "" && af == "" { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) - } - } - - default: - if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) - } - } - } - - } - - if differences == 0 { - return "No differences.", differences - } - - return output, differences -} - -// Assert compares the arguments with the specified objects and fails if -// they do not exactly match. -func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - // get the differences - diff, diffCount := args.Diff(objects) - - if diffCount == 0 { - return true - } - - // there are differences... report them... - t.Logf(diff) - t.Errorf("%sArguments do not match.", assert.CallerInfo()) - - return false -} - -// String gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -// -// If no index is provided, String() returns a complete string representation -// of the arguments. -func (args Arguments) String(indexOrNil ...int) string { - if len(indexOrNil) == 0 { - // normal String() method - return a string representation of the args - var argsStr []string - for _, arg := range args { - argsStr = append(argsStr, fmt.Sprintf("%T", arg)) // handles nil nicely - } - return strings.Join(argsStr, ",") - } else if len(indexOrNil) == 1 { - // Index has been specified - get the argument at that index - index := indexOrNil[0] - var s string - var ok bool - if s, ok = args.Get(index).(string); !ok { - panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index))) - } - return s - } - - panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil))) -} - -// Int gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Int(index int) int { - var s int - var ok bool - if s, ok = args.Get(index).(int); !ok { - panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -// Error gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Error(index int) error { - obj := args.Get(index) - var s error - var ok bool - if obj == nil { - return nil - } - if s, ok = obj.(error); !ok { - panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, obj)) - } - return s -} - -// Bool gets the argument at the specified index. Panics if there is no argument, or -// if the argument is of the wrong type. -func (args Arguments) Bool(index int) bool { - var s bool - var ok bool - if s, ok = args.Get(index).(bool); !ok { - panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index))) - } - return s -} - -func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { - t := reflect.TypeOf(v) - k := t.Kind() - - if k == reflect.Ptr { - t = t.Elem() - k = t.Kind() - } - return t, k -} - -func diffArguments(expected Arguments, actual Arguments) string { - if len(expected) != len(actual) { - return fmt.Sprintf("Provided %v arguments, mocked for %v arguments", len(expected), len(actual)) - } - - for x := range expected { - if diffString := diff(expected[x], actual[x]); diffString != "" { - return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString) - } - } - - return "" -} - -// diff returns a diff of both values as long as both are of the same type and -// are a struct, map, slice or array. Otherwise it returns an empty string. -func diff(expected interface{}, actual interface{}) string { - if expected == nil || actual == nil { - return "" - } - - et, ek := typeAndKind(expected) - at, _ := typeAndKind(actual) - - if et != at { - return "" - } - - if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { - return "" - } - - e := spewConfig.Sdump(expected) - a := spewConfig.Sdump(actual) - - diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(e), - B: difflib.SplitLines(a), - FromFile: "Expected", - FromDate: "", - ToFile: "Actual", - ToDate: "", - Context: 1, - }) - - return diff -} - -var spewConfig = spew.ConfigState{ - Indent: " ", - DisablePointerAddresses: true, - DisableCapacities: true, - SortKeys: true, -} - -type tHelper interface { - Helper() -} - -func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { - expectedOpts := reflect.ValueOf(expected) - actualOpts := reflect.ValueOf(actual) - - var expectedFuncs []*runtime.Func - var expectedNames []string - for i := 0; i < expectedOpts.Len(); i++ { - f := runtimeFunc(expectedOpts.Index(i).Interface()) - expectedFuncs = append(expectedFuncs, f) - expectedNames = append(expectedNames, funcName(f)) - } - var actualFuncs []*runtime.Func - var actualNames []string - for i := 0; i < actualOpts.Len(); i++ { - f := runtimeFunc(actualOpts.Index(i).Interface()) - actualFuncs = append(actualFuncs, f) - actualNames = append(actualNames, funcName(f)) - } - - if expectedOpts.Len() != actualOpts.Len() { - expectedFmt = fmt.Sprintf("%v", expectedNames) - actualFmt = fmt.Sprintf("%v", actualNames) - return - } - - for i := 0; i < expectedOpts.Len(); i++ { - if !isFuncSame(expectedFuncs[i], actualFuncs[i]) { - expectedFmt = expectedNames[i] - actualFmt = actualNames[i] - return - } - - expectedOpt := expectedOpts.Index(i).Interface() - actualOpt := actualOpts.Index(i).Interface() - - ot := reflect.TypeOf(expectedOpt) - var expectedValues []reflect.Value - var actualValues []reflect.Value - if ot.NumIn() == 0 { - return - } - - for i := 0; i < ot.NumIn(); i++ { - vt := ot.In(i).Elem() - expectedValues = append(expectedValues, reflect.New(vt)) - actualValues = append(actualValues, reflect.New(vt)) - } - - reflect.ValueOf(expectedOpt).Call(expectedValues) - reflect.ValueOf(actualOpt).Call(actualValues) - - for i := 0; i < ot.NumIn(); i++ { - if expectedArg, actualArg := expectedValues[i].Interface(), actualValues[i].Interface(); !assert.ObjectsAreEqual(expectedArg, actualArg) { - expectedFmt = fmt.Sprintf("%s(%T) -> %#v", expectedNames[i], expectedArg, expectedArg) - actualFmt = fmt.Sprintf("%s(%T) -> %#v", expectedNames[i], actualArg, actualArg) - return - } - } - } - - return "", "" -} - -func runtimeFunc(opt interface{}) *runtime.Func { - return runtime.FuncForPC(reflect.ValueOf(opt).Pointer()) -} - -func funcName(f *runtime.Func) string { - name := f.Name() - trimmed := strings.TrimSuffix(path.Base(name), path.Ext(name)) - splitted := strings.Split(trimmed, ".") - - if len(splitted) == 0 { - return trimmed - } - - return splitted[len(splitted)-1] -} - -func isFuncSame(f1, f2 *runtime.Func) bool { - f1File, f1Loc := f1.FileLine(f1.Entry()) - f2File, f2Loc := f2.FileLine(f2.Entry()) - - return f1File == f2File && f1Loc == f2Loc -} diff --git a/vendor/go.uber.org/mock/AUTHORS b/vendor/go.uber.org/mock/AUTHORS new file mode 100644 index 0000000000..660b8ccc8a --- /dev/null +++ b/vendor/go.uber.org/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/go.uber.org/mock/LICENSE b/vendor/go.uber.org/mock/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/go.uber.org/mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/go.uber.org/mock/gomock/call.go b/vendor/go.uber.org/mock/gomock/call.go new file mode 100644 index 0000000000..e1fe222afc --- /dev/null +++ b/vendor/go.uber.org/mock/gomock/call.go @@ -0,0 +1,506 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// Call represents an expected call to a mock. +type Call struct { + t TestHelper // for triggering test failures on invalid call setup + + receiver any // the receiver of the method call + method string // the name of the method + methodType reflect.Type // the type of the method + args []Matcher // the args + origin string // file and line number of call setup + + preReqs []*Call // prerequisite calls + + // Expectations + minCalls, maxCalls int + + numCalls int // actual number made + + // actions are called when this Call is called. Each action gets the args and + // can set the return values by returning a non-nil slice. Actions run in the + // order they are created. + actions []func([]any) []any +} + +// newCall creates a *Call. It requires the method type in order to support +// unexported methods. +func newCall(t TestHelper, receiver any, method string, methodType reflect.Type, args ...any) *Call { + t.Helper() + + // TODO: check arity, types. + mArgs := make([]Matcher, len(args)) + for i, arg := range args { + if m, ok := arg.(Matcher); ok { + mArgs[i] = m + } else if arg == nil { + // Handle nil specially so that passing a nil interface value + // will match the typed nils of concrete args. + mArgs[i] = Nil() + } else { + mArgs[i] = Eq(arg) + } + } + + // callerInfo's skip should be updated if the number of calls between the user's test + // and this line changes, i.e. this code is wrapped in another anonymous function. + // 0 is us, 1 is RecordCallWithMethodType(), 2 is the generated recorder, and 3 is the user's test. + origin := callerInfo(3) + actions := []func([]any) []any{func([]any) []any { + // Synthesize the zero value for each of the return args' types. + rets := make([]any, methodType.NumOut()) + for i := 0; i < methodType.NumOut(); i++ { + rets[i] = reflect.Zero(methodType.Out(i)).Interface() + } + return rets + }} + return &Call{ + t: t, receiver: receiver, method: method, methodType: methodType, + args: mArgs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions, + } +} + +// AnyTimes allows the expectation to be called 0 or more times +func (c *Call) AnyTimes() *Call { + c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity + return c +} + +// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called or if MaxTimes +// was previously called with 1, MinTimes also sets the maximum number of calls to infinity. +func (c *Call) MinTimes(n int) *Call { + c.minCalls = n + if c.maxCalls == 1 { + c.maxCalls = 1e8 + } + return c +} + +// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called or if MinTimes was +// previously called with 1, MaxTimes also sets the minimum number of calls to 0. +func (c *Call) MaxTimes(n int) *Call { + c.maxCalls = n + if c.minCalls == 1 { + c.minCalls = 0 + } + return c +} + +// DoAndReturn declares the action to run when the call is matched. +// The return values from this function are returned by the mocked function. +// It takes an any argument to support n-arity functions. +// The anonymous function must match the function signature mocked method. +func (c *Call) DoAndReturn(f any) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []any) []any { + c.t.Helper() + ft := v.Type() + if c.methodType.NumIn() != ft.NumIn() { + if ft.IsVariadic() { + c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v The function signature must match the mocked method, a variadic function cannot be used.", + c.receiver, c.method) + } else { + c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin) + } + return nil + } + vArgs := make([]reflect.Value, len(args)) + for i := 0; i < len(args); i++ { + if args[i] != nil { + vArgs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vArgs[i] = reflect.Zero(ft.In(i)) + } + } + vRets := v.Call(vArgs) + rets := make([]any, len(vRets)) + for i, ret := range vRets { + rets[i] = ret.Interface() + } + return rets + }) + return c +} + +// Do declares the action to run when the call is matched. The function's +// return values are ignored to retain backward compatibility. To use the +// return values call DoAndReturn. +// It takes an any argument to support n-arity functions. +// The anonymous function must match the function signature mocked method. +func (c *Call) Do(f any) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []any) []any { + c.t.Helper() + ft := v.Type() + if c.methodType.NumIn() != ft.NumIn() { + if ft.IsVariadic() { + c.t.Fatalf("wrong number of arguments in Do func for %T.%v The function signature must match the mocked method, a variadic function cannot be used.", + c.receiver, c.method) + } else { + c.t.Fatalf("wrong number of arguments in Do func for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin) + } + return nil + } + vArgs := make([]reflect.Value, len(args)) + for i := 0; i < len(args); i++ { + if args[i] != nil { + vArgs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vArgs[i] = reflect.Zero(ft.In(i)) + } + } + v.Call(vArgs) + return nil + }) + return c +} + +// Return declares the values to be returned by the mocked function call. +func (c *Call) Return(rets ...any) *Call { + c.t.Helper() + + mt := c.methodType + if len(rets) != mt.NumOut() { + c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, len(rets), mt.NumOut(), c.origin) + } + for i, ret := range rets { + if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { + // Identical types; nothing to do. + } else if got == nil { + // Nil needs special handling. + switch want.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + // ok + default: + c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", + i, c.receiver, c.method, want, c.origin) + } + } else if got.AssignableTo(want) { + // Assignable type relation. Make the assignment now so that the generated code + // can return the values with a type assertion. + v := reflect.New(want).Elem() + v.Set(reflect.ValueOf(ret)) + rets[i] = v.Interface() + } else { + c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", + i, c.receiver, c.method, got, want, c.origin) + } + } + + c.addAction(func([]any) []any { + return rets + }) + + return c +} + +// Times declares the exact number of times a function call is expected to be executed. +func (c *Call) Times(n int) *Call { + c.minCalls, c.maxCalls = n, n + return c +} + +// SetArg declares an action that will set the nth argument's value, +// indirected through a pointer. Or, in the case of a slice and map, SetArg +// will copy value's elements/key-value pairs into the nth argument. +func (c *Call) SetArg(n int, value any) *Call { + c.t.Helper() + + mt := c.methodType + // TODO: This will break on variadic methods. + // We will need to check those at invocation time. + if n < 0 || n >= mt.NumIn() { + c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", + n, mt.NumIn(), c.origin) + } + // Permit setting argument through an interface. + // In the interface case, we don't (nay, can't) check the type here. + at := mt.In(n) + switch at.Kind() { + case reflect.Ptr: + dt := at.Elem() + if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { + c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", + n, vt, dt, c.origin) + } + case reflect.Interface, reflect.Slice, reflect.Map: + // nothing to do + default: + c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice non-map type %v [%s]", + n, at, c.origin) + } + + c.addAction(func(args []any) []any { + v := reflect.ValueOf(value) + switch reflect.TypeOf(args[n]).Kind() { + case reflect.Slice: + setSlice(args[n], v) + case reflect.Map: + setMap(args[n], v) + default: + reflect.ValueOf(args[n]).Elem().Set(v) + } + return nil + }) + return c +} + +// isPreReq returns true if other is a direct or indirect prerequisite to c. +func (c *Call) isPreReq(other *Call) bool { + for _, preReq := range c.preReqs { + if other == preReq || preReq.isPreReq(other) { + return true + } + } + return false +} + +// After declares that the call may only match after preReq has been exhausted. +func (c *Call) After(preReq *Call) *Call { + c.t.Helper() + + if c == preReq { + c.t.Fatalf("A call isn't allowed to be its own prerequisite") + } + if preReq.isPreReq(c) { + c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) + } + + c.preReqs = append(c.preReqs, preReq) + return c +} + +// Returns true if the minimum number of calls have been made. +func (c *Call) satisfied() bool { + return c.numCalls >= c.minCalls +} + +// Returns true if the maximum number of calls have been made. +func (c *Call) exhausted() bool { + return c.numCalls >= c.maxCalls +} + +func (c *Call) String() string { + args := make([]string, len(c.args)) + for i, arg := range c.args { + args[i] = arg.String() + } + arguments := strings.Join(args, ", ") + return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) +} + +// Tests if the given call matches the expected call. +// If yes, returns nil. If no, returns error with message explaining why it does not match. +func (c *Call) matches(args []any) error { + if !c.methodType.IsVariadic() { + if len(args) != len(c.args) { + return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + + for i, m := range c.args { + if !m.Matches(args[i]) { + return fmt.Errorf( + "expected call at %s doesn't match the argument at index %d.\nGot: %v\nWant: %v", + c.origin, i, formatGottenArg(m, args[i]), m, + ) + } + } + } else { + if len(c.args) < c.methodType.NumIn()-1 { + return fmt.Errorf("expected call at %s has the wrong number of matchers. Got: %d, want: %d", + c.origin, len(c.args), c.methodType.NumIn()-1) + } + if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) { + return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + if len(args) < len(c.args)-1 { + return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d", + c.origin, len(args), len(c.args)-1) + } + + for i, m := range c.args { + if i < c.methodType.NumIn()-1 { + // Non-variadic args + if !m.Matches(args[i]) { + return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), formatGottenArg(m, args[i]), m) + } + continue + } + // The last arg has a possibility of a variadic argument, so let it branch + + // sample: Foo(a int, b int, c ...int) + if i < len(c.args) && i < len(args) { + if m.Matches(args[i]) { + // Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC) + // Got Foo(a, b) want Foo(matcherA, matcherB) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD) + continue + } + } + + // The number of actual args don't match the number of matchers, + // or the last matcher is a slice and the last arg is not. + // If this function still matches it is because the last matcher + // matches all the remaining arguments or the lack of any. + // Convert the remaining arguments, if any, into a slice of the + // expected type. + vArgsType := c.methodType.In(c.methodType.NumIn() - 1) + vArgs := reflect.MakeSlice(vArgsType, 0, len(args)-i) + for _, arg := range args[i:] { + vArgs = reflect.Append(vArgs, reflect.ValueOf(arg)) + } + if m.Matches(vArgs.Interface()) { + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher) + break + } + // Wrong number of matchers or not match. Fail. + // Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB) + + return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), formatGottenArg(m, args[i:]), c.args[i]) + } + } + + // Check that all prerequisite calls have been satisfied. + for _, preReqCall := range c.preReqs { + if !preReqCall.satisfied() { + return fmt.Errorf("expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", + c.origin, preReqCall, c) + } + } + + // Check that the call is not exhausted. + if c.exhausted() { + return fmt.Errorf("expected call at %s has already been called the max number of times", c.origin) + } + + return nil +} + +// dropPrereqs tells the expected Call to not re-check prerequisite calls any +// longer, and to return its current set. +func (c *Call) dropPrereqs() (preReqs []*Call) { + preReqs = c.preReqs + c.preReqs = nil + return +} + +func (c *Call) call() []func([]any) []any { + c.numCalls++ + return c.actions +} + +// InOrder declares that the given calls should occur in order. +// It panics if the type of any of the arguments isn't *Call or a generated +// mock with an embedded *Call. +func InOrder(args ...any) { + calls := make([]*Call, 0, len(args)) + for i := 0; i < len(args); i++ { + if call := getCall(args[i]); call != nil { + calls = append(calls, call) + continue + } + panic(fmt.Sprintf( + "invalid argument at position %d of type %T, InOrder expects *gomock.Call or generated mock types with an embedded *gomock.Call", + i, + args[i], + )) + } + for i := 1; i < len(calls); i++ { + calls[i].After(calls[i-1]) + } +} + +// getCall checks if the parameter is a *Call or a generated struct +// that wraps a *Call and returns the *Call pointer - if neither, it returns nil. +func getCall(arg any) *Call { + if call, ok := arg.(*Call); ok { + return call + } + t := reflect.ValueOf(arg) + if t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface { + return nil + } + t = t.Elem() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if !f.CanInterface() { + continue + } + if call, ok := f.Interface().(*Call); ok { + return call + } + } + return nil +} + +func setSlice(arg any, v reflect.Value) { + va := reflect.ValueOf(arg) + for i := 0; i < v.Len(); i++ { + va.Index(i).Set(v.Index(i)) + } +} + +func setMap(arg any, v reflect.Value) { + va := reflect.ValueOf(arg) + for _, e := range va.MapKeys() { + va.SetMapIndex(e, reflect.Value{}) + } + for _, e := range v.MapKeys() { + va.SetMapIndex(e, v.MapIndex(e)) + } +} + +func (c *Call) addAction(action func([]any) []any) { + c.actions = append(c.actions, action) +} + +func formatGottenArg(m Matcher, arg any) string { + got := fmt.Sprintf("%v (%T)", arg, arg) + if gs, ok := m.(GotFormatter); ok { + got = gs.Got(arg) + } + return got +} diff --git a/vendor/go.uber.org/mock/gomock/callset.go b/vendor/go.uber.org/mock/gomock/callset.go new file mode 100644 index 0000000000..f5cc592d6f --- /dev/null +++ b/vendor/go.uber.org/mock/gomock/callset.go @@ -0,0 +1,164 @@ +// Copyright 2011 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "bytes" + "errors" + "fmt" + "sync" +) + +// callSet represents a set of expected calls, indexed by receiver and method +// name. +type callSet struct { + // Calls that are still expected. + expected map[callSetKey][]*Call + expectedMu *sync.Mutex + // Calls that have been exhausted. + exhausted map[callSetKey][]*Call + // when set to true, existing call expectations are overridden when new call expectations are made + allowOverride bool +} + +// callSetKey is the key in the maps in callSet +type callSetKey struct { + receiver any + fname string +} + +func newCallSet() *callSet { + return &callSet{ + expected: make(map[callSetKey][]*Call), + expectedMu: &sync.Mutex{}, + exhausted: make(map[callSetKey][]*Call), + } +} + +func newOverridableCallSet() *callSet { + return &callSet{ + expected: make(map[callSetKey][]*Call), + expectedMu: &sync.Mutex{}, + exhausted: make(map[callSetKey][]*Call), + allowOverride: true, + } +} + +// Add adds a new expected call. +func (cs callSet) Add(call *Call) { + key := callSetKey{call.receiver, call.method} + + cs.expectedMu.Lock() + defer cs.expectedMu.Unlock() + + m := cs.expected + if call.exhausted() { + m = cs.exhausted + } + if cs.allowOverride { + m[key] = make([]*Call, 0) + } + + m[key] = append(m[key], call) +} + +// Remove removes an expected call. +func (cs callSet) Remove(call *Call) { + key := callSetKey{call.receiver, call.method} + + cs.expectedMu.Lock() + defer cs.expectedMu.Unlock() + + calls := cs.expected[key] + for i, c := range calls { + if c == call { + // maintain order for remaining calls + cs.expected[key] = append(calls[:i], calls[i+1:]...) + cs.exhausted[key] = append(cs.exhausted[key], call) + break + } + } +} + +// FindMatch searches for a matching call. Returns error with explanation message if no call matched. +func (cs callSet) FindMatch(receiver any, method string, args []any) (*Call, error) { + key := callSetKey{receiver, method} + + cs.expectedMu.Lock() + defer cs.expectedMu.Unlock() + + // Search through the expected calls. + expected := cs.expected[key] + var callsErrors bytes.Buffer + for _, call := range expected { + err := call.matches(args) + if err != nil { + _, _ = fmt.Fprintf(&callsErrors, "\n%v", err) + } else { + return call, nil + } + } + + // If we haven't found a match then search through the exhausted calls so we + // get useful error messages. + exhausted := cs.exhausted[key] + for _, call := range exhausted { + if err := call.matches(args); err != nil { + _, _ = fmt.Fprintf(&callsErrors, "\n%v", err) + continue + } + _, _ = fmt.Fprintf( + &callsErrors, "all expected calls for method %q have been exhausted", method, + ) + } + + if len(expected)+len(exhausted) == 0 { + _, _ = fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method) + } + + return nil, errors.New(callsErrors.String()) +} + +// Failures returns the calls that are not satisfied. +func (cs callSet) Failures() []*Call { + cs.expectedMu.Lock() + defer cs.expectedMu.Unlock() + + failures := make([]*Call, 0, len(cs.expected)) + for _, calls := range cs.expected { + for _, call := range calls { + if !call.satisfied() { + failures = append(failures, call) + } + } + } + return failures +} + +// Satisfied returns true in case all expected calls in this callSet are satisfied. +func (cs callSet) Satisfied() bool { + cs.expectedMu.Lock() + defer cs.expectedMu.Unlock() + + for _, calls := range cs.expected { + for _, call := range calls { + if !call.satisfied() { + return false + } + } + } + + return true +} diff --git a/vendor/go.uber.org/mock/gomock/controller.go b/vendor/go.uber.org/mock/gomock/controller.go new file mode 100644 index 0000000000..674c3298c4 --- /dev/null +++ b/vendor/go.uber.org/mock/gomock/controller.go @@ -0,0 +1,326 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "context" + "fmt" + "reflect" + "runtime" + "sync" +) + +// A TestReporter is something that can be used to report test failures. It +// is satisfied by the standard library's *testing.T. +type TestReporter interface { + Errorf(format string, args ...any) + Fatalf(format string, args ...any) +} + +// TestHelper is a TestReporter that has the Helper method. It is satisfied +// by the standard library's *testing.T. +type TestHelper interface { + TestReporter + Helper() +} + +// cleanuper is used to check if TestHelper also has the `Cleanup` method. A +// common pattern is to pass in a `*testing.T` to +// `NewController(t TestReporter)`. In Go 1.14+, `*testing.T` has a cleanup +// method. This can be utilized to call `Finish()` so the caller of this library +// does not have to. +type cleanuper interface { + Cleanup(func()) +} + +// A Controller represents the top-level control of a mock ecosystem. It +// defines the scope and lifetime of mock objects, as well as their +// expectations. It is safe to call Controller's methods from multiple +// goroutines. Each test should create a new Controller. +// +// func TestFoo(t *testing.T) { +// ctrl := gomock.NewController(t) +// // .. +// } +// +// func TestBar(t *testing.T) { +// t.Run("Sub-Test-1", st) { +// ctrl := gomock.NewController(st) +// // .. +// }) +// t.Run("Sub-Test-2", st) { +// ctrl := gomock.NewController(st) +// // .. +// }) +// }) +type Controller struct { + // T should only be called within a generated mock. It is not intended to + // be used in user code and may be changed in future versions. T is the + // TestReporter passed in when creating the Controller via NewController. + // If the TestReporter does not implement a TestHelper it will be wrapped + // with a nopTestHelper. + T TestHelper + mu sync.Mutex + expectedCalls *callSet + finished bool +} + +// NewController returns a new Controller. It is the preferred way to create a Controller. +// +// Passing [*testing.T] registers cleanup function to automatically call [Controller.Finish] +// when the test and all its subtests complete. +func NewController(t TestReporter, opts ...ControllerOption) *Controller { + h, ok := t.(TestHelper) + if !ok { + h = &nopTestHelper{t} + } + ctrl := &Controller{ + T: h, + expectedCalls: newCallSet(), + } + for _, opt := range opts { + opt.apply(ctrl) + } + if c, ok := isCleanuper(ctrl.T); ok { + c.Cleanup(func() { + ctrl.T.Helper() + ctrl.finish(true, nil) + }) + } + + return ctrl +} + +// ControllerOption configures how a Controller should behave. +type ControllerOption interface { + apply(*Controller) +} + +type overridableExpectationsOption struct{} + +// WithOverridableExpectations allows for overridable call expectations +// i.e., subsequent call expectations override existing call expectations +func WithOverridableExpectations() overridableExpectationsOption { + return overridableExpectationsOption{} +} + +func (o overridableExpectationsOption) apply(ctrl *Controller) { + ctrl.expectedCalls = newOverridableCallSet() +} + +type cancelReporter struct { + t TestHelper + cancel func() +} + +func (r *cancelReporter) Errorf(format string, args ...any) { + r.t.Errorf(format, args...) +} + +func (r *cancelReporter) Fatalf(format string, args ...any) { + defer r.cancel() + r.t.Fatalf(format, args...) +} + +func (r *cancelReporter) Helper() { + r.t.Helper() +} + +// WithContext returns a new Controller and a Context, which is cancelled on any +// fatal failure. +func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) { + h, ok := t.(TestHelper) + if !ok { + h = &nopTestHelper{t: t} + } + + ctx, cancel := context.WithCancel(ctx) + return NewController(&cancelReporter{t: h, cancel: cancel}), ctx +} + +type nopTestHelper struct { + t TestReporter +} + +func (h *nopTestHelper) Errorf(format string, args ...any) { + h.t.Errorf(format, args...) +} + +func (h *nopTestHelper) Fatalf(format string, args ...any) { + h.t.Fatalf(format, args...) +} + +func (h nopTestHelper) Helper() {} + +// RecordCall is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCall(receiver any, method string, args ...any) *Call { + ctrl.T.Helper() + + recv := reflect.ValueOf(receiver) + for i := 0; i < recv.Type().NumMethod(); i++ { + if recv.Type().Method(i).Name == method { + return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...) + } + } + ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver) + panic("unreachable") +} + +// RecordCallWithMethodType is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCallWithMethodType(receiver any, method string, methodType reflect.Type, args ...any) *Call { + ctrl.T.Helper() + + call := newCall(ctrl.T, receiver, method, methodType, args...) + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + ctrl.expectedCalls.Add(call) + + return call +} + +// Call is called by a mock. It should not be called by user code. +func (ctrl *Controller) Call(receiver any, method string, args ...any) []any { + ctrl.T.Helper() + + // Nest this code so we can use defer to make sure the lock is released. + actions := func() []func([]any) []any { + ctrl.T.Helper() + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) + if err != nil { + // callerInfo's skip should be updated if the number of calls between the user's test + // and this line changes, i.e. this code is wrapped in another anonymous function. + // 0 is us, 1 is controller.Call(), 2 is the generated mock, and 3 is the user's test. + origin := callerInfo(3) + stringArgs := make([]string, len(args)) + for i, arg := range args { + stringArgs[i] = getString(arg) + } + ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, stringArgs, origin, err) + } + + // Two things happen here: + // * the matching call no longer needs to check prerequisite calls, + // * and the prerequisite calls are no longer expected, so remove them. + preReqCalls := expected.dropPrereqs() + for _, preReqCall := range preReqCalls { + ctrl.expectedCalls.Remove(preReqCall) + } + + actions := expected.call() + if expected.exhausted() { + ctrl.expectedCalls.Remove(expected) + } + return actions + }() + + var rets []any + for _, action := range actions { + if r := action(args); r != nil { + rets = r + } + } + + return rets +} + +// Finish checks to see if all the methods that were expected to be called were called. +// It is not idempotent and therefore can only be invoked once. +// +// Note: If you pass a *testing.T into [NewController], you no longer +// need to call ctrl.Finish() in your test methods. +func (ctrl *Controller) Finish() { + // If we're currently panicking, probably because this is a deferred call. + // This must be recovered in the deferred function. + err := recover() + ctrl.finish(false, err) +} + +// Satisfied returns whether all expected calls bound to this Controller have been satisfied. +// Calling Finish is then guaranteed to not fail due to missing calls. +func (ctrl *Controller) Satisfied() bool { + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + return ctrl.expectedCalls.Satisfied() +} + +func (ctrl *Controller) finish(cleanup bool, panicErr any) { + ctrl.T.Helper() + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + if ctrl.finished { + if _, ok := isCleanuper(ctrl.T); !ok { + ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.") + } + return + } + ctrl.finished = true + + // Short-circuit, pass through the panic. + if panicErr != nil { + panic(panicErr) + } + + // Check that all remaining expected calls are satisfied. + failures := ctrl.expectedCalls.Failures() + for _, call := range failures { + ctrl.T.Errorf("missing call(s) to %v", call) + } + if len(failures) != 0 { + if !cleanup { + ctrl.T.Fatalf("aborting test due to missing call(s)") + return + } + ctrl.T.Errorf("aborting test due to missing call(s)") + } +} + +// callerInfo returns the file:line of the call site. skip is the number +// of stack frames to skip when reporting. 0 is callerInfo's call site. +func callerInfo(skip int) string { + if _, file, line, ok := runtime.Caller(skip + 1); ok { + return fmt.Sprintf("%s:%d", file, line) + } + return "unknown file" +} + +// isCleanuper checks it if t's base TestReporter has a Cleanup method. +func isCleanuper(t TestReporter) (cleanuper, bool) { + tr := unwrapTestReporter(t) + c, ok := tr.(cleanuper) + return c, ok +} + +// unwrapTestReporter unwraps TestReporter to the base implementation. +func unwrapTestReporter(t TestReporter) TestReporter { + tr := t + switch nt := t.(type) { + case *cancelReporter: + tr = nt.t + if h, check := tr.(*nopTestHelper); check { + tr = h.t + } + case *nopTestHelper: + tr = nt.t + default: + // not wrapped + } + return tr +} diff --git a/vendor/go.uber.org/mock/gomock/doc.go b/vendor/go.uber.org/mock/gomock/doc.go new file mode 100644 index 0000000000..696dda3882 --- /dev/null +++ b/vendor/go.uber.org/mock/gomock/doc.go @@ -0,0 +1,60 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package gomock is a mock framework for Go. +// +// Standard usage: +// +// (1) Define an interface that you wish to mock. +// type MyInterface interface { +// SomeMethod(x int64, y string) +// } +// (2) Use mockgen to generate a mock from the interface. +// (3) Use the mock in a test: +// func TestMyThing(t *testing.T) { +// mockCtrl := gomock.NewController(t) +// mockObj := something.NewMockMyInterface(mockCtrl) +// mockObj.EXPECT().SomeMethod(4, "blah") +// // pass mockObj to a real object and play with it. +// } +// +// By default, expected calls are not enforced to run in any particular order. +// Call order dependency can be enforced by use of InOrder and/or Call.After. +// Call.After can create more varied call order dependencies, but InOrder is +// often more convenient. +// +// The following examples create equivalent call order dependencies. +// +// Example of using Call.After to chain expected call order: +// +// firstCall := mockObj.EXPECT().SomeMethod(1, "first") +// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall) +// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall) +// +// Example of using InOrder to declare expected call order: +// +// gomock.InOrder( +// mockObj.EXPECT().SomeMethod(1, "first"), +// mockObj.EXPECT().SomeMethod(2, "second"), +// mockObj.EXPECT().SomeMethod(3, "third"), +// ) +// +// The standard TestReporter most users will pass to `NewController` is a +// `*testing.T` from the context of the test. Note that this will use the +// standard `t.Error` and `t.Fatal` methods to report what happened in the test. +// In some cases this can leave your testing package in a weird state if global +// state is used since `t.Fatal` is like calling panic in the middle of a +// function. In these cases it is recommended that you pass in your own +// `TestReporter`. +package gomock diff --git a/vendor/go.uber.org/mock/gomock/matchers.go b/vendor/go.uber.org/mock/gomock/matchers.go new file mode 100644 index 0000000000..d52495f39e --- /dev/null +++ b/vendor/go.uber.org/mock/gomock/matchers.go @@ -0,0 +1,447 @@ +// Copyright 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gomock + +import ( + "fmt" + "reflect" + "regexp" + "strings" +) + +// A Matcher is a representation of a class of values. +// It is used to represent the valid or expected arguments to a mocked method. +type Matcher interface { + // Matches returns whether x is a match. + Matches(x any) bool + + // String describes what the matcher matches. + String() string +} + +// WantFormatter modifies the given Matcher's String() method to the given +// Stringer. This allows for control on how the "Want" is formatted when +// printing . +func WantFormatter(s fmt.Stringer, m Matcher) Matcher { + type matcher interface { + Matches(x any) bool + } + + return struct { + matcher + fmt.Stringer + }{ + matcher: m, + Stringer: s, + } +} + +// StringerFunc type is an adapter to allow the use of ordinary functions as +// a Stringer. If f is a function with the appropriate signature, +// StringerFunc(f) is a Stringer that calls f. +type StringerFunc func() string + +// String implements fmt.Stringer. +func (f StringerFunc) String() string { + return f() +} + +// GotFormatter is used to better print failure messages. If a matcher +// implements GotFormatter, it will use the result from Got when printing +// the failure message. +type GotFormatter interface { + // Got is invoked with the received value. The result is used when + // printing the failure message. + Got(got any) string +} + +// GotFormatterFunc type is an adapter to allow the use of ordinary +// functions as a GotFormatter. If f is a function with the appropriate +// signature, GotFormatterFunc(f) is a GotFormatter that calls f. +type GotFormatterFunc func(got any) string + +// Got implements GotFormatter. +func (f GotFormatterFunc) Got(got any) string { + return f(got) +} + +// GotFormatterAdapter attaches a GotFormatter to a Matcher. +func GotFormatterAdapter(s GotFormatter, m Matcher) Matcher { + return struct { + GotFormatter + Matcher + }{ + GotFormatter: s, + Matcher: m, + } +} + +type anyMatcher struct{} + +func (anyMatcher) Matches(any) bool { + return true +} + +func (anyMatcher) String() string { + return "is anything" +} + +type condMatcher[T any] struct { + fn func(x T) bool +} + +func (c condMatcher[T]) Matches(x any) bool { + typed, ok := x.(T) + if !ok { + return false + } + return c.fn(typed) +} + +func (c condMatcher[T]) String() string { + return "adheres to a custom condition" +} + +type eqMatcher struct { + x any +} + +func (e eqMatcher) Matches(x any) bool { + // In case, some value is nil + if e.x == nil || x == nil { + return reflect.DeepEqual(e.x, x) + } + + // Check if types assignable and convert them to common type + x1Val := reflect.ValueOf(e.x) + x2Val := reflect.ValueOf(x) + + if x1Val.Type().AssignableTo(x2Val.Type()) { + x1ValConverted := x1Val.Convert(x2Val.Type()) + return reflect.DeepEqual(x1ValConverted.Interface(), x2Val.Interface()) + } + + return false +} + +func (e eqMatcher) String() string { + return fmt.Sprintf("is equal to %s (%T)", getString(e.x), e.x) +} + +type nilMatcher struct{} + +func (nilMatcher) Matches(x any) bool { + if x == nil { + return true + } + + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice: + return v.IsNil() + } + + return false +} + +func (nilMatcher) String() string { + return "is nil" +} + +type notMatcher struct { + m Matcher +} + +func (n notMatcher) Matches(x any) bool { + return !n.m.Matches(x) +} + +func (n notMatcher) String() string { + return "not(" + n.m.String() + ")" +} + +type regexMatcher struct { + regex *regexp.Regexp +} + +func (m regexMatcher) Matches(x any) bool { + switch t := x.(type) { + case string: + return m.regex.MatchString(t) + case []byte: + return m.regex.Match(t) + default: + return false + } +} + +func (m regexMatcher) String() string { + return "matches regex " + m.regex.String() +} + +type assignableToTypeOfMatcher struct { + targetType reflect.Type +} + +func (m assignableToTypeOfMatcher) Matches(x any) bool { + return reflect.TypeOf(x).AssignableTo(m.targetType) +} + +func (m assignableToTypeOfMatcher) String() string { + return "is assignable to " + m.targetType.Name() +} + +type anyOfMatcher struct { + matchers []Matcher +} + +func (am anyOfMatcher) Matches(x any) bool { + for _, m := range am.matchers { + if m.Matches(x) { + return true + } + } + return false +} + +func (am anyOfMatcher) String() string { + ss := make([]string, 0, len(am.matchers)) + for _, matcher := range am.matchers { + ss = append(ss, matcher.String()) + } + return strings.Join(ss, " | ") +} + +type allMatcher struct { + matchers []Matcher +} + +func (am allMatcher) Matches(x any) bool { + for _, m := range am.matchers { + if !m.Matches(x) { + return false + } + } + return true +} + +func (am allMatcher) String() string { + ss := make([]string, 0, len(am.matchers)) + for _, matcher := range am.matchers { + ss = append(ss, matcher.String()) + } + return strings.Join(ss, "; ") +} + +type lenMatcher struct { + i int +} + +func (m lenMatcher) Matches(x any) bool { + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == m.i + default: + return false + } +} + +func (m lenMatcher) String() string { + return fmt.Sprintf("has length %d", m.i) +} + +type inAnyOrderMatcher struct { + x any +} + +func (m inAnyOrderMatcher) Matches(x any) bool { + given, ok := m.prepareValue(x) + if !ok { + return false + } + wanted, ok := m.prepareValue(m.x) + if !ok { + return false + } + + if given.Len() != wanted.Len() { + return false + } + + usedFromGiven := make([]bool, given.Len()) + foundFromWanted := make([]bool, wanted.Len()) + for i := 0; i < wanted.Len(); i++ { + wantedMatcher := Eq(wanted.Index(i).Interface()) + for j := 0; j < given.Len(); j++ { + if usedFromGiven[j] { + continue + } + if wantedMatcher.Matches(given.Index(j).Interface()) { + foundFromWanted[i] = true + usedFromGiven[j] = true + break + } + } + } + + missingFromWanted := 0 + for _, found := range foundFromWanted { + if !found { + missingFromWanted++ + } + } + extraInGiven := 0 + for _, used := range usedFromGiven { + if !used { + extraInGiven++ + } + } + + return extraInGiven == 0 && missingFromWanted == 0 +} + +func (m inAnyOrderMatcher) prepareValue(x any) (reflect.Value, bool) { + xValue := reflect.ValueOf(x) + switch xValue.Kind() { + case reflect.Slice, reflect.Array: + return xValue, true + default: + return reflect.Value{}, false + } +} + +func (m inAnyOrderMatcher) String() string { + return fmt.Sprintf("has the same elements as %v", m.x) +} + +// Constructors + +// All returns a composite Matcher that returns true if and only all of the +// matchers return true. +func All(ms ...Matcher) Matcher { return allMatcher{ms} } + +// Any returns a matcher that always matches. +func Any() Matcher { return anyMatcher{} } + +// Cond returns a matcher that matches when the given function returns true +// after passing it the parameter to the mock function. +// This is particularly useful in case you want to match over a field of a custom struct, or dynamic logic. +// +// Example usage: +// +// Cond(func(x int){return x == 1}).Matches(1) // returns true +// Cond(func(x int){return x == 2}).Matches(1) // returns false +func Cond[T any](fn func(x T) bool) Matcher { return condMatcher[T]{fn} } + +// AnyOf returns a composite Matcher that returns true if at least one of the +// matchers returns true. +// +// Example usage: +// +// AnyOf(1, 2, 3).Matches(2) // returns true +// AnyOf(1, 2, 3).Matches(10) // returns false +// AnyOf(Nil(), Len(2)).Matches(nil) // returns true +// AnyOf(Nil(), Len(2)).Matches("hi") // returns true +// AnyOf(Nil(), Len(2)).Matches("hello") // returns false +func AnyOf(xs ...any) Matcher { + ms := make([]Matcher, 0, len(xs)) + for _, x := range xs { + if m, ok := x.(Matcher); ok { + ms = append(ms, m) + } else { + ms = append(ms, Eq(x)) + } + } + return anyOfMatcher{ms} +} + +// Eq returns a matcher that matches on equality. +// +// Example usage: +// +// Eq(5).Matches(5) // returns true +// Eq(5).Matches(4) // returns false +func Eq(x any) Matcher { return eqMatcher{x} } + +// Len returns a matcher that matches on length. This matcher returns false if +// is compared to a type that is not an array, chan, map, slice, or string. +func Len(i int) Matcher { + return lenMatcher{i} +} + +// Nil returns a matcher that matches if the received value is nil. +// +// Example usage: +// +// var x *bytes.Buffer +// Nil().Matches(x) // returns true +// x = &bytes.Buffer{} +// Nil().Matches(x) // returns false +func Nil() Matcher { return nilMatcher{} } + +// Not reverses the results of its given child matcher. +// +// Example usage: +// +// Not(Eq(5)).Matches(4) // returns true +// Not(Eq(5)).Matches(5) // returns false +func Not(x any) Matcher { + if m, ok := x.(Matcher); ok { + return notMatcher{m} + } + return notMatcher{Eq(x)} +} + +// Regex checks whether parameter matches the associated regex. +// +// Example usage: +// +// Regex("[0-9]{2}:[0-9]{2}").Matches("23:02") // returns true +// Regex("[0-9]{2}:[0-9]{2}").Matches([]byte{'2', '3', ':', '0', '2'}) // returns true +// Regex("[0-9]{2}:[0-9]{2}").Matches("hello world") // returns false +// Regex("[0-9]{2}").Matches(21) // returns false as it's not a valid type +func Regex(regexStr string) Matcher { + return regexMatcher{regex: regexp.MustCompile(regexStr)} +} + +// AssignableToTypeOf is a Matcher that matches if the parameter to the mock +// function is assignable to the type of the parameter to this function. +// +// Example usage: +// +// var s fmt.Stringer = &bytes.Buffer{} +// AssignableToTypeOf(s).Matches(time.Second) // returns true +// AssignableToTypeOf(s).Matches(99) // returns false +// +// var ctx = reflect.TypeOf((*context.Context)(nil)).Elem() +// AssignableToTypeOf(ctx).Matches(context.Background()) // returns true +func AssignableToTypeOf(x any) Matcher { + if xt, ok := x.(reflect.Type); ok { + return assignableToTypeOfMatcher{xt} + } + return assignableToTypeOfMatcher{reflect.TypeOf(x)} +} + +// InAnyOrder is a Matcher that returns true for collections of the same elements ignoring the order. +// +// Example usage: +// +// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 3, 2}) // returns true +// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 2}) // returns false +func InAnyOrder(x any) Matcher { + return inAnyOrderMatcher{x} +} diff --git a/vendor/go.uber.org/mock/gomock/string.go b/vendor/go.uber.org/mock/gomock/string.go new file mode 100644 index 0000000000..ec4ca7e4d3 --- /dev/null +++ b/vendor/go.uber.org/mock/gomock/string.go @@ -0,0 +1,36 @@ +package gomock + +import ( + "fmt" + "reflect" +) + +// getString is a safe way to convert a value to a string for printing results +// If the value is a a mock, getString avoids calling the mocked String() method, +// which avoids potential deadlocks +func getString(x any) string { + if isGeneratedMock(x) { + return fmt.Sprintf("%T", x) + } + if s, ok := x.(fmt.Stringer); ok { + return s.String() + } + return fmt.Sprintf("%v", x) +} + +// isGeneratedMock checks if the given type has a "isgomock" field, +// indicating it is a generated mock. +func isGeneratedMock(x any) bool { + typ := reflect.TypeOf(x) + if typ == nil { + return false + } + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + if typ.Kind() != reflect.Struct { + return false + } + _, isgomock := typ.FieldByName("isgomock") + return isgomock +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a3d8bcf8fe..6546ef2305 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -798,14 +798,10 @@ github.com/spf13/pflag # github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 ## explicit; go 1.19 github.com/stefanberger/go-pkcs11uri -# github.com/stretchr/objx v0.5.3 -## explicit; go 1.20 -github.com/stretchr/objx # github.com/stretchr/testify v1.11.1 ## explicit; go 1.17 github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml -github.com/stretchr/testify/mock github.com/stretchr/testify/require # github.com/ulikunitz/xz v0.5.15 ## explicit; go 1.12 @@ -994,6 +990,9 @@ go.podman.io/storage/pkg/reexec go.podman.io/storage/pkg/regexp go.podman.io/storage/pkg/system go.podman.io/storage/pkg/unshare +# go.uber.org/mock v0.6.0 +## explicit; go 1.23.0 +go.uber.org/mock/gomock # go.yaml.in/yaml/v2 v2.4.4 ## explicit; go 1.15 go.yaml.in/yaml/v2