package adapter_test import ( "context" "testing" "time" "gitea.dwysokinski.me/twhelp/dcbot/internal/adapter" "gitea.dwysokinski.me/twhelp/dcbot/internal/domain" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/text/language" ) func TestGroup_Create(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } repo := adapter.NewGroupBun(newBunDB(t)) t.Run("OK", func(t *testing.T) { t.Parallel() params, err := domain.NewCreateGroupParams( "592292203234328587", "en", "en113", language.Polish.String(), "1234", "1235", true, true, ) require.NoError(t, err) group, err := repo.Create(context.Background(), params) assert.NoError(t, err) assert.NotZero(t, group.ID) assert.Equal(t, params.ServerID(), group.ServerID) assert.Equal(t, params.ServerKey(), group.ServerKey) assert.Equal(t, params.VersionCode(), group.VersionCode) assert.Equal(t, params.ChannelGains(), group.ChannelGains) assert.Equal(t, params.ChannelLosses(), group.ChannelLosses) assert.Equal(t, params.Barbarians(), group.Barbarians) assert.Equal(t, params.Internals(), group.Internals) assert.Equal(t, params.LanguageTag(), group.LanguageTag) assert.WithinDuration(t, time.Now(), group.CreatedAt, 1*time.Second) }) } func TestGroup_Update(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } db := newBunDB(t) fixture := loadFixtures(t, db) repo := adapter.NewGroupBun(db) group := fixture.group(t, "group-1-server-1") t.Run("OK", func(t *testing.T) { t.Parallel() params := domain.UpdateGroupParams{ ChannelGains: domain.NullString{ String: group.ChannelGains + "update", Valid: true, }, ChannelLosses: domain.NullString{ String: group.ChannelLosses + "update", Valid: true, }, LanguageTag: domain.NullString{ String: language.AmericanEnglish.String(), Valid: true, }, Barbarians: domain.NullBool{ Bool: !group.Barbarians, Valid: true, }, Internals: domain.NullBool{ Bool: !group.Internals, Valid: true, }, } updatedGroup, err := repo.Update(context.Background(), group.ID, params) assert.NoError(t, err) assert.Equal(t, params.ChannelGains.String, updatedGroup.ChannelGains) assert.Equal(t, params.ChannelLosses.String, updatedGroup.ChannelLosses) assert.Equal(t, params.LanguageTag.String, updatedGroup.LanguageTag) assert.Equal(t, params.Barbarians.Bool, updatedGroup.Barbarians) assert.Equal(t, params.Internals.Bool, updatedGroup.Internals) }) t.Run("ERR: nothing to update", func(t *testing.T) { t.Parallel() updatedGroup, err := repo.Update(context.Background(), "", domain.UpdateGroupParams{}) assert.ErrorIs(t, err, domain.ErrNothingToUpdate) assert.Zero(t, updatedGroup) }) t.Run("ERR: group not found", func(t *testing.T) { t.Parallel() tests := []struct { name string id string }{ { name: "ID - not UUID", id: "test", }, { name: "ID - random UUID", id: uuid.NewString(), }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() updatedGroup, err := repo.Update(context.Background(), tt.id, domain.UpdateGroupParams{ ChannelGains: domain.NullString{ String: "update", Valid: true, }, }) assert.ErrorIs(t, err, domain.GroupNotFoundError{ID: tt.id}) assert.Zero(t, updatedGroup) }) } }) } func TestGroup_AddMonitor(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } db := newBunDB(t) fixture := loadFixtures(t, db) repo := adapter.NewGroupBun(db) group := fixture.group(t, "group-1-server-1") t.Run("OK", func(t *testing.T) { t.Parallel() var tribeID int64 = 91283718 updatedGroup, err := repo.AddMonitor(context.Background(), group.ID, tribeID) assert.NoError(t, err) var found bool for _, m := range updatedGroup.Monitors { if m.GroupID == group.ID && m.TribeID == tribeID { found = true break } } assert.True(t, found) }) t.Run("ERR: group doesn't exist", func(t *testing.T) { t.Parallel() tests := []struct { name string id string }{ { name: "ID - not UUID", id: "test", }, { name: "ID - random UUID", id: uuid.NewString(), }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() updatedGroup, err := repo.AddMonitor(context.Background(), tt.id, 125) assert.ErrorIs(t, err, domain.GroupDoesNotExistError{ID: tt.id}) assert.Zero(t, updatedGroup) }) } }) t.Run("ERR: monitor already exists", func(t *testing.T) { t.Parallel() monitor := fixture.monitor(t, "monitor-2-group-1-server-1") updatedGroup, err := repo.AddMonitor(context.Background(), monitor.GroupID, monitor.TribeID) assert.ErrorIs(t, err, domain.MonitorAlreadyExistsError{ TribeID: monitor.TribeID, GroupID: monitor.GroupID, }) assert.Zero(t, updatedGroup) }) } func TestGroup_DeleteMonitors(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } db := newBunDB(t) fixture := loadFixtures(t, db) repo := adapter.NewGroupBun(db) t.Run("OK", func(t *testing.T) { t.Parallel() monitor := fixture.monitor(t, "monitor-1-group-1-server-1") updatedGroup, err := repo.DeleteMonitors(context.Background(), monitor.GroupID, monitor.ID) assert.NoError(t, err) var found bool for _, m := range updatedGroup.Monitors { if m.ID == monitor.ID { found = true break } } assert.False(t, found) }) t.Run("ERR: group not found", func(t *testing.T) { t.Parallel() monitor := fixture.monitor(t, "monitor-2-group-1-server-1") tests := []struct { name string id string }{ { name: "group ID - not UUID", id: "id", }, { name: "group ID - not UUID", id: uuid.NewString(), }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() updatedGroup, err := repo.DeleteMonitors(context.Background(), tt.id, monitor.ID) assert.ErrorIs(t, err, domain.GroupNotFoundError{ID: tt.id}) assert.Zero(t, updatedGroup) }) } }) t.Run("ERR: monitor not found", func(t *testing.T) { t.Parallel() monitor := fixture.monitor(t, "monitor-2-group-1-server-1") tests := []struct { name string monitorID string }{ { name: "monitor ID - not UUID", monitorID: "id", }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() updatedGroup, err := repo.DeleteMonitors(context.Background(), monitor.GroupID, tt.monitorID) assert.ErrorIs(t, err, domain.MonitorNotFoundError{ID: tt.monitorID}) assert.Zero(t, updatedGroup) }) } }) } func TestGroup_List(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } db := newBunDB(t) fixture := loadFixtures(t, db) repo := adapter.NewGroupBun(db) groups := fixture.groups(t) allGroups := make([]string, 0, len(groups)) for _, g := range groups { allGroups = append(allGroups, g.ID) } tests := []struct { name string params domain.ListGroupsParams expectedGroups []string }{ { name: "no params", params: domain.ListGroupsParams{}, expectedGroups: allGroups, }, { name: "ServerIDs=[server-1]", params: domain.ListGroupsParams{ ServerIDs: []string{"server-1"}, }, expectedGroups: []string{ "d56ad37f-2637-48ea-98f8-79627f3fcc96", "429b790e-7186-4106-b531-4cc4931ce2ba", }, }, { name: "ServerIDs=[server-1],EnabledNotifications=true", params: domain.ListGroupsParams{ ServerIDs: []string{"server-1"}, EnabledNotifications: domain.NullBool{ Bool: true, Valid: true, }, }, expectedGroups: []string{ "d56ad37f-2637-48ea-98f8-79627f3fcc96", "429b790e-7186-4106-b531-4cc4931ce2ba", }, }, { name: "ServerIDs=[server-2],EnabledNotifications=true", params: domain.ListGroupsParams{ ServerIDs: []string{"server-2"}, EnabledNotifications: domain.NullBool{ Bool: true, Valid: true, }, }, expectedGroups: []string{ "0be82203-4ca3-4b4c-a0c8-3a70099d88f7", }, }, { name: "ServerIDs=[server-2],EnabledNotifications=false", params: domain.ListGroupsParams{ ServerIDs: []string{"server-2"}, EnabledNotifications: domain.NullBool{ Bool: false, Valid: true, }, }, expectedGroups: []string{ "abeb6c8e-70b6-445c-989f-890cd2a1f87a", }, }, { name: "VersionCode=pl", params: domain.ListGroupsParams{ VersionCode: domain.NullString{ String: "pl", Valid: true, }, }, expectedGroups: []string{ "d56ad37f-2637-48ea-98f8-79627f3fcc96", "429b790e-7186-4106-b531-4cc4931ce2ba", "abeb6c8e-70b6-445c-989f-890cd2a1f87a", "0be82203-4ca3-4b4c-a0c8-3a70099d88f7", "982a9765-471c-43e8-9abb-1e4ee4f03738", }, }, { name: "VersionCode=pl,ServerKeys=[pl180]", params: domain.ListGroupsParams{ VersionCode: domain.NullString{ String: "pl", Valid: true, }, ServerKeys: []string{"pl180"}, }, expectedGroups: []string{ "982a9765-471c-43e8-9abb-1e4ee4f03738", }, }, { name: "CreatedAtLTE=2022-03-15T15:00:10.000Z", params: domain.ListGroupsParams{ CreatedAtLTE: time.Date(2022, time.March, 15, 15, 00, 10, 0, time.UTC), }, expectedGroups: []string{ "d56ad37f-2637-48ea-98f8-79627f3fcc96", }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() res, err := repo.List(context.Background(), tt.params) assert.NoError(t, err) assert.Len(t, res, len(tt.expectedGroups)) for _, id := range tt.expectedGroups { found := false for _, grp := range res { if grp.ID == id { found = true break } } assert.True(t, found, "group (id=%s) not found", id) } }) } } func TestGroup_Get(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } db := newBunDB(t) fixture := loadFixtures(t, db) repo := adapter.NewGroupBun(db) group := fixture.group(t, "group-1-server-1") t.Run("OK", func(t *testing.T) { t.Parallel() g, err := repo.Get(context.Background(), group.ID) assert.NoError(t, err) assert.Equal(t, group.ID, g.ID) }) t.Run("ERR: group not found (unknown ID)", func(t *testing.T) { t.Parallel() tests := []struct { name string id string }{ { name: "ID - not UUID", id: "test", }, { name: "ID - random UUID", id: uuid.NewString(), }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() g, err := repo.Get(context.Background(), tt.id) assert.ErrorIs(t, err, domain.GroupNotFoundError{ID: tt.id}) assert.Zero(t, g) }) } }) } func TestGroup_Delete(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } db := newBunDB(t) fixture := loadFixtures(t, db) groupRepo := adapter.NewGroupBun(db) group := fixture.group(t, "group-1-server-1") t.Run("OK", func(t *testing.T) { t.Parallel() beforeDelete, err := groupRepo.List(context.Background(), domain.ListGroupsParams{ ServerIDs: []string{group.ServerID}, }) assert.NoError(t, err) assert.NoError(t, groupRepo.Delete(context.Background(), group.ID)) afterDelete, err := groupRepo.List(context.Background(), domain.ListGroupsParams{ ServerIDs: []string{group.ServerID}, }) assert.NoError(t, err) assert.Len(t, afterDelete, len(beforeDelete)-1) }) t.Run("ERR: group not found", func(t *testing.T) { t.Parallel() tests := []struct { name string id string }{ { name: "ID - not UUID", id: "test", }, { name: "ID - random UUID", id: uuid.NewString(), }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() assert.ErrorIs(t, groupRepo.Delete(context.Background(), tt.id), domain.GroupNotFoundError{ID: tt.id}) }) } }) } func TestGroup_DeleteMany(t *testing.T) { t.Parallel() if testing.Short() { t.Skip("skipping long-running test") } db := newBunDB(t) fixture := loadFixtures(t, db) groupRepo := adapter.NewGroupBun(db) group1 := fixture.group(t, "group-1-server-1") group2 := fixture.group(t, "group-2-server-1") ids := []string{group1.ID, group2.ID} serverIDs := []string{group1.ServerID} t.Run("OK", func(t *testing.T) { t.Parallel() beforeDelete, err := groupRepo.List(context.Background(), domain.ListGroupsParams{ ServerIDs: serverIDs, }) assert.NoError(t, err) assert.NoError(t, groupRepo.DeleteMany(context.Background(), ids...)) afterDelete, err := groupRepo.List(context.Background(), domain.ListGroupsParams{ ServerIDs: serverIDs, }) assert.NoError(t, err) assert.Len(t, afterDelete, len(beforeDelete)-len(ids)) }) t.Run("OK: 0 ids", func(t *testing.T) { t.Parallel() assert.NoError(t, groupRepo.DeleteMany(context.Background())) }) }