diff --git a/runner/mocks/poolManagerController.go b/runner/mocks/poolManagerController.go index 2c25948b..6229ff6a 100644 --- a/runner/mocks/poolManagerController.go +++ b/runner/mocks/poolManagerController.go @@ -65,13 +65,31 @@ func (_m *PoolManagerController) CreateRepoPoolManager(ctx context.Context, repo } // DeleteOrgPoolManager provides a mock function with given fields: org -func (_m *PoolManagerController) DeleteOrgPoolManager(org params.Organization) { - _m.Called(org) +func (_m *PoolManagerController) DeleteOrgPoolManager(org params.Organization) error { + ret := _m.Called(org) + + var r0 error + if rf, ok := ret.Get(0).(func(params.Organization) error); ok { + r0 = rf(org) + } else { + r0 = ret.Error(0) + } + + return r0 } // DeleteRepoPoolManager provides a mock function with given fields: repo -func (_m *PoolManagerController) DeleteRepoPoolManager(repo params.Repository) { - _m.Called(repo) +func (_m *PoolManagerController) DeleteRepoPoolManager(repo params.Repository) error { + ret := _m.Called(repo) + + var r0 error + if rf, ok := ret.Get(0).(func(params.Repository) error); ok { + r0 = rf(repo) + } else { + r0 = ret.Error(0) + } + + return r0 } // GetOrgPoolManager provides a mock function with given fields: org @@ -98,7 +116,7 @@ func (_m *PoolManagerController) GetOrgPoolManager(org params.Organization) (com } // GetOrgPoolManagers provides a mock function with given fields: -func (_m *PoolManagerController) GetOrgPoolManagers() map[string]common.PoolManager { +func (_m *PoolManagerController) GetOrgPoolManagers() (map[string]common.PoolManager, error) { ret := _m.Called() var r0 map[string]common.PoolManager @@ -110,7 +128,14 @@ func (_m *PoolManagerController) GetOrgPoolManagers() map[string]common.PoolMana } } - return r0 + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // GetRepoPoolManager provides a mock function with given fields: repo @@ -137,7 +162,7 @@ func (_m *PoolManagerController) GetRepoPoolManager(repo params.Repository) (com } // GetRepoPoolManagers provides a mock function with given fields: -func (_m *PoolManagerController) GetRepoPoolManagers() map[string]common.PoolManager { +func (_m *PoolManagerController) GetRepoPoolManagers() (map[string]common.PoolManager, error) { ret := _m.Called() var r0 map[string]common.PoolManager @@ -149,7 +174,14 @@ func (_m *PoolManagerController) GetRepoPoolManagers() map[string]common.PoolMan } } - return r0 + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 } type mockConstructorTestingTNewPoolManagerController interface { diff --git a/runner/organizations_test.go b/runner/organizations_test.go index be5ff7e9..e075bb55 100644 --- a/runner/organizations_test.go +++ b/runner/organizations_test.go @@ -19,173 +19,597 @@ import ( "fmt" "garm/auth" "garm/config" - dbMocks "garm/database/common/mocks" + "garm/database" + dbCommon "garm/database/common" runnerErrors "garm/errors" "garm/params" + "garm/runner/common" + runnerCommonMocks "garm/runner/common/mocks" + runnerMocks "garm/runner/mocks" + "os" + "path/filepath" "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" ) -func TestCreateOrganizationErrUnauthorized(t *testing.T) { - ctx := context.Background() - createOrgParams := params.CreateOrgParams{} - runner := Runner{} +var ( + EncryptionPassphrase = "bocyasicgatEtenOubwonIbsudNutDom" +) - org, err := runner.CreateOrganization(ctx, createOrgParams) - var expectedOrg params.Organization - require.Equal(t, expectedOrg, org) - require.Equal(t, runnerErrors.ErrUnauthorized, err) +type OrgTestFixtures struct { + AdminContext context.Context + DBFile string + Store dbCommon.Store + StoreOrgs map[string]params.Organization + Providers map[string]common.Provider + Credentials map[string]config.Github + CreateOrgParams params.CreateOrgParams + CreatePoolParams params.CreatePoolParams + CreateInstanceParams params.CreateInstanceParams + UpdateRepoParams params.UpdateRepositoryParams + UpdatePoolParams params.UpdatePoolParams + UpdatePoolStateParams params.UpdatePoolStateParams + ErrMock error + ProviderMock *runnerCommonMocks.Provider + PoolMgrMock *runnerCommonMocks.PoolManager + PoolMgrCtrlMock *runnerMocks.PoolManagerController } -func TestCreateOrganizationInvalidParams(t *testing.T) { - adminCtx := auth.GetAdminContext() - createOrgParams := params.CreateOrgParams{} - runner := Runner{} - - org, err := runner.CreateOrganization(adminCtx, createOrgParams) - require.NotNil(t, err) - require.Equal(t, params.Organization{}, org) - require.Regexp(t, "validating params: missing repo name", err.Error()) +type OrgTestSuite struct { + suite.Suite + Fixtures *OrgTestFixtures + Runner *Runner } -func TestCreateOrganizationMissingCredentials(t *testing.T) { - adminCtx := auth.GetAdminContext() - createOrgParams := params.CreateOrgParams{ - Name: "test", - CredentialsName: "test", - WebhookSecret: "test", +func getTestSqliteDBConfig(t *testing.T) config.Database { + dir, err := os.MkdirTemp("", "garm-config-test") + if err != nil { + t.Fatalf("failed to create temporary directory: %s", err) } - runner := Runner{} + t.Cleanup(func() { os.RemoveAll(dir) }) - org, err := runner.CreateOrganization(adminCtx, createOrgParams) - require.Equal(t, params.Organization{}, org) - require.Equal(t, runnerErrors.NewBadRequestError("credentials %s not defined", createOrgParams.CredentialsName), err) + return config.Database{ + Debug: false, + DbBackend: config.SQLiteBackend, + Passphrase: EncryptionPassphrase, + SQLite: config.SQLite{ + DBFile: filepath.Join(dir, "garm.db"), + }, + } } -func TestCreateOrganizationOrgFetchFailed(t *testing.T) { - adminCtx := auth.GetAdminContext() - createOrgParams := params.CreateOrgParams{ - Name: "test", - CredentialsName: "test", - WebhookSecret: "test", +func (s *OrgTestSuite) EqualOrgsByName(expected map[string]params.Organization, actual []params.Organization) { + s.Require().Equal(len(expected), len(actual)) + for _, i := range expected { + found := false + for _, j := range actual { + if i.Name == j.Name { + found = true + break + } + } + if !found { + s.FailNow(fmt.Sprintf("expected org (%s) cannot be found in the actual orgs", i.Name)) + } } - storeMock := dbMocks.NewStore(t) - errMock := fmt.Errorf("mock error") - storeMock.On("GetOrganization", adminCtx, createOrgParams.Name).Return(params.Organization{}, errMock) - runner := Runner{ - credentials: map[string]config.Github{ - "test": { - Name: "test", - Description: "test", - OAuth2Token: "test-token", +} + +func (s *OrgTestSuite) EqualPoolsByID(expected, actual []params.Pool) { + s.Require().Equal(len(expected), len(actual)) + for _, i := range expected { + found := false + for _, j := range actual { + if i.ID == j.ID { + found = true + break + } + } + if !found { + s.FailNow(fmt.Sprintf("expected pool (%s) cannot be found in the actual pools", i.ID)) + } + } +} + +func (s *OrgTestSuite) EqualInstancesByName(expected, actual []params.Instance) { + s.Require().Equal(len(expected), len(actual)) + for _, i := range expected { + found := false + for _, j := range actual { + if i.Name == j.Name { + found = true + break + } + } + if !found { + s.FailNow(fmt.Sprintf("expected instance (%s) cannot be found in the actual instances", i.Name)) + } + } +} + +func (s *OrgTestSuite) SetupTest() { + adminCtx := auth.GetAdminContext() + + // create testing sqlite database + dbCfg := getTestSqliteDBConfig(s.T()) + db, err := database.NewDatabase(adminCtx, dbCfg) + if err != nil { + s.FailNow(fmt.Sprintf("failed to create db connection: %s", err)) + } + + // create some organization objects in the database, for testing purposes + orgs := map[string]params.Organization{} + for i := 1; i <= 3; i++ { + name := fmt.Sprintf("test-org-%v", i) + org, err := db.CreateOrganization( + adminCtx, + name, + fmt.Sprintf("test-creds-%v", i), + fmt.Sprintf("test-webhook-secret-%v", i), + ) + if err != nil { + s.FailNow(fmt.Sprintf("failed to create database object (test-org-%v)", i)) + } + orgs[name] = org + } + + // setup test fixtures + var maxRunners uint = 40 + var minIdleRunners uint = 20 + providerMock := runnerCommonMocks.NewProvider(s.T()) + fixtures := &OrgTestFixtures{ + AdminContext: adminCtx, + DBFile: dbCfg.SQLite.DBFile, + Store: db, + StoreOrgs: orgs, + Providers: map[string]common.Provider{ + "test-provider": providerMock, + }, + Credentials: map[string]config.Github{ + "test-creds": { + Name: "test-creds-name", + Description: "test-creds-description", + OAuth2Token: "test-creds-oauth2-token", }, }, - store: storeMock, - } - - org, err := runner.CreateOrganization(adminCtx, createOrgParams) - storeMock.AssertExpectations(t) - require.Equal(t, params.Organization{}, org) - require.Equal(t, "fetching repo: mock error", err.Error()) -} - -func TestCreateOrganizationAlreadyExists(t *testing.T) { - adminCtx := auth.GetAdminContext() - createOrgParams := params.CreateOrgParams{ - Name: "test", - CredentialsName: "test", - WebhookSecret: "test", - } - storeMock := dbMocks.NewStore(t) - storeMock.On("GetOrganization", adminCtx, createOrgParams.Name).Return(params.Organization{}, nil) - runner := Runner{ - credentials: map[string]config.Github{ - "test": { - Name: "test", - Description: "test", - OAuth2Token: "test-token", - }, + CreateOrgParams: params.CreateOrgParams{ + Name: "test-org-create", + CredentialsName: "test-creds", }, - store: storeMock, - } - - org, err := runner.CreateOrganization(adminCtx, createOrgParams) - storeMock.AssertExpectations(t) - require.Equal(t, params.Organization{}, org) - require.Equal(t, runnerErrors.NewConflictError("organization %s already exists", createOrgParams.Name), err) -} - -func TestCreateOrganizationOrgFailed(t *testing.T) { - adminCtx := auth.GetAdminContext() - createOrgParams := params.CreateOrgParams{ - Name: "test", - CredentialsName: "test", - WebhookSecret: "test", - } - - testCreds := config.Github{ - Name: "test", - Description: "test", - OAuth2Token: "test-token", - } - - storeMock := dbMocks.NewStore(t) - errMock := fmt.Errorf("mock error") - storeMock.On("GetOrganization", adminCtx, createOrgParams.Name).Return(params.Organization{}, runnerErrors.ErrNotFound) - storeMock.On("CreateOrganization", adminCtx, createOrgParams.Name, testCreds.Name, createOrgParams.WebhookSecret).Return(params.Organization{}, errMock) - runner := Runner{ - credentials: map[string]config.Github{ - "test": testCreds, + CreatePoolParams: params.CreatePoolParams{ + ProviderName: "test-provider", + MaxRunners: 4, + MinIdleRunners: 2, + Image: "test", + Flavor: "test", + OSType: "linux", + OSArch: "arm64", + Tags: []string{"self-hosted", "arm64", "linux"}, + RunnerBootstrapTimeout: 0, }, - store: storeMock, + CreateInstanceParams: params.CreateInstanceParams{ + Name: "test-instance-name", + OSType: "linux", + }, + UpdateRepoParams: params.UpdateRepositoryParams{ + CredentialsName: "test-creds", + WebhookSecret: "test-update-repo-webhook-secret", + }, + UpdatePoolParams: params.UpdatePoolParams{ + MaxRunners: &maxRunners, + MinIdleRunners: &minIdleRunners, + Image: "test-images-updated", + Flavor: "test-flavor-updated", + }, + UpdatePoolStateParams: params.UpdatePoolStateParams{ + WebhookSecret: "test-update-repo-webhook-secret", + }, + ErrMock: fmt.Errorf("mock error"), + ProviderMock: providerMock, + PoolMgrMock: runnerCommonMocks.NewPoolManager(s.T()), + PoolMgrCtrlMock: runnerMocks.NewPoolManagerController(s.T()), + } + s.Fixtures = fixtures + + // setup test runner + runner := &Runner{ + providers: fixtures.Providers, + credentials: fixtures.Credentials, + ctx: fixtures.AdminContext, + store: fixtures.Store, + poolManagerCtrl: fixtures.PoolMgrCtrlMock, + } + s.Runner = runner +} + +func (s *OrgTestSuite) TestCreateOrganization() { + // setup mocks expectations + s.Fixtures.PoolMgrMock.On("Start").Return(nil) + s.Fixtures.PoolMgrCtrlMock.On("CreateOrgPoolManager", s.Fixtures.AdminContext, mock.AnythingOfType("params.Organization"), s.Fixtures.Providers, s.Fixtures.Store).Return(s.Fixtures.PoolMgrMock, nil) + + // call tested function + org, err := s.Runner.CreateOrganization(s.Fixtures.AdminContext, s.Fixtures.CreateOrgParams) + + // assertions + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Nil(err) + s.Require().Equal(s.Fixtures.CreateOrgParams.Name, org.Name) + s.Require().Equal(s.Fixtures.Credentials[s.Fixtures.CreateOrgParams.CredentialsName].Name, org.CredentialsName) +} + +func (s *OrgTestSuite) TestCreateOrganizationErrUnauthorized() { + _, err := s.Runner.CreateOrganization(context.Background(), s.Fixtures.CreateOrgParams) + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestCreateOrganizationEmptyParams() { + _, err := s.Runner.CreateOrganization(s.Fixtures.AdminContext, params.CreateOrgParams{}) + + s.Require().Regexp("validating params: missing org name", err.Error()) +} + +func (s *OrgTestSuite) TestCreateOrganizationMissingCredentials() { + s.Fixtures.CreateOrgParams.CredentialsName = "not-existent-creds-name" + + _, err := s.Runner.CreateOrganization(s.Fixtures.AdminContext, s.Fixtures.CreateOrgParams) + + s.Require().Equal(runnerErrors.NewBadRequestError("credentials %s not defined", s.Fixtures.CreateOrgParams.CredentialsName), err) +} + +func (s *OrgTestSuite) TestCreateOrganizationAlreadyExists() { + s.Fixtures.CreateOrgParams.Name = "test-org-1" // this is already created in `SetupTest()` + + _, err := s.Runner.CreateOrganization(s.Fixtures.AdminContext, s.Fixtures.CreateOrgParams) + + s.Require().Equal(runnerErrors.NewConflictError("organization %s already exists", s.Fixtures.CreateOrgParams.Name), err) +} + +func (s *OrgTestSuite) TestCreateOrganizationPoolMgrFailed() { + s.Fixtures.PoolMgrCtrlMock.On("CreateOrgPoolManager", s.Fixtures.AdminContext, mock.AnythingOfType("params.Organization"), s.Fixtures.Providers, s.Fixtures.Store).Return(s.Fixtures.PoolMgrMock, s.Fixtures.ErrMock) + + _, err := s.Runner.CreateOrganization(s.Fixtures.AdminContext, s.Fixtures.CreateOrgParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Equal(fmt.Sprintf("creating org pool manager: %s", s.Fixtures.ErrMock.Error()), err.Error()) +} + +func (s *OrgTestSuite) TestCreateOrganizationStartPoolMgrFailed() { + s.Fixtures.PoolMgrMock.On("Start").Return(s.Fixtures.ErrMock) + s.Fixtures.PoolMgrCtrlMock.On("CreateOrgPoolManager", s.Fixtures.AdminContext, mock.AnythingOfType("params.Organization"), s.Fixtures.Providers, s.Fixtures.Store).Return(s.Fixtures.PoolMgrMock, nil) + s.Fixtures.PoolMgrCtrlMock.On("DeleteOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.ErrMock) + + _, err := s.Runner.CreateOrganization(s.Fixtures.AdminContext, s.Fixtures.CreateOrgParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Equal(fmt.Sprintf("starting org pool manager: %s", s.Fixtures.ErrMock.Error()), err.Error()) +} + +func (s *OrgTestSuite) TestListOrganizations() { + orgs, err := s.Runner.ListOrganizations(s.Fixtures.AdminContext) + + s.Require().Nil(err) + s.EqualOrgsByName(s.Fixtures.StoreOrgs, orgs) +} + +func (s *OrgTestSuite) TestListOrganizationsErrUnauthorized() { + _, err := s.Runner.ListOrganizations(context.Background()) + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestGetOrganizationByID() { + org, err := s.Runner.GetOrganizationByID(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID) + + s.Require().Nil(err) + s.Require().Equal(s.Fixtures.StoreOrgs["test-org-1"].ID, org.ID) +} + +func (s *OrgTestSuite) TestGetOrganizationByIDErrUnauthorized() { + _, err := s.Runner.GetOrganizationByID(context.Background(), "dummy-org-id") + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestDeleteOrganization() { + s.Fixtures.PoolMgrCtrlMock.On("DeleteOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(nil) + + err := s.Runner.DeleteOrganization(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-3"].ID) + + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Nil(err) + + _, err = s.Fixtures.Store.GetOrganizationByID(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-3"].ID) + s.Require().Equal("fetching org: not found", err.Error()) +} + +func (s *OrgTestSuite) TestDeleteOrganizationErrUnauthorized() { + err := s.Runner.DeleteOrganization(context.Background(), "dummy-org-id") + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestDeleteOrganizationPoolDefinedFailed() { + pool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create store organizations pool: %v", err)) } - org, err := runner.CreateOrganization(adminCtx, createOrgParams) - storeMock.AssertExpectations(t) - require.Equal(t, params.Organization{}, org) - require.Equal(t, "creating organization: mock error", err.Error()) + err = s.Runner.DeleteOrganization(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID) + + s.Require().Equal(runnerErrors.NewBadRequestError("org has pools defined (%s)", pool.ID), err) } -func TestListOrganizations(t *testing.T) { - adminCtx := auth.GetAdminContext() - storeMock := dbMocks.NewStore(t) - var orgs []params.Organization - storeMock.On("ListOrganizations", adminCtx).Return(orgs, nil) - runner := Runner{ - store: storeMock, +func (s *OrgTestSuite) TestDeleteOrganizationPoolMgrFailed() { + s.Fixtures.PoolMgrCtrlMock.On("DeleteOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.ErrMock) + + err := s.Runner.DeleteOrganization(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID) + + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Equal(fmt.Sprintf("deleting org pool manager: %s", s.Fixtures.ErrMock.Error()), err.Error()) +} + +func (s *OrgTestSuite) TestUpdateOrganization() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) + s.Fixtures.PoolMgrCtrlMock.On("CreateOrgPoolManager", s.Fixtures.AdminContext, mock.AnythingOfType("params.Organization"), s.Fixtures.Providers, s.Fixtures.Store).Return(s.Fixtures.PoolMgrMock, nil) + + org, err := s.Runner.UpdateOrganization(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.UpdateRepoParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Nil(err) + s.Require().Equal(s.Fixtures.UpdateRepoParams.CredentialsName, org.CredentialsName) + s.Require().Equal(s.Fixtures.UpdateRepoParams.WebhookSecret, org.WebhookSecret) +} + +func (s *OrgTestSuite) TestUpdateOrganizationErrUnauthorized() { + _, err := s.Runner.UpdateOrganization(context.Background(), "dummy-org-id", s.Fixtures.UpdateRepoParams) + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestUpdateOrganizationInvalidCreds() { + s.Fixtures.UpdateRepoParams.CredentialsName = "invalid-creds-name" + + _, err := s.Runner.UpdateOrganization(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.UpdateRepoParams) + + s.Require().Equal(runnerErrors.NewBadRequestError("invalid credentials (%s) for org %s", s.Fixtures.UpdateRepoParams.CredentialsName, s.Fixtures.StoreOrgs["test-org-1"].Name), err) +} + +func (s *OrgTestSuite) TestUpdateOrganizationPoolMgrFailed() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, s.Fixtures.ErrMock) + s.Fixtures.PoolMgrMock.On("RefreshState", s.Fixtures.UpdatePoolStateParams).Return(s.Fixtures.ErrMock) + + _, err := s.Runner.UpdateOrganization(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.UpdateRepoParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Equal(fmt.Sprintf("updating org pool manager: %s", s.Fixtures.ErrMock.Error()), err.Error()) +} + +func (s *OrgTestSuite) TestUpdateOrganizationCreateOrgPoolMgrFailed() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) + s.Fixtures.PoolMgrCtrlMock.On("CreateOrgPoolManager", s.Fixtures.AdminContext, mock.AnythingOfType("params.Organization"), s.Fixtures.Providers, s.Fixtures.Store).Return(s.Fixtures.PoolMgrMock, s.Fixtures.ErrMock) + + _, err := s.Runner.UpdateOrganization(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.UpdateRepoParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Equal(fmt.Sprintf("creating org pool manager: %s", s.Fixtures.ErrMock.Error()), err.Error()) +} + +func (s *OrgTestSuite) TestCreateOrgPool() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) + + pool, err := s.Runner.CreateOrgPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Nil(err) + + org, err := s.Fixtures.Store.GetOrganizationByID(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID) + if err != nil { + s.FailNow(fmt.Sprintf("cannot get org by ID: %v", err)) + } + s.Require().Equal(1, len(org.Pools)) + s.Require().Equal(pool.ID, org.Pools[0].ID) + s.Require().Equal(s.Fixtures.CreatePoolParams.ProviderName, org.Pools[0].ProviderName) + s.Require().Equal(s.Fixtures.CreatePoolParams.MaxRunners, org.Pools[0].MaxRunners) + s.Require().Equal(s.Fixtures.CreatePoolParams.MinIdleRunners, org.Pools[0].MinIdleRunners) +} + +func (s *OrgTestSuite) TestCreateOrgPoolErrUnauthorized() { + _, err := s.Runner.CreateOrgPool(context.Background(), "dummy-org-id", s.Fixtures.CreatePoolParams) + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestCreateOrgPoolErrNotFound() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, runnerErrors.ErrNotFound) + + _, err := s.Runner.CreateOrgPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Equal(runnerErrors.ErrNotFound, err) +} + +func (s *OrgTestSuite) TestCreateOrgPoolFetchPoolParamsFailed() { + s.Fixtures.CreatePoolParams.ProviderName = "not-existent-provider-name" + + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) + + _, err := s.Runner.CreateOrgPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Regexp("fetching pool params: no such provider", err.Error()) +} + +func (s *OrgTestSuite) TestGetOrgPoolByID() { + orgPool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create org pool: %s", err)) } - org, err := runner.ListOrganizations(adminCtx) - storeMock.AssertExpectations(t) - require.Nil(t, err) - var exceptOrgs []params.Organization - require.Equal(t, exceptOrgs, org) + pool, err := s.Runner.GetOrgPoolByID(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, orgPool.ID) + + s.Require().Nil(err) + s.Require().Equal(orgPool.ID, pool.ID) } -func TestListOrganizationsErrUnauthorized(t *testing.T) { - ctx := context.Background() - runner := Runner{} +func (s *OrgTestSuite) TestGetOrgPoolByIDErrUnauthorized() { + _, err := s.Runner.GetOrgPoolByID(context.Background(), "dummy-org-id", "dummy-pool-id") - org, err := runner.ListOrganizations(ctx) - var expectedOrg []params.Organization - require.Equal(t, expectedOrg, org) - require.Equal(t, runnerErrors.ErrUnauthorized, err) + s.Require().Equal(runnerErrors.ErrUnauthorized, err) } -func TestListOrganizationsOrgMissing(t *testing.T) { - adminCtx := auth.GetAdminContext() - storeMock := dbMocks.NewStore(t) - - errMock := fmt.Errorf("mock error") - storeMock.On("ListOrganizations", adminCtx).Return(nil, errMock) - runner := Runner{ - store: storeMock, +func (s *OrgTestSuite) TestDeleteOrgPool() { + pool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create org pool: %s", err)) } - org, err := runner.ListOrganizations(adminCtx) - storeMock.AssertExpectations(t) - var exceptOrg []params.Organization - require.Equal(t, exceptOrg, org) - require.Regexp(t, "listing organizations: mock error", err.Error()) + err = s.Runner.DeleteOrgPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, pool.ID) + + s.Require().Nil(err) + + _, err = s.Fixtures.Store.GetOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, pool.ID) + s.Require().Equal("fetching pool: not found", err.Error()) +} + +func (s *OrgTestSuite) TestDeleteOrgPoolErrUnauthorized() { + err := s.Runner.DeleteOrgPool(context.Background(), "dummy-org-id", "dummy-pool-id") + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestDeleteOrgPoolRunnersFailed() { + pool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) + } + s.Fixtures.CreateInstanceParams.Pool = pool.ID + instance, err := s.Fixtures.Store.CreateInstance(s.Fixtures.AdminContext, pool.ID, s.Fixtures.CreateInstanceParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create instance: %s", err)) + } + + err = s.Runner.DeleteOrgPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, pool.ID) + + s.Require().Equal(runnerErrors.NewBadRequestError("pool has runners: %s", instance.ID), err) +} + +func (s *OrgTestSuite) TestListOrgPools() { + orgPools := []params.Pool{} + for i := 1; i <= 2; i++ { + s.Fixtures.CreatePoolParams.Image = fmt.Sprintf("test-org-%v", i) + pool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) + } + orgPools = append(orgPools, pool) + } + + pools, err := s.Runner.ListOrgPools(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID) + + s.Require().Nil(err) + s.EqualPoolsByID(orgPools, pools) +} + +func (s *OrgTestSuite) TestListOrgPoolsErrUnauthorized() { + _, err := s.Runner.ListOrgPools(context.Background(), "dummy-org-id") + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestUpdateOrgPool() { + orgPool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create org pool: %s", err)) + } + + pool, err := s.Runner.UpdateOrgPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, orgPool.ID, s.Fixtures.UpdatePoolParams) + + s.Require().Nil(err) + s.Require().Equal(*s.Fixtures.UpdatePoolParams.MaxRunners, pool.MaxRunners) + s.Require().Equal(*s.Fixtures.UpdatePoolParams.MinIdleRunners, pool.MinIdleRunners) +} + +func (s *OrgTestSuite) TestUpdateOrgPoolErrUnauthorized() { + _, err := s.Runner.UpdateOrgPool(context.Background(), "dummy-org-id", "dummy-pool-id", s.Fixtures.UpdatePoolParams) + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestUpdateOrgPoolCompareFailed() { + pool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create org pool: %s", err)) + } + var maxRunners uint = 10 + var minIdleRunners uint = 11 + s.Fixtures.UpdatePoolParams.MaxRunners = &maxRunners + s.Fixtures.UpdatePoolParams.MinIdleRunners = &minIdleRunners + + _, err = s.Runner.UpdateOrgPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, pool.ID, s.Fixtures.UpdatePoolParams) + + s.Require().Equal(runnerErrors.NewBadRequestError("min_idle_runners cannot be larger than max_runners"), err) +} + +func (s *OrgTestSuite) TestListOrgInstances() { + pool, err := s.Fixtures.Store.CreateOrganizationPool(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID, s.Fixtures.CreatePoolParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) + } + poolInstances := []params.Instance{} + s.Fixtures.CreateInstanceParams.Pool = pool.ID + for i := 1; i <= 3; i++ { + s.Fixtures.CreateInstanceParams.Name = fmt.Sprintf("test-org-%v", i) + instance, err := s.Fixtures.Store.CreateInstance(s.Fixtures.AdminContext, pool.ID, s.Fixtures.CreateInstanceParams) + if err != nil { + s.FailNow(fmt.Sprintf("cannot create instance: %s", err)) + } + poolInstances = append(poolInstances, instance) + } + + instances, err := s.Runner.ListOrgInstances(s.Fixtures.AdminContext, s.Fixtures.StoreOrgs["test-org-1"].ID) + + s.Require().Nil(err) + s.EqualInstancesByName(poolInstances, instances) +} + +func (s *OrgTestSuite) TestListOrgInstancesErrUnauthorized() { + _, err := s.Runner.ListOrgInstances(context.Background(), "dummy-org-id") + + s.Require().Equal(runnerErrors.ErrUnauthorized, err) +} + +func (s *OrgTestSuite) TestFindOrgPoolManager() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) + + poolManager, err := s.Runner.findOrgPoolManager(s.Fixtures.StoreOrgs["test-org-1"].Name) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Nil(err) + s.Require().Equal(s.Fixtures.PoolMgrMock, poolManager) +} + +func (s *OrgTestSuite) TestFindOrgPoolManagerFetchPoolMgrFailed() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, s.Fixtures.ErrMock) + + _, err := s.Runner.findOrgPoolManager(s.Fixtures.StoreOrgs["test-org-1"].Name) + + s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) + s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) + s.Require().Regexp("fetching pool manager for org", err.Error()) +} + +func TestOrgTestSuite(t *testing.T) { + suite.Run(t, new(OrgTestSuite)) } diff --git a/vendor/github.com/stretchr/testify/suite/doc.go b/vendor/github.com/stretchr/testify/suite/doc.go new file mode 100644 index 00000000..f91a245d --- /dev/null +++ b/vendor/github.com/stretchr/testify/suite/doc.go @@ -0,0 +1,65 @@ +// Package suite contains logic for creating testing suite structs +// and running the methods on those structs as tests. The most useful +// piece of this package is that you can create setup/teardown methods +// on your testing suites, which will run before/after the whole suite +// or individual tests (depending on which interface(s) you +// implement). +// +// A testing suite is usually built by first extending the built-in +// suite functionality from suite.Suite in testify. Alternatively, +// you could reproduce that logic on your own if you wanted (you +// just need to implement the TestingSuite interface from +// suite/interfaces.go). +// +// After that, you can implement any of the interfaces in +// suite/interfaces.go to add setup/teardown functionality to your +// suite, and add any methods that start with "Test" to add tests. +// Methods that do not match any suite interfaces and do not begin +// with "Test" will not be run by testify, and can safely be used as +// helper methods. +// +// Once you've built your testing suite, you need to run the suite +// (using suite.Run from testify) inside any function that matches the +// identity that "go test" is already looking for (i.e. +// func(*testing.T)). +// +// Regular expression to select test suites specified command-line +// argument "-run". Regular expression to select the methods +// of test suites specified command-line argument "-m". +// Suite object has assertion methods. +// +// A crude example: +// // Basic imports +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/suite" +// ) +// +// // Define the suite, and absorb the built-in basic suite +// // functionality from testify - including a T() method which +// // returns the current testing context +// type ExampleTestSuite struct { +// suite.Suite +// VariableThatShouldStartAtFive int +// } +// +// // Make sure that VariableThatShouldStartAtFive is set to five +// // before each test +// func (suite *ExampleTestSuite) SetupTest() { +// suite.VariableThatShouldStartAtFive = 5 +// } +// +// // All methods that begin with "Test" are run as tests within a +// // suite. +// func (suite *ExampleTestSuite) TestExample() { +// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) +// suite.Equal(5, suite.VariableThatShouldStartAtFive) +// } +// +// // In order for 'go test' to run this suite, we need to create +// // a normal test function and pass our suite to suite.Run +// func TestExampleTestSuite(t *testing.T) { +// suite.Run(t, new(ExampleTestSuite)) +// } +package suite diff --git a/vendor/github.com/stretchr/testify/suite/interfaces.go b/vendor/github.com/stretchr/testify/suite/interfaces.go new file mode 100644 index 00000000..8b98a8af --- /dev/null +++ b/vendor/github.com/stretchr/testify/suite/interfaces.go @@ -0,0 +1,53 @@ +package suite + +import "testing" + +// TestingSuite can store and return the current *testing.T context +// generated by 'go test'. +type TestingSuite interface { + T() *testing.T + SetT(*testing.T) +} + +// SetupAllSuite has a SetupSuite method, which will run before the +// tests in the suite are run. +type SetupAllSuite interface { + SetupSuite() +} + +// SetupTestSuite has a SetupTest method, which will run before each +// test in the suite. +type SetupTestSuite interface { + SetupTest() +} + +// TearDownAllSuite has a TearDownSuite method, which will run after +// all the tests in the suite have been run. +type TearDownAllSuite interface { + TearDownSuite() +} + +// TearDownTestSuite has a TearDownTest method, which will run after +// each test in the suite. +type TearDownTestSuite interface { + TearDownTest() +} + +// BeforeTest has a function to be executed right before the test +// starts and receives the suite and test names as input +type BeforeTest interface { + BeforeTest(suiteName, testName string) +} + +// AfterTest has a function to be executed right after the test +// finishes and receives the suite and test names as input +type AfterTest interface { + AfterTest(suiteName, testName string) +} + +// WithStats implements HandleStats, a function that will be executed +// when a test suite is finished. The stats contain information about +// the execution of that suite and its tests. +type WithStats interface { + HandleStats(suiteName string, stats *SuiteInformation) +} diff --git a/vendor/github.com/stretchr/testify/suite/stats.go b/vendor/github.com/stretchr/testify/suite/stats.go new file mode 100644 index 00000000..261da37f --- /dev/null +++ b/vendor/github.com/stretchr/testify/suite/stats.go @@ -0,0 +1,46 @@ +package suite + +import "time" + +// SuiteInformation stats stores stats for the whole suite execution. +type SuiteInformation struct { + Start, End time.Time + TestStats map[string]*TestInformation +} + +// TestInformation stores information about the execution of each test. +type TestInformation struct { + TestName string + Start, End time.Time + Passed bool +} + +func newSuiteInformation() *SuiteInformation { + testStats := make(map[string]*TestInformation) + + return &SuiteInformation{ + TestStats: testStats, + } +} + +func (s SuiteInformation) start(testName string) { + s.TestStats[testName] = &TestInformation{ + TestName: testName, + Start: time.Now(), + } +} + +func (s SuiteInformation) end(testName string, passed bool) { + s.TestStats[testName].End = time.Now() + s.TestStats[testName].Passed = passed +} + +func (s SuiteInformation) Passed() bool { + for _, stats := range s.TestStats { + if !stats.Passed { + return false + } + } + + return true +} diff --git a/vendor/github.com/stretchr/testify/suite/suite.go b/vendor/github.com/stretchr/testify/suite/suite.go new file mode 100644 index 00000000..89559187 --- /dev/null +++ b/vendor/github.com/stretchr/testify/suite/suite.go @@ -0,0 +1,226 @@ +package suite + +import ( + "flag" + "fmt" + "os" + "reflect" + "regexp" + "runtime/debug" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var allTestsFilter = func(_, _ string) (bool, error) { return true, nil } +var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") + +// Suite is a basic testing suite with methods for storing and +// retrieving the current *testing.T context. +type Suite struct { + *assert.Assertions + mu sync.RWMutex + require *require.Assertions + t *testing.T +} + +// T retrieves the current *testing.T context. +func (suite *Suite) T() *testing.T { + suite.mu.RLock() + defer suite.mu.RUnlock() + return suite.t +} + +// SetT sets the current *testing.T context. +func (suite *Suite) SetT(t *testing.T) { + suite.mu.Lock() + defer suite.mu.Unlock() + suite.t = t + suite.Assertions = assert.New(t) + suite.require = require.New(t) +} + +// Require returns a require context for suite. +func (suite *Suite) Require() *require.Assertions { + suite.mu.Lock() + defer suite.mu.Unlock() + if suite.require == nil { + suite.require = require.New(suite.T()) + } + return suite.require +} + +// Assert returns an assert context for suite. Normally, you can call +// `suite.NoError(expected, actual)`, but for situations where the embedded +// methods are overridden (for example, you might want to override +// assert.Assertions with require.Assertions), this method is provided so you +// can call `suite.Assert().NoError()`. +func (suite *Suite) Assert() *assert.Assertions { + suite.mu.Lock() + defer suite.mu.Unlock() + if suite.Assertions == nil { + suite.Assertions = assert.New(suite.T()) + } + return suite.Assertions +} + +func recoverAndFailOnPanic(t *testing.T) { + r := recover() + failOnPanic(t, r) +} + +func failOnPanic(t *testing.T, r interface{}) { + if r != nil { + t.Errorf("test panicked: %v\n%s", r, debug.Stack()) + t.FailNow() + } +} + +// Run provides suite functionality around golang subtests. It should be +// called in place of t.Run(name, func(t *testing.T)) in test suite code. +// The passed-in func will be executed as a subtest with a fresh instance of t. +// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName. +func (suite *Suite) Run(name string, subtest func()) bool { + oldT := suite.T() + defer suite.SetT(oldT) + return oldT.Run(name, func(t *testing.T) { + suite.SetT(t) + subtest() + }) +} + +// Run takes a testing suite and runs all of the tests attached +// to it. +func Run(t *testing.T, suite TestingSuite) { + defer recoverAndFailOnPanic(t) + + suite.SetT(t) + + var suiteSetupDone bool + + var stats *SuiteInformation + if _, ok := suite.(WithStats); ok { + stats = newSuiteInformation() + } + + tests := []testing.InternalTest{} + methodFinder := reflect.TypeOf(suite) + suiteName := methodFinder.Elem().Name() + + for i := 0; i < methodFinder.NumMethod(); i++ { + method := methodFinder.Method(i) + + ok, err := methodFilter(method.Name) + if err != nil { + fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) + os.Exit(1) + } + + if !ok { + continue + } + + if !suiteSetupDone { + if stats != nil { + stats.Start = time.Now() + } + + if setupAllSuite, ok := suite.(SetupAllSuite); ok { + setupAllSuite.SetupSuite() + } + + suiteSetupDone = true + } + + test := testing.InternalTest{ + Name: method.Name, + F: func(t *testing.T) { + parentT := suite.T() + suite.SetT(t) + defer recoverAndFailOnPanic(t) + defer func() { + r := recover() + + if stats != nil { + passed := !t.Failed() && r == nil + stats.end(method.Name, passed) + } + + if afterTestSuite, ok := suite.(AfterTest); ok { + afterTestSuite.AfterTest(suiteName, method.Name) + } + + if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { + tearDownTestSuite.TearDownTest() + } + + suite.SetT(parentT) + failOnPanic(t, r) + }() + + if setupTestSuite, ok := suite.(SetupTestSuite); ok { + setupTestSuite.SetupTest() + } + if beforeTestSuite, ok := suite.(BeforeTest); ok { + beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) + } + + if stats != nil { + stats.start(method.Name) + } + + method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) + }, + } + tests = append(tests, test) + } + if suiteSetupDone { + defer func() { + if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { + tearDownAllSuite.TearDownSuite() + } + + if suiteWithStats, measureStats := suite.(WithStats); measureStats { + stats.End = time.Now() + suiteWithStats.HandleStats(suiteName, stats) + } + }() + } + + runTests(t, tests) +} + +// Filtering method according to set regular expression +// specified command-line argument -m +func methodFilter(name string) (bool, error) { + if ok, _ := regexp.MatchString("^Test", name); !ok { + return false, nil + } + return regexp.MatchString(*matchMethod, name) +} + +func runTests(t testing.TB, tests []testing.InternalTest) { + if len(tests) == 0 { + t.Log("warning: no tests to run") + return + } + + r, ok := t.(runner) + if !ok { // backwards compatibility with Go 1.6 and below + if !testing.RunTests(allTestsFilter, tests) { + t.Fail() + } + return + } + + for _, test := range tests { + r.Run(test.Name, test.F) + } +} + +type runner interface { + Run(name string, f func(t *testing.T)) bool +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0dffd300..a8375b14 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -167,6 +167,7 @@ github.com/stretchr/objx github.com/stretchr/testify/assert github.com/stretchr/testify/mock github.com/stretchr/testify/require +github.com/stretchr/testify/suite # github.com/xdg-go/stringprep v1.0.3 ## explicit; go 1.11 # golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064