From 9789543f8f98bd444638a65e80f6916738ef168f Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Tue, 10 Jan 2017 16:49:14 -0500 Subject: [PATCH] add mock package and use in invite tests (#603) * add mock package and use in invite tests * mock expired invite test --- server/mock/datastore.go | 38 +++++ server/mock/datastore_appconfig.go | 39 +++++ server/mock/datastore_appconfig_helpers.go | 9 ++ server/mock/datastore_invites.go | 79 ++++++++++ server/mock/datastore_invites_helpers.go | 33 ++++ server/mock/datastore_users.go | 69 +++++++++ server/mock/datastore_users_helpers.go | 21 +++ server/mock/errors.go | 19 +++ server/service/service_invites_test.go | 171 +++++++++++++-------- 9 files changed, 416 insertions(+), 62 deletions(-) create mode 100644 server/mock/datastore.go create mode 100644 server/mock/datastore_appconfig.go create mode 100644 server/mock/datastore_appconfig_helpers.go create mode 100644 server/mock/datastore_invites.go create mode 100644 server/mock/datastore_invites_helpers.go create mode 100644 server/mock/datastore_users.go create mode 100644 server/mock/datastore_users_helpers.go create mode 100644 server/mock/errors.go diff --git a/server/mock/datastore.go b/server/mock/datastore.go new file mode 100644 index 00000000..e1b3b525 --- /dev/null +++ b/server/mock/datastore.go @@ -0,0 +1,38 @@ +package mock + +//go:generate mockimpl -o datastore_users.go "s *UserStore" "kolide.UserStore" +//go:generate mockimpl -o datastore_invites.go "s *InviteStore" "kolide.InviteStore" +//go:generate mockimpl -o datastore_appconfig.go "s *AppConfigStore" "kolide.AppConfigStore" + +import "github.com/kolide/kolide-ose/server/kolide" + +var _ kolide.Datastore = (*Store)(nil) + +type Store struct { + kolide.HostStore + kolide.LabelStore + kolide.PackStore + kolide.CampaignStore + kolide.SessionStore + kolide.PasswordResetStore + kolide.QueryStore + kolide.OptionStore + kolide.ScheduledQueryStore + + InviteStore + UserStore + AppConfigStore +} + +func (m *Store) Drop() error { + return nil +} +func (m *Store) MigrateTables() error { + return nil +} +func (m *Store) MigrateData() error { + return nil +} +func (m *Store) Name() string { + return "mock" +} diff --git a/server/mock/datastore_appconfig.go b/server/mock/datastore_appconfig.go new file mode 100644 index 00000000..982628d5 --- /dev/null +++ b/server/mock/datastore_appconfig.go @@ -0,0 +1,39 @@ +// Automatically generated by mockimpl. DO NOT EDIT! + +package mock + +import "github.com/kolide/kolide-ose/server/kolide" + +var _ kolide.AppConfigStore = (*AppConfigStore)(nil) + +type NewAppConfigFunc func(info *kolide.AppConfig) (*kolide.AppConfig, error) + +type AppConfigFunc func() (*kolide.AppConfig, error) + +type SaveAppConfigFunc func(info *kolide.AppConfig) error + +type AppConfigStore struct { + NewAppConfigFunc NewAppConfigFunc + NewAppConfigFuncInvoked bool + + AppConfigFunc AppConfigFunc + AppConfigFuncInvoked bool + + SaveAppConfigFunc SaveAppConfigFunc + SaveAppConfigFuncInvoked bool +} + +func (s *AppConfigStore) NewAppConfig(info *kolide.AppConfig) (*kolide.AppConfig, error) { + s.NewAppConfigFuncInvoked = true + return s.NewAppConfigFunc(info) +} + +func (s *AppConfigStore) AppConfig() (*kolide.AppConfig, error) { + s.AppConfigFuncInvoked = true + return s.AppConfigFunc() +} + +func (s *AppConfigStore) SaveAppConfig(info *kolide.AppConfig) error { + s.SaveAppConfigFuncInvoked = true + return s.SaveAppConfigFunc(info) +} diff --git a/server/mock/datastore_appconfig_helpers.go b/server/mock/datastore_appconfig_helpers.go new file mode 100644 index 00000000..ea197ab3 --- /dev/null +++ b/server/mock/datastore_appconfig_helpers.go @@ -0,0 +1,9 @@ +package mock + +import "github.com/kolide/kolide-ose/server/kolide" + +func ReturnFakeAppConfig(fake *kolide.AppConfig) AppConfigFunc { + return func() (*kolide.AppConfig, error) { + return fake, nil + } +} diff --git a/server/mock/datastore_invites.go b/server/mock/datastore_invites.go new file mode 100644 index 00000000..04723c2c --- /dev/null +++ b/server/mock/datastore_invites.go @@ -0,0 +1,79 @@ +// Automatically generated by mockimpl. DO NOT EDIT! + +package mock + +import "github.com/kolide/kolide-ose/server/kolide" + +var _ kolide.InviteStore = (*InviteStore)(nil) + +type NewInviteFunc func(i *kolide.Invite) (*kolide.Invite, error) + +type ListInvitesFunc func(opt kolide.ListOptions) ([]*kolide.Invite, error) + +type InviteFunc func(id uint) (*kolide.Invite, error) + +type InviteByEmailFunc func(email string) (*kolide.Invite, error) + +type InviteByTokenFunc func(token string) (*kolide.Invite, error) + +type SaveInviteFunc func(i *kolide.Invite) error + +type DeleteInviteFunc func(id uint) error + +type InviteStore struct { + NewInviteFunc NewInviteFunc + NewInviteFuncInvoked bool + + ListInvitesFunc ListInvitesFunc + ListInvitesFuncInvoked bool + + InviteFunc InviteFunc + InviteFuncInvoked bool + + InviteByEmailFunc InviteByEmailFunc + InviteByEmailFuncInvoked bool + + InviteByTokenFunc InviteByTokenFunc + InviteByTokenFuncInvoked bool + + SaveInviteFunc SaveInviteFunc + SaveInviteFuncInvoked bool + + DeleteInviteFunc DeleteInviteFunc + DeleteInviteFuncInvoked bool +} + +func (s *InviteStore) NewInvite(i *kolide.Invite) (*kolide.Invite, error) { + s.NewInviteFuncInvoked = true + return s.NewInviteFunc(i) +} + +func (s *InviteStore) ListInvites(opt kolide.ListOptions) ([]*kolide.Invite, error) { + s.ListInvitesFuncInvoked = true + return s.ListInvitesFunc(opt) +} + +func (s *InviteStore) Invite(id uint) (*kolide.Invite, error) { + s.InviteFuncInvoked = true + return s.InviteFunc(id) +} + +func (s *InviteStore) InviteByEmail(email string) (*kolide.Invite, error) { + s.InviteByEmailFuncInvoked = true + return s.InviteByEmailFunc(email) +} + +func (s *InviteStore) InviteByToken(token string) (*kolide.Invite, error) { + s.InviteByTokenFuncInvoked = true + return s.InviteByTokenFunc(token) +} + +func (s *InviteStore) SaveInvite(i *kolide.Invite) error { + s.SaveInviteFuncInvoked = true + return s.SaveInviteFunc(i) +} + +func (s *InviteStore) DeleteInvite(id uint) error { + s.DeleteInviteFuncInvoked = true + return s.DeleteInviteFunc(id) +} diff --git a/server/mock/datastore_invites_helpers.go b/server/mock/datastore_invites_helpers.go new file mode 100644 index 00000000..80e10a52 --- /dev/null +++ b/server/mock/datastore_invites_helpers.go @@ -0,0 +1,33 @@ +package mock + +import "github.com/kolide/kolide-ose/server/kolide" + +func ReturnNewInivite(fake *kolide.Invite) NewInviteFunc { + return func(i *kolide.Invite) (*kolide.Invite, error) { + return fake, nil + } +} + +func ReturnFakeInviteByEmail(fake *kolide.Invite) InviteByEmailFunc { + return func(string) (*kolide.Invite, error) { + return fake, nil + } +} + +func ReturnFakeInviteByToken(fake *kolide.Invite) InviteByTokenFunc { + return func(string) (*kolide.Invite, error) { + return fake, nil + } +} + +func ReturnInviteFuncNotFound() InviteFunc { + return func(id uint) (*kolide.Invite, error) { + return nil, &Error{"not found"} + } +} + +func ReturnFakeInviteByID(fake *kolide.Invite) InviteFunc { + return func(id uint) (*kolide.Invite, error) { + return fake, nil + } +} diff --git a/server/mock/datastore_users.go b/server/mock/datastore_users.go new file mode 100644 index 00000000..f397b8de --- /dev/null +++ b/server/mock/datastore_users.go @@ -0,0 +1,69 @@ +// Automatically generated by mockimpl. DO NOT EDIT! + +package mock + +import "github.com/kolide/kolide-ose/server/kolide" + +var _ kolide.UserStore = (*UserStore)(nil) + +type NewUserFunc func(user *kolide.User) (*kolide.User, error) + +type UserFunc func(username string) (*kolide.User, error) + +type ListUsersFunc func(opt kolide.ListOptions) ([]*kolide.User, error) + +type UserByEmailFunc func(email string) (*kolide.User, error) + +type UserByIDFunc func(id uint) (*kolide.User, error) + +type SaveUserFunc func(user *kolide.User) error + +type UserStore struct { + NewUserFunc NewUserFunc + NewUserFuncInvoked bool + + UserFunc UserFunc + UserFuncInvoked bool + + ListUsersFunc ListUsersFunc + ListUsersFuncInvoked bool + + UserByEmailFunc UserByEmailFunc + UserByEmailFuncInvoked bool + + UserByIDFunc UserByIDFunc + UserByIDFuncInvoked bool + + SaveUserFunc SaveUserFunc + SaveUserFuncInvoked bool +} + +func (s *UserStore) NewUser(user *kolide.User) (*kolide.User, error) { + s.NewUserFuncInvoked = true + return s.NewUserFunc(user) +} + +func (s *UserStore) User(username string) (*kolide.User, error) { + s.UserFuncInvoked = true + return s.UserFunc(username) +} + +func (s *UserStore) ListUsers(opt kolide.ListOptions) ([]*kolide.User, error) { + s.ListUsersFuncInvoked = true + return s.ListUsersFunc(opt) +} + +func (s *UserStore) UserByEmail(email string) (*kolide.User, error) { + s.UserByEmailFuncInvoked = true + return s.UserByEmailFunc(email) +} + +func (s *UserStore) UserByID(id uint) (*kolide.User, error) { + s.UserByIDFuncInvoked = true + return s.UserByIDFunc(id) +} + +func (s *UserStore) SaveUser(user *kolide.User) error { + s.SaveUserFuncInvoked = true + return s.SaveUserFunc(user) +} diff --git a/server/mock/datastore_users_helpers.go b/server/mock/datastore_users_helpers.go new file mode 100644 index 00000000..1c2cd3d8 --- /dev/null +++ b/server/mock/datastore_users_helpers.go @@ -0,0 +1,21 @@ +package mock + +import "github.com/kolide/kolide-ose/server/kolide" + +func UserByEmailWithUser(u *kolide.User) UserByEmailFunc { + return func(email string) (*kolide.User, error) { + return u, nil + } +} + +func UserWithEmailNotFound() UserByEmailFunc { + return func(email string) (*kolide.User, error) { + return nil, &Error{"not found"} + } +} + +func UserWithID(u *kolide.User) UserByIDFunc { + return func(id uint) (*kolide.User, error) { + return u, nil + } +} diff --git a/server/mock/errors.go b/server/mock/errors.go new file mode 100644 index 00000000..983a5e44 --- /dev/null +++ b/server/mock/errors.go @@ -0,0 +1,19 @@ +package mock + +type Error struct { + Message string +} + +func (e *Error) Error() string { + return e.Message +} + +// implement kolide.NotFoundError +func (e *Error) IsNotFound() bool { + return true +} + +// implement kolide.AlreadyExistsError +func (e *Error) IsExists() bool { + return true +} diff --git a/server/service/service_invites_test.go b/server/service/service_invites_test.go index aa225a29..7d6d77a1 100644 --- a/server/service/service_invites_test.go +++ b/server/service/service_invites_test.go @@ -1,80 +1,127 @@ package service import ( - "errors" "testing" - - "golang.org/x/net/context" + "time" "github.com/WatchBeam/clock" "github.com/kolide/kolide-ose/server/config" - "github.com/kolide/kolide-ose/server/datastore/inmem" "github.com/kolide/kolide-ose/server/kolide" + "github.com/kolide/kolide-ose/server/mock" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" ) -func TestInviteNewUser(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - createTestUsers(t, ds) - createTestAppConfig(t, ds) - assert.Nil(t, err) - nosuchAdminID := uint(999) - adminID := uint(1) +func TestInviteNewUserMock(t *testing.T) { + svc, mockStore, mailer := setupInviteTest(t) + ctx := context.Background() + + payload := kolide.InvitePayload{ + Email: stringPtr("user@acme.co"), + InvitedBy: &adminUser.ID, + Admin: boolPtr(false), + } + + // happy path + invite, err := svc.InviteNewUser(ctx, payload) + require.Nil(t, err) + assert.Equal(t, invite.ID, validInvite.ID) + assert.True(t, mockStore.NewInviteFuncInvoked) + assert.True(t, mockStore.AppConfigFuncInvoked) + assert.True(t, mailer.Invoked) + + mockStore.UserByEmailFunc = mock.UserByEmailWithUser(new(kolide.User)) + _, err = svc.InviteNewUser(ctx, payload) + require.NotNil(t, err, "should err if the user we're inviting already exists") +} + +func TestVerifyInvite(t *testing.T) { + ms := new(mock.Store) + svc := service{ + ds: ms, + config: config.TestConfig(), + clock: clock.NewMockClock(), + } + ctx := context.Background() + + ms.InviteByTokenFunc = mock.ReturnFakeInviteByToken(expiredInvite) + wantErr := &invalidArgumentError{{name: "invite_token", reason: "Invite token has expired."}} + _, err := svc.VerifyInvite(ctx, expiredInvite.Token) + assert.Equal(t, err, wantErr) + + wantErr = &invalidArgumentError{{name: "invite_token", + reason: "Invite Token does not match Email Address."}} + + _, err = svc.VerifyInvite(ctx, "bad_token") + assert.Equal(t, err, wantErr) +} + +func TestDeleteInvite(t *testing.T) { + ms := new(mock.Store) + svc := service{ds: ms} + + ms.DeleteInviteFunc = func(uint) error { return nil } + err := svc.DeleteInvite(context.Background(), 1) + require.Nil(t, err) + assert.True(t, ms.DeleteInviteFuncInvoked) +} + +func TestListInvites(t *testing.T) { + ms := new(mock.Store) + svc := service{ds: ms} + + ms.ListInvitesFunc = func(kolide.ListOptions) ([]*kolide.Invite, error) { + return nil, nil + } + _, err := svc.ListInvites(context.Background(), kolide.ListOptions{}) + require.Nil(t, err) + assert.True(t, ms.ListInvitesFuncInvoked) +} + +func setupInviteTest(t *testing.T) (kolide.Service, *mock.Store, *mockMailService) { + ms := new(mock.Store) + ms.UserByEmailFunc = mock.UserWithEmailNotFound() + ms.UserByIDFunc = mock.UserWithID(adminUser) + ms.NewInviteFunc = mock.ReturnNewInivite(validInvite) + ms.AppConfigFunc = mock.ReturnFakeAppConfig(&kolide.AppConfig{ + KolideServerURL: "https://acme.co", + }) mailer := &mockMailService{SendEmailFn: func(e kolide.Email) error { return nil }} + svc := validationMiddleware{service{ - ds: ds, + ds: ms, config: config.TestConfig(), mailService: mailer, clock: clock.NewMockClock(), }} - - var inviteTests = []struct { - payload kolide.InvitePayload - wantErr error - }{ - { - wantErr: &invalidArgumentError{ - {name: "email", reason: "missing required argument"}, - {name: "invited_by", reason: "missing required argument"}, - {name: "admin", reason: "missing required argument"}, - }, - }, - { - payload: kolide.InvitePayload{ - Email: stringPtr("nosuchuser@example.com"), - InvitedBy: &nosuchAdminID, - Admin: boolPtr(false), - }, - wantErr: errors.New("User 999 was not found in the datastore"), - }, - { - payload: kolide.InvitePayload{ - Email: stringPtr("admin1@example.com"), - InvitedBy: &adminID, - Admin: boolPtr(false), - }, - wantErr: &invalidArgumentError{ - {name: "email", reason: "a user with this account already exists"}}, - }, - { - payload: kolide.InvitePayload{ - Email: stringPtr("nosuchuser@example.com"), - InvitedBy: &adminID, - Admin: boolPtr(false), - }, - }, - } - - for _, tt := range inviteTests { - t.Run("", func(t *testing.T) { - invite, err := svc.InviteNewUser(context.Background(), tt.payload) - if tt.wantErr != nil { - assert.Equal(t, tt.wantErr.Error(), err.Error()) - return - } else { - assert.Nil(t, err) - } - assert.Equal(t, *tt.payload.InvitedBy, invite.InvitedBy) - }) - } + return svc, ms, mailer +} + +var adminUser = &kolide.User{ + ID: 1, + Email: "admin@acme.co", + Username: "admin", + Name: "Administrator", +} + +var existingUser = &kolide.User{ + ID: 2, + Email: "user@acme.co", + Username: "user", +} + +var validInvite = &kolide.Invite{ + ID: 1, + Token: "abcd", +} + +var expiredInvite = &kolide.Invite{ + ID: 1, + Token: "abcd", + UpdateCreateTimestamps: kolide.UpdateCreateTimestamps{ + CreateTimestamp: kolide.CreateTimestamp{ + CreatedAt: time.Now().AddDate(-1, 0, 0), + }, + }, }