Skip to content

Commit

Permalink
Aim namespace tests for apps and dashboards (#610)
Browse files Browse the repository at this point in the history
* integration tests for Apps and Dashboards in namespace contexts
* filtering by namespace in "/apps" endpoints
  • Loading branch information
suprjinx authored Nov 20, 2023
1 parent 746c559 commit 7ccd506
Show file tree
Hide file tree
Showing 10 changed files with 602 additions and 5 deletions.
3 changes: 3 additions & 0 deletions pkg/api/aim/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func GetApp(c *fiber.Ctx) error {
}
if err := database.DB.
Where("NOT is_archived").
Where("namespace_id = ?", ns.ID).
First(&app).
Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
Expand Down Expand Up @@ -131,6 +132,7 @@ func UpdateApp(c *fiber.Ctx) error {
}
if err := database.DB.
Where("NOT is_archived").
Where("namespace_id = ?", ns.ID).
First(&app).
Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
Expand Down Expand Up @@ -176,6 +178,7 @@ func DeleteApp(c *fiber.Ctx) error {
if err := database.DB.
Select("ID").
Where("NOT is_archived").
Where("namespace_id = ?", ns.ID).
First(&app).
Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
Expand Down
270 changes: 270 additions & 0 deletions tests/integration/golang/aim/namespace/flows/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
//go:build integration

package flows

import (
"context"
"fmt"
"net/http"
"testing"

"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

"github.com/G-Research/fasttrackml/pkg/api/aim/request"
"github.com/G-Research/fasttrackml/pkg/api/aim/response"
"github.com/G-Research/fasttrackml/pkg/api/mlflow/common"
"github.com/G-Research/fasttrackml/pkg/api/mlflow/dao/models"
"github.com/G-Research/fasttrackml/tests/integration/golang/helpers"
)

type AppFlowTestSuite struct {
helpers.BaseTestSuite
}

func TestAppFlowTestSuite(t *testing.T) {
suite.Run(t, &AppFlowTestSuite{})
}

func (s *AppFlowTestSuite) TearDownTest() {
require.Nil(s.T(), s.NamespaceFixtures.UnloadFixtures())
}

func (s *AppFlowTestSuite) Test_Ok() {
tests := []struct {
name string
namespace1Code string
namespace2Code string
}{
{
name: "TestCustomNamespaces",
namespace1Code: "namespace-1",
namespace2Code: "namespace-2",
},
{
name: "TestExplicitDefaultAndCustomNamespaces",
namespace1Code: "default",
namespace2Code: "namespace-1",
},
{
name: "TestImplicitDefaultAndCustomNamespaces",
namespace1Code: "",
namespace2Code: "namespace-1",
},
}

for _, tt := range tests {
s.T().Run(tt.name, func(T *testing.T) {
defer func() {
require.Nil(s.T(), s.NamespaceFixtures.UnloadFixtures())
}()

// setup namespaces
for _, nsCode := range []string{"default", tt.namespace1Code, tt.namespace2Code} {
_, err := s.NamespaceFixtures.UpsertNamespace(context.Background(), &models.Namespace{
Code: nsCode,
DefaultExperimentID: common.GetPointer(int32(0)),
})
require.Nil(s.T(), err)
}

// run actual flow test over the test data.
s.testAppFlow(tt.namespace1Code, tt.namespace2Code)
})
}
}

func (s *AppFlowTestSuite) testAppFlow(
namespace1Code, namespace2Code string,
) {
// create Apps
app1ID := s.createAppAndCompare(namespace1Code, &request.CreateApp{
Type: "tf",
State: request.AppState{
"app-state-key": "app-state-value1",
},
})

app2ID := s.createAppAndCompare(namespace2Code, &request.CreateApp{
Type: "mpi",
State: request.AppState{
"app-state-key": "app-state-value2",
},
})

// test `GET /apps` endpoint with namespace 1
resp := s.getApps(namespace1Code)
// only app 1 should be present
assert.Equal(s.T(), 1, len(resp))
assert.Equal(s.T(), app1ID, resp[0].ID)

// test `GET /apps` endpoint with namespace 2
resp = s.getApps(namespace2Code)
// only app 2 should be present
assert.Equal(s.T(), 1, len(resp))
assert.Equal(s.T(), app2ID, resp[0].ID)

// IDs from active namespace can be fetched, updated, and deleted
s.getAppAndCompare(namespace1Code, app1ID)
s.updateAppAndCompare(namespace1Code, app1ID)
s.deleteAppAndCompare(namespace2Code, app2ID)

// IDs from other namespace cannot be fetched, updated, or deleted
errResp := response.Error{}
client := s.AIMClient()
require.Nil(
s.T(),
client.WithMethod(
http.MethodGet,
).WithNamespace(
namespace1Code,
).WithResponse(
&errResp,
).DoRequest(
fmt.Sprintf("/apps/%s", app2ID),
),
)
assert.Equal(s.T(), fiber.ErrNotFound.Code, client.GetStatusCode())

client = s.AIMClient()
require.Nil(
s.T(),
client.WithMethod(
http.MethodPut,
).WithNamespace(
namespace2Code,
).WithRequest(
request.UpdateApp{
Type: "app-type",
State: request.AppState{
"app-state-key": "new-app-state-value",
},
},
).WithResponse(
&errResp,
).DoRequest(
fmt.Sprintf("/apps/%s", app1ID),
),
)
assert.Equal(s.T(), fiber.ErrNotFound.Code, client.GetStatusCode())

client = s.AIMClient()
require.Nil(
s.T(),
client.WithMethod(
http.MethodDelete,
).WithNamespace(
namespace2Code,
).WithResponse(
&errResp,
).DoRequest(
fmt.Sprintf("/apps/%s", app1ID),
),
)
assert.Equal(s.T(), fiber.ErrNotFound.Code, client.GetStatusCode())
}

func (s *AppFlowTestSuite) deleteAppAndCompare(namespaceCode string, appID string) {
client := s.AIMClient()
appResp := response.App{}
require.Nil(
s.T(),
client.WithMethod(
http.MethodDelete,
).WithNamespace(
namespaceCode,
).WithResponse(
&appResp,
).DoRequest(
fmt.Sprintf("/apps/%s", appID),
),
)
assert.Equal(s.T(), fiber.StatusOK, client.GetStatusCode())
}

func (s *AppFlowTestSuite) updateAppAndCompare(namespaceCode string, appID string) {
client := s.AIMClient()
appResp := response.App{}
require.Nil(
s.T(),
client.WithMethod(
http.MethodPut,
).WithNamespace(
namespaceCode,
).WithRequest(
request.UpdateApp{
Type: "app-type",
State: request.AppState{
"app-state-key": "new-app-state-value",
},
},
).WithResponse(
&appResp,
).DoRequest(
fmt.Sprintf("/apps/%s", appID),
),
)
assert.Equal(s.T(), appID, appResp.ID)
assert.Equal(s.T(), fiber.StatusOK, client.GetStatusCode())
}

func (s *AppFlowTestSuite) getAppAndCompare(namespaceCode string, appID string) response.App {
appResp := response.App{}
client := s.AIMClient()
require.Nil(
s.T(),
client.WithMethod(
http.MethodGet,
).WithNamespace(
namespaceCode,
).WithResponse(
&appResp,
).DoRequest(
fmt.Sprintf("/apps/%s", appID),
),
)
assert.Equal(s.T(), appID, appResp.ID)
assert.Equal(s.T(), fiber.StatusOK, client.GetStatusCode())
return appResp
}

func (s *AppFlowTestSuite) getApps(namespaceCode string) []response.App {
resp := []response.App{}
require.Nil(
s.T(),
s.AIMClient().WithMethod(
http.MethodGet,
).WithNamespace(
namespaceCode,
).WithResponse(
&resp,
).DoRequest(
"/apps",
),
)
return resp
}

func (s *AppFlowTestSuite) createAppAndCompare(namespace string, req *request.CreateApp) string {
var resp response.App
require.Nil(
s.T(),
s.AIMClient().WithMethod(
http.MethodPost,
).WithNamespace(
namespace,
).WithRequest(
req,
).WithResponse(
&resp,
).DoRequest(
"/apps",
),
)
assert.Equal(s.T(), req.Type, resp.Type)
assert.Equal(s.T(), req.State["app-state-key"], resp.State["app-state-key"])
assert.NotEmpty(s.T(), resp.ID)
return resp.ID
}
Loading

0 comments on commit 7ccd506

Please sign in to comment.