From 8fc48779115c6a0e7a6fc4f5f4e96823eacc8692 Mon Sep 17 00:00:00 2001 From: Eder Ignatowicz Date: Fri, 18 Oct 2024 17:42:54 -0400 Subject: [PATCH] feat(bff): using ginkgo and gomega as our test suite Signed-off-by: Eder Ignatowicz --- clients/ui/bff/go.mod | 8 + clients/ui/bff/internal/api/app.go | 3 +- .../api/model_registry_handler_test.go | 85 ++++---- .../api/model_versions_handler_test.go | 158 ++++++++------- .../api/registered_models_handler_test.go | 181 +++++++++--------- clients/ui/bff/internal/api/suite_test.go | 58 ++++++ clients/ui/bff/internal/api/test_utils.go | 12 +- clients/ui/bff/internal/mocks/k8s_mock.go | 4 +- .../ui/bff/internal/mocks/k8s_mock_test.go | 119 ++++++------ clients/ui/bff/internal/mocks/suite_test.go | 54 ++++++ .../repositories/model_registry_test.go | 38 ++-- .../bff/internal/repositories/suite_test.go | 60 ++++++ 12 files changed, 484 insertions(+), 296 deletions(-) create mode 100644 clients/ui/bff/internal/api/suite_test.go create mode 100644 clients/ui/bff/internal/mocks/suite_test.go create mode 100644 clients/ui/bff/internal/repositories/suite_test.go diff --git a/clients/ui/bff/go.mod b/clients/ui/bff/go.mod index 89b1cfb5a..3497872c7 100644 --- a/clients/ui/bff/go.mod +++ b/clients/ui/bff/go.mod @@ -6,6 +6,8 @@ require ( github.com/brianvoe/gofakeit/v7 v7.0.4 github.com/julienschmidt/httprouter v1.3.0 github.com/kubeflow/model-registry v0.2.8-alpha + github.com/onsi/ginkgo/v2 v2.19.0 + github.com/onsi/gomega v1.33.1 github.com/stretchr/testify v1.9.0 k8s.io/api v0.31.1 k8s.io/apimachinery v0.31.1 @@ -22,15 +24,18 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af // indirect github.com/google/uuid v1.6.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -48,6 +53,8 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect @@ -55,6 +62,7 @@ require ( golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/clients/ui/bff/internal/api/app.go b/clients/ui/bff/internal/api/app.go index 6cf2ce6a1..93b635666 100644 --- a/clients/ui/bff/internal/api/app.go +++ b/clients/ui/bff/internal/api/app.go @@ -45,7 +45,8 @@ func NewApp(cfg config.EnvConfig, logger *slog.Logger) (*App, error) { var err error if cfg.MockK8Client { //mock all k8s calls - k8sClient, err = mocks.NewKubernetesClient(logger) + ctx, cancel := context.WithCancel(context.Background()) + k8sClient, err = mocks.NewKubernetesClient(logger, ctx, cancel) } else { k8sClient, err = integrations.NewKubernetesClient(logger) } diff --git a/clients/ui/bff/internal/api/model_registry_handler_test.go b/clients/ui/bff/internal/api/model_registry_handler_test.go index 70d7b49a5..13c460fe7 100644 --- a/clients/ui/bff/internal/api/model_registry_handler_test.go +++ b/clients/ui/bff/internal/api/model_registry_handler_test.go @@ -2,53 +2,52 @@ package api import ( "encoding/json" - "github.com/kubeflow/model-registry/ui/bff/internal/mocks" "github.com/kubeflow/model-registry/ui/bff/internal/models" "github.com/kubeflow/model-registry/ui/bff/internal/repositories" - "github.com/stretchr/testify/assert" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "io" - "log/slog" "net/http" "net/http/httptest" - "os" - "testing" ) -func TestModelRegistryHandler(t *testing.T) { - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - mockK8sClient, _ := mocks.NewKubernetesClient(logger) - mockMRClient, _ := mocks.NewModelRegistryClient(nil) - - testApp := App{ - kubernetesClient: mockK8sClient, - repositories: repositories.NewRepositories(mockMRClient), - } - - req, err := http.NewRequest(http.MethodGet, ModelRegistryListPath, nil) - assert.NoError(t, err) - - rr := httptest.NewRecorder() - - testApp.ModelRegistryHandler(rr, req, nil) - rs := rr.Result() - - defer rs.Body.Close() - body, err := io.ReadAll(rs.Body) - assert.NoError(t, err) - var actual ModelRegistryListEnvelope - err = json.Unmarshal(body, &actual) - assert.NoError(t, err) - - assert.Equal(t, http.StatusOK, rr.Code) - - var expected = ModelRegistryListEnvelope{ - Data: []models.ModelRegistryModel{ - {Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry"}, - {Name: "model-registry-bella", Description: "Model Registry Bella description", DisplayName: "Model Registry Bella"}, - {Name: "model-registry-dora", Description: "Model Registry Dora description", DisplayName: "Model Registry Dora"}, - }, - } - - assert.Equal(t, expected, actual) - -} +var _ = Describe("TestModelRegistryHandler", func() { + Context("fetching model registries", Ordered, func() { + It("should retrieve the model registries successfully", func() { + + By("creating the test app") + testApp := App{ + kubernetesClient: k8sClient, + repositories: repositories.NewRepositories(mockMRClient), + logger: logger, + } + + By("creating the http test infrastructure") + req, err := http.NewRequest(http.MethodGet, ModelRegistryListPath, nil) + Expect(err).NotTo(HaveOccurred()) + rr := httptest.NewRecorder() + + By("creating the http request for the handler") + testApp.ModelRegistryHandler(rr, req, nil) + rs := rr.Result() + defer rs.Body.Close() + body, err := io.ReadAll(rs.Body) + Expect(err).NotTo(HaveOccurred()) + + By("unmarshalling the model registries") + var actual ModelRegistryListEnvelope + err = json.Unmarshal(body, &actual) + Expect(err).NotTo(HaveOccurred()) + Expect(rr.Code).To(Equal(http.StatusOK)) + + By("should match the expected model registries") + var expected = []models.ModelRegistryModel{ + {Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry"}, + {Name: "model-registry-bella", Description: "Model Registry Bella description", DisplayName: "Model Registry Bella"}, + {Name: "model-registry-dora", Description: "Model Registry Dora description", DisplayName: "Model Registry Dora"}, + } + Expect(actual.Data).To(ConsistOf(expected)) + }) + + }) +}) diff --git a/clients/ui/bff/internal/api/model_versions_handler_test.go b/clients/ui/bff/internal/api/model_versions_handler_test.go index 877856c45..729aa7158 100644 --- a/clients/ui/bff/internal/api/model_versions_handler_test.go +++ b/clients/ui/bff/internal/api/model_versions_handler_test.go @@ -3,80 +3,90 @@ package api import ( "github.com/kubeflow/model-registry/pkg/openapi" "github.com/kubeflow/model-registry/ui/bff/internal/mocks" - "github.com/stretchr/testify/assert" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "net/http" - "testing" ) -func TestGetModelVersionHandler(t *testing.T) { - data := mocks.GetModelVersionMocks()[0] - expected := ModelVersionEnvelope{Data: &data} - - actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/model_versions/1", nil) - assert.NoError(t, err) - - assert.Equal(t, http.StatusOK, rs.StatusCode) - assert.Equal(t, expected.Data.Name, actual.Data.Name) -} - -func TestCreateModelVersionHandler(t *testing.T) { - data := mocks.GetModelVersionMocks()[0] - expected := ModelVersionEnvelope{Data: &data} - - body := ModelVersionEnvelope{Data: openapi.NewModelVersion("Model One", "1")} - - actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/model_versions", body) - assert.NoError(t, err) - - assert.Equal(t, http.StatusCreated, rs.StatusCode) - assert.Equal(t, expected.Data.Name, actual.Data.Name) - assert.Equal(t, rs.Header.Get("Location"), "/api/v1/model_registry/model-registry/model_versions/1") -} - -func TestUpdateModelVersionHandler(t *testing.T) { - data := mocks.GetModelVersionMocks()[0] - expected := ModelVersionEnvelope{Data: &data} - - reqData := openapi.ModelVersionUpdate{ - Description: openapi.PtrString("New description"), - } - body := ModelVersionUpdateEnvelope{Data: &reqData} - - actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodPatch, "/api/v1/model_registry/model-registry/model_versions/1", body) - assert.NoError(t, err) - - assert.Equal(t, http.StatusOK, rs.StatusCode) - assert.Equal(t, expected.Data.Name, actual.Data.Name) -} - -func TestGetAllModelArtifactsByModelVersionHandler(t *testing.T) { - data := mocks.GetModelArtifactListMock() - expected := ModelArtifactListEnvelope{Data: &data} - - actual, rs, err := setupApiTest[ModelArtifactListEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/model_versions/1/artifacts", nil) - assert.NoError(t, err) - - assert.Equal(t, http.StatusOK, rs.StatusCode) - assert.Equal(t, expected.Data.Size, actual.Data.Size) - assert.Equal(t, expected.Data.PageSize, actual.Data.PageSize) - assert.Equal(t, expected.Data.NextPageToken, actual.Data.NextPageToken) - assert.Equal(t, len(expected.Data.Items), len(actual.Data.Items)) -} - -func TestCreateModelArtifactByModelVersionHandler(t *testing.T) { - data := mocks.GetModelArtifactMocks()[0] - expected := ModelArtifactEnvelope{Data: &data} - - artifact := openapi.ModelArtifact{ - Name: openapi.PtrString("Artifact One"), - ArtifactType: "ARTIFACT_TYPE_ONE", - } - body := ModelArtifactEnvelope{Data: &artifact} - - actual, rs, err := setupApiTest[ModelArtifactEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/model_versions/1/artifacts", body) - assert.NoError(t, err) - - assert.Equal(t, http.StatusCreated, rs.StatusCode) - assert.Equal(t, expected.Data.GetArtifactType(), actual.Data.GetArtifactType()) - assert.Equal(t, rs.Header.Get("Location"), "/api/v1/model_registry/model-registry/model_artifacts/1") -} +var _ = Describe("TestGetModelVersionHandler", func() { + Context("testing Model Version Handler", Ordered, func() { + + It("should retrieve a model version", func() { + By("fetching a model version") + data := mocks.GetModelVersionMocks()[0] + expected := ModelVersionEnvelope{Data: &data} + actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/model_versions/1", nil, k8sClient) + Expect(err).NotTo(HaveOccurred()) + By("should match the expected model version") + Expect(rs.StatusCode).To(Equal(http.StatusOK)) + Expect(actual.Data.Name).To(Equal(expected.Data.Name)) + }) + + It("should create a model version", func() { + By("creating a model version") + data := mocks.GetModelVersionMocks()[0] + expected := ModelVersionEnvelope{Data: &data} + body := ModelVersionEnvelope{Data: openapi.NewModelVersion("Model One", "1")} + actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/model_versions", body, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should match the expected model version created") + Expect(rs.StatusCode).To(Equal(http.StatusCreated)) + Expect(actual.Data.Name).To(Equal(expected.Data.Name)) + Expect(rs.Header.Get("Location")).To(Equal("/api/v1/model_registry/model-registry/model_versions/1")) + }) + + It("should updated a model version", func() { + By("updating a model version") + data := mocks.GetModelVersionMocks()[0] + expected := ModelVersionEnvelope{Data: &data} + + reqData := openapi.ModelVersionUpdate{ + Description: openapi.PtrString("New description"), + } + body := ModelVersionUpdateEnvelope{Data: &reqData} + + actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodPatch, "/api/v1/model_registry/model-registry/model_versions/1", body, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should match the expected model version updated") + Expect(rs.StatusCode).To(Equal(http.StatusOK)) + Expect(actual.Data.Name).To(Equal(expected.Data.Name)) + }) + + It("get all model artifacts by a model version", func() { + By("getting a model artifacts by model version") + data := mocks.GetModelArtifactListMock() + expected := ModelArtifactListEnvelope{Data: &data} + actual, rs, err := setupApiTest[ModelArtifactListEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/model_versions/1/artifacts", nil, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should get all expected model version artifacts") + Expect(rs.StatusCode).To(Equal(http.StatusOK)) + Expect(actual.Data.Size).To(Equal(expected.Data.Size)) + Expect(actual.Data.PageSize).To(Equal(expected.Data.PageSize)) + Expect(actual.Data.NextPageToken).To(Equal(expected.Data.NextPageToken)) + Expect(len(actual.Data.Items)).To(Equal(len(expected.Data.Items))) + }) + + It("create Model Artifact By Model Version", func() { + By("creating a model version") + data := mocks.GetModelArtifactMocks()[0] + expected := ModelArtifactEnvelope{Data: &data} + + artifact := openapi.ModelArtifact{ + Name: openapi.PtrString("Artifact One"), + ArtifactType: "ARTIFACT_TYPE_ONE", + } + body := ModelArtifactEnvelope{Data: &artifact} + actual, rs, err := setupApiTest[ModelArtifactEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/model_versions/1/artifacts", body, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should get all expected model artifacts") + Expect(rs.StatusCode).To(Equal(http.StatusCreated)) + Expect(actual.Data.GetArtifactType()).To(Equal(expected.Data.GetArtifactType())) + Expect(rs.Header.Get("Location")).To(Equal("/api/v1/model_registry/model-registry/model_artifacts/1")) + + }) + }) +}) diff --git a/clients/ui/bff/internal/api/registered_models_handler_test.go b/clients/ui/bff/internal/api/registered_models_handler_test.go index 33b073d4d..34bdbc1a9 100644 --- a/clients/ui/bff/internal/api/registered_models_handler_test.go +++ b/clients/ui/bff/internal/api/registered_models_handler_test.go @@ -3,92 +3,101 @@ package api import ( "github.com/kubeflow/model-registry/pkg/openapi" "github.com/kubeflow/model-registry/ui/bff/internal/mocks" - "github.com/stretchr/testify/assert" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "net/http" - "testing" ) -func TestGetRegisteredModelHandler(t *testing.T) { - data := mocks.GetRegisteredModelMocks()[0] - expected := RegisteredModelEnvelope{Data: &data} - - actual, rs, err := setupApiTest[RegisteredModelEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/registered_models/1", nil) - assert.NoError(t, err) - - //TODO assert the full structure, I couldn't get unmarshalling to work for the full customProperties values - // this issue is in the test only - assert.Equal(t, http.StatusOK, rs.StatusCode) - assert.Equal(t, expected.Data.Name, actual.Data.Name) -} - -func TestGetAllRegisteredModelsHandler(t *testing.T) { - data := mocks.GetRegisteredModelListMock() - expected := RegisteredModelListEnvelope{Data: &data} - - actual, rs, err := setupApiTest[RegisteredModelListEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/registered_models", nil) - assert.NoError(t, err) - - assert.Equal(t, http.StatusOK, rs.StatusCode) - assert.Equal(t, expected.Data.Size, actual.Data.Size) - assert.Equal(t, expected.Data.PageSize, actual.Data.PageSize) - assert.Equal(t, expected.Data.NextPageToken, actual.Data.NextPageToken) - assert.Equal(t, len(expected.Data.Items), len(actual.Data.Items)) -} - -func TestCreateRegisteredModelHandler(t *testing.T) { - data := mocks.GetRegisteredModelMocks()[0] - expected := RegisteredModelEnvelope{Data: &data} - - body := RegisteredModelEnvelope{Data: openapi.NewRegisteredModel("Model One")} - - actual, rs, err := setupApiTest[RegisteredModelEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/registered_models", body) - assert.NoError(t, err) - - assert.Equal(t, http.StatusCreated, rs.StatusCode) - assert.Equal(t, expected.Data.Name, actual.Data.Name) - assert.Equal(t, rs.Header.Get("location"), "/api/v1/model_registry/model-registry/registered_models/1") -} - -func TestUpdateRegisteredModelHandler(t *testing.T) { - data := mocks.GetRegisteredModelMocks()[0] - expected := RegisteredModelEnvelope{Data: &data} - - reqData := openapi.RegisteredModelUpdate{ - Description: openapi.PtrString("This is a new description"), - } - body := RegisteredModelUpdateEnvelope{Data: &reqData} - - actual, rs, err := setupApiTest[RegisteredModelEnvelope](http.MethodPatch, "/api/v1/model_registry/model-registry/registered_models/1", body) - assert.NoError(t, err) - - assert.Equal(t, http.StatusOK, rs.StatusCode) - //TODO when mock client can handle changing state, update this to verify the changes are made. - assert.Equal(t, expected.Data.Description, actual.Data.Description) -} - -func TestGetAllModelVersionsForRegisteredModelHandler(t *testing.T) { - data := mocks.GetModelVersionListMock() - expected := ModelVersionListEnvelope{Data: &data} - - actual, rs, err := setupApiTest[ModelVersionListEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/registered_models/1/versions", nil) - assert.NoError(t, err) - - assert.Equal(t, http.StatusOK, rs.StatusCode) - assert.Equal(t, expected.Data.Size, actual.Data.Size) - assert.Equal(t, expected.Data.PageSize, actual.Data.PageSize) - assert.Equal(t, expected.Data.NextPageToken, actual.Data.NextPageToken) - assert.Equal(t, len(expected.Data.Items), len(actual.Data.Items)) -} - -func TestCreateModelVersionForRegisteredModelHandler(t *testing.T) { - data := mocks.GetModelVersionMocks()[0] - expected := ModelVersionEnvelope{Data: &data} - - body := ModelVersionEnvelope{Data: openapi.NewModelVersion("Version Fifty", "")} - actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/registered_models/1/versions", body) - assert.NoError(t, err) - - assert.Equal(t, http.StatusCreated, rs.StatusCode) - assert.Equal(t, expected.Data.Name, actual.Data.Name) - assert.Equal(t, rs.Header.Get("Location"), "/api/v1/model_registry/model-registry/model_versions/1") -} +var _ = Describe("TestGetRegisteredModelHandler", func() { + Context("testing registered models by id", Ordered, func() { + + It("should retrieve a registered model", func() { + By("fetching all model registries") + data := mocks.GetRegisteredModelMocks()[0] + expected := RegisteredModelEnvelope{Data: &data} + actual, rs, err := setupApiTest[RegisteredModelEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/registered_models/1", nil, k8sClient) + Expect(err).NotTo(HaveOccurred()) + By("should match the expected model registry") + //TODO assert the full structure, I couldn't get unmarshalling to work for the full customProperties values + // this issue is in the test only + Expect(rs.StatusCode).To(Equal(http.StatusOK)) + Expect(actual.Data.Name).To(Equal(expected.Data.Name)) + }) + + It("should retrieve all registered models", func() { + By("fetching all registered models") + data := mocks.GetRegisteredModelListMock() + expected := RegisteredModelListEnvelope{Data: &data} + actual, rs, err := setupApiTest[RegisteredModelListEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/registered_models", nil, k8sClient) + Expect(err).NotTo(HaveOccurred()) + By("should match the expected model registry") + Expect(rs.StatusCode).To(Equal(http.StatusOK)) + Expect(actual.Data.Size).To(Equal(expected.Data.Size)) + Expect(actual.Data.PageSize).To(Equal(expected.Data.PageSize)) + Expect(actual.Data.NextPageToken).To(Equal(expected.Data.NextPageToken)) + Expect(len(actual.Data.Items)).To(Equal(len(expected.Data.Items))) + }) + + It("creating registered models", func() { + By("post to registered models") + data := mocks.GetRegisteredModelMocks()[0] + expected := RegisteredModelEnvelope{Data: &data} + body := RegisteredModelEnvelope{Data: openapi.NewRegisteredModel("Model One")} + actual, rs, err := setupApiTest[RegisteredModelEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/registered_models", body, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should do a successful post") + Expect(rs.StatusCode).To(Equal(http.StatusCreated)) + Expect(actual.Data.Name).To(Equal(expected.Data.Name)) + Expect(rs.Header.Get("location")).To(Equal("/api/v1/model_registry/model-registry/registered_models/1")) + }) + + It("updating registered models", func() { + By("path to registered models") + data := mocks.GetRegisteredModelMocks()[0] + expected := RegisteredModelEnvelope{Data: &data} + reqData := openapi.RegisteredModelUpdate{ + Description: openapi.PtrString("This is a new description"), + } + body := RegisteredModelUpdateEnvelope{Data: &reqData} + actual, rs, err := setupApiTest[RegisteredModelEnvelope](http.MethodPatch, "/api/v1/model_registry/model-registry/registered_models/1", body, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should do a successful patch") + Expect(rs.StatusCode).To(Equal(http.StatusOK)) + Expect(actual.Data.Description).To(Equal(expected.Data.Description)) + }) + + It("get all model versions for registered model", func() { + By("get to registered models versions") + data := mocks.GetModelVersionListMock() + expected := ModelVersionListEnvelope{Data: &data} + + actual, rs, err := setupApiTest[ModelVersionListEnvelope](http.MethodGet, "/api/v1/model_registry/model-registry/registered_models/1/versions", nil, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should get all items") + Expect(rs.StatusCode).To(Equal(http.StatusOK)) + Expect(actual.Data.Size).To(Equal(expected.Data.Size)) + Expect(actual.Data.PageSize).To(Equal(expected.Data.PageSize)) + Expect(actual.Data.NextPageToken).To(Equal(expected.Data.NextPageToken)) + Expect(len(actual.Data.Items)).To(Equal(len(expected.Data.Items))) + }) + + It("create model version for registered model", func() { + By("doing a post to registered model versions") + data := mocks.GetModelVersionMocks()[0] + expected := ModelVersionEnvelope{Data: &data} + + body := ModelVersionEnvelope{Data: openapi.NewModelVersion("Version Fifty", "")} + actual, rs, err := setupApiTest[ModelVersionEnvelope](http.MethodPost, "/api/v1/model_registry/model-registry/registered_models/1/versions", body, k8sClient) + Expect(err).NotTo(HaveOccurred()) + + By("should successfully create it") + Expect(rs.StatusCode).To(Equal(http.StatusCreated)) + Expect(actual.Data.Name).To(Equal(expected.Data.Name)) + Expect(rs.Header.Get("Location")).To(Equal("/api/v1/model_registry/model-registry/model_versions/1")) + + }) + }) +}) diff --git a/clients/ui/bff/internal/api/suite_test.go b/clients/ui/bff/internal/api/suite_test.go new file mode 100644 index 000000000..e7d86f2c7 --- /dev/null +++ b/clients/ui/bff/internal/api/suite_test.go @@ -0,0 +1,58 @@ +package api + +import ( + "context" + k8s "github.com/kubeflow/model-registry/ui/bff/internal/integrations" + "github.com/kubeflow/model-registry/ui/bff/internal/mocks" + "log/slog" + "os" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "testing" +) + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + k8sClient k8s.KubernetesClientInterface + mockMRClient *mocks.ModelRegistryClientMock + ctx context.Context + cancel context.CancelFunc + logger *slog.Logger + err error +) + +func TestAPI(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "API Suite") + + k8sClient.Shutdown(ctx, logger) +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + ctx, cancel = context.WithCancel(context.Background()) + + By("bootstrapping test environment") + logger = slog.New(slog.NewTextHandler(os.Stdout, nil)) + + k8sClient, err = mocks.NewKubernetesClient(logger, ctx, cancel) + Expect(err).NotTo(HaveOccurred()) + + mockMRClient, err = mocks.NewModelRegistryClient(nil) + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + defer cancel() + err := k8sClient.Shutdown(ctx, logger) + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/clients/ui/bff/internal/api/test_utils.go b/clients/ui/bff/internal/api/test_utils.go index c4b6fc3f8..94bc598f3 100644 --- a/clients/ui/bff/internal/api/test_utils.go +++ b/clients/ui/bff/internal/api/test_utils.go @@ -4,31 +4,25 @@ import ( "bytes" "context" "encoding/json" + k8s "github.com/kubeflow/model-registry/ui/bff/internal/integrations" "github.com/kubeflow/model-registry/ui/bff/internal/mocks" "github.com/kubeflow/model-registry/ui/bff/internal/repositories" "io" - "log/slog" "net/http" "net/http/httptest" - "os" ) -func setupApiTest[T any](method string, url string, body interface{}) (T, *http.Response, error) { +func setupApiTest[T any](method string, url string, body interface{}, k8sClient k8s.KubernetesClientInterface) (T, *http.Response, error) { mockMRClient, err := mocks.NewModelRegistryClient(nil) if err != nil { return *new(T), nil, err } - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - mockK8sClient, err := mocks.NewKubernetesClient(logger) - if err != nil { - return *new(T), nil, err - } mockClient := new(mocks.MockHTTPClient) testApp := App{ repositories: repositories.NewRepositories(mockMRClient), - kubernetesClient: mockK8sClient, + kubernetesClient: k8sClient, } var req *http.Request diff --git a/clients/ui/bff/internal/mocks/k8s_mock.go b/clients/ui/bff/internal/mocks/k8s_mock.go index 3e77b73d7..e2427edef 100644 --- a/clients/ui/bff/internal/mocks/k8s_mock.go +++ b/clients/ui/bff/internal/mocks/k8s_mock.go @@ -32,8 +32,8 @@ func (m *KubernetesClientMock) Shutdown(ctx context.Context, logger *slog.Logger return nil } -func NewKubernetesClient(logger *slog.Logger) (k8s.KubernetesClientInterface, error) { - ctx, cancel := context.WithCancel(context.Background()) +func NewKubernetesClient(logger *slog.Logger, ctx context.Context, cancel context.CancelFunc) (k8s.KubernetesClientInterface, error) { + projectRoot, err := getProjectRoot() if err != nil { logger.Error("failed to find project root to locate binaries", err) diff --git a/clients/ui/bff/internal/mocks/k8s_mock_test.go b/clients/ui/bff/internal/mocks/k8s_mock_test.go index f1787484a..9ef9f502c 100644 --- a/clients/ui/bff/internal/mocks/k8s_mock_test.go +++ b/clients/ui/bff/internal/mocks/k8s_mock_test.go @@ -1,67 +1,62 @@ package mocks import ( - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "log/slog" - "os" - "testing" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) -func TestGetServiceDetails(t *testing.T) { - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - - k8sClient, err := NewKubernetesClient(logger) - require.NoError(t, err, "Failed to initialize KubernetesClientMock") - - services, err := k8sClient.GetServiceDetails() - require.NoError(t, err, "Failed to get service details") - - // Check that all services have the modified ClusterIP and HTTPPort - for _, service := range services { - assert.Equal(t, "127.0.0.1", service.ClusterIP, "ClusterIP should be set to 127.0.0.1") - assert.Equal(t, int32(8080), service.HTTPPort, "HTTPPort should be set to 8080") - } - - //Check that a specific service exists - foundService := false - for _, service := range services { - if service.Name == "model-registry" { - foundService = true - assert.Equal(t, "Model Registry", service.DisplayName) - assert.Equal(t, "Model Registry Description", service.Description) - break - } - } - assert.True(t, foundService, "Expected to find service 'model-registry'") -} - -func TestGetServiceDetailsByName(t *testing.T) { - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - - k8sClient, err := NewKubernetesClient(logger) - require.NoError(t, err, "Failed to initialize KubernetesClientMock") - - service, err := k8sClient.GetServiceDetailsByName("model-registry-dora") - require.NoError(t, err, "Failed to get service details") - - assert.Equal(t, "model-registry-dora", service.Name) - assert.Equal(t, "Model Registry Dora description", service.Description) - assert.Equal(t, "Model Registry Dora", service.DisplayName) - -} - -func TestGetService(t *testing.T) { - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - - k8sClient, err := NewKubernetesClient(logger) - require.NoError(t, err, "Failed to initialize KubernetesClientMock") - - services, err := k8sClient.GetServiceNames() - require.NoError(t, err, "Failed to get service details") - - assert.Equal(t, "model-registry", services[0]) - assert.Equal(t, "model-registry-bella", services[1]) - assert.Equal(t, "model-registry-dora", services[2]) - -} +var _ = Describe("Kubernetes Client Test", func() { + Context("with existing services", Ordered, func() { + + It("should retrieve the get all service successfully", func() { + + By("getting service details") + services, err := k8sClient.GetServiceDetails() + Expect(err).NotTo(HaveOccurred(), "Failed to create HTTP request") + + By("checking that all services have the modified ClusterIP and HTTPPort") + for _, service := range services { + Expect(service.ClusterIP).To(Equal("127.0.0.1"), "ClusterIP should be set to 127.0.0.1") + Expect(service.HTTPPort).To(Equal(int32(8080)), "HTTPPort should be set to 8080") + + } + + By("checking that that a specific service exists") + foundService := false + for _, service := range services { + if service.Name == "model-registry" { + foundService = true + Expect(service.DisplayName).To(Equal("Model Registry")) + Expect(service.Description).To(Equal("Model Registry Description")) + break + } + } + Expect(foundService).To(Equal(true), "Expected to find service 'model-registry'") + }) + + It("should retrieve the service details by name", func() { + + By("getting service by name") + service, err := k8sClient.GetServiceDetailsByName("model-registry-dora") + Expect(err).NotTo(HaveOccurred(), "Failed to create k8s request") + + By("checking that service details are correct") + Expect(service.Name).To(Equal("model-registry-dora")) + Expect(service.Description).To(Equal("Model Registry Dora description")) + Expect(service.DisplayName).To(Equal("Model Registry Dora")) + }) + + It("should retrieve the services names", func() { + + By("getting service by name") + services, err := k8sClient.GetServiceNames() + Expect(err).NotTo(HaveOccurred(), "Failed to create HTTP request") + + By("checking that service details are correct") + Expect(services[0]).To(Equal("model-registry")) + Expect(services[1]).To(Equal("model-registry-bella")) + Expect(services[2]).To(Equal("model-registry-dora")) + }) + }) + +}) diff --git a/clients/ui/bff/internal/mocks/suite_test.go b/clients/ui/bff/internal/mocks/suite_test.go new file mode 100644 index 000000000..b931404d8 --- /dev/null +++ b/clients/ui/bff/internal/mocks/suite_test.go @@ -0,0 +1,54 @@ +package mocks + +import ( + "context" + k8s "github.com/kubeflow/model-registry/ui/bff/internal/integrations" + "log/slog" + "os" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "testing" +) + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + k8sClient k8s.KubernetesClientInterface + ctx context.Context + cancel context.CancelFunc + logger *slog.Logger + err error +) + +func TestAPI(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "API Suite") + + k8sClient.Shutdown(ctx, logger) +} + +var _ = BeforeSuite(func() { + defer GinkgoRecover() + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + ctx, cancel = context.WithCancel(context.Background()) + + By("bootstrapping test environment") + logger = slog.New(slog.NewTextHandler(os.Stdout, nil)) + + k8sClient, err = NewKubernetesClient(logger, ctx, cancel) + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + defer cancel() + err := k8sClient.Shutdown(ctx, logger) + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/clients/ui/bff/internal/repositories/model_registry_test.go b/clients/ui/bff/internal/repositories/model_registry_test.go index 9258173c1..e430011cd 100644 --- a/clients/ui/bff/internal/repositories/model_registry_test.go +++ b/clients/ui/bff/internal/repositories/model_registry_test.go @@ -1,28 +1,28 @@ package repositories import ( - "github.com/kubeflow/model-registry/ui/bff/internal/mocks" "github.com/kubeflow/model-registry/ui/bff/internal/models" - "github.com/stretchr/testify/assert" - "log/slog" - "os" - "testing" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) -func TestFetchAllModelRegistry(t *testing.T) { - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - mockK8sClient, _ := mocks.NewKubernetesClient(logger) +var _ = Describe("TestFetchAllModelRegistry", func() { + Context("with existing model registries", Ordered, func() { - mrClient := NewModelRegistryRepository() + It("should retrieve the get all service successfully", func() { - registries, err := mrClient.FetchAllModelRegistries(mockK8sClient) + By("fetching all model registries in the repository") + modelRegistryRepository := NewModelRegistryRepository() + registries, err := modelRegistryRepository.FetchAllModelRegistries(k8sClient) + Expect(err).NotTo(HaveOccurred()) - assert.NoError(t, err) - - expectedRegistries := []models.ModelRegistryModel{ - {Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry"}, - {Name: "model-registry-bella", Description: "Model Registry Bella description", DisplayName: "Model Registry Bella"}, - {Name: "model-registry-dora", Description: "Model Registry Dora description", DisplayName: "Model Registry Dora"}, - } - assert.Equal(t, expectedRegistries, registries) -} + By("should match the expected model registries") + expectedRegistries := []models.ModelRegistryModel{ + {Name: "model-registry", Description: "Model Registry Description", DisplayName: "Model Registry"}, + {Name: "model-registry-bella", Description: "Model Registry Bella description", DisplayName: "Model Registry Bella"}, + {Name: "model-registry-dora", Description: "Model Registry Dora description", DisplayName: "Model Registry Dora"}, + } + Expect(registries).To(ConsistOf(expectedRegistries)) + }) + }) +}) diff --git a/clients/ui/bff/internal/repositories/suite_test.go b/clients/ui/bff/internal/repositories/suite_test.go new file mode 100644 index 000000000..ade6e336b --- /dev/null +++ b/clients/ui/bff/internal/repositories/suite_test.go @@ -0,0 +1,60 @@ +package repositories + +import ( + "context" + k8s "github.com/kubeflow/model-registry/ui/bff/internal/integrations" + "github.com/kubeflow/model-registry/ui/bff/internal/mocks" + "log/slog" + "os" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "testing" +) + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + k8sClient k8s.KubernetesClientInterface + mockMRClient *mocks.ModelRegistryClientMock + ctx context.Context + cancel context.CancelFunc + logger *slog.Logger + err error +) + +func TestAPI(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "API Suite") + + k8sClient.Shutdown(ctx, logger) +} + +var _ = BeforeSuite(func() { + defer GinkgoRecover() // Ensure Ginkgo can handle any panic during setup + + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + ctx, cancel = context.WithCancel(context.Background()) + + By("bootstrapping test environment") + logger = slog.New(slog.NewTextHandler(os.Stdout, nil)) + + k8sClient, err = mocks.NewKubernetesClient(logger, ctx, cancel) + Expect(err).NotTo(HaveOccurred()) + + mockMRClient, err = mocks.NewModelRegistryClient(nil) + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := k8sClient.Shutdown(ctx, logger) + defer cancel() + Expect(err).NotTo(HaveOccurred()) +})