diff --git a/internal/adapter/adaptertest/sqlite.go b/internal/adapter/adaptertest/sqlite.go index 3db1087..cdf5cd7 100644 --- a/internal/adapter/adaptertest/sqlite.go +++ b/internal/adapter/adaptertest/sqlite.go @@ -12,6 +12,8 @@ import ( // NewBunDBSQLite initializes a new instance of *bun.DB, which is ready for use (all required migrations are applied). // Data is stored in memory (https://www.sqlite.org/inmemorydb.html). func NewBunDBSQLite(tb TestingTB) *bun.DB { + tb.Helper() + sqlDB, err := sql.Open(sqliteshim.ShimName, "file::memory:") require.NoError(tb, err) sqlDB.SetMaxOpenConns(1) diff --git a/internal/app/service_tribe.go b/internal/app/service_tribe.go index 2166da4..ba8400f 100644 --- a/internal/app/service_tribe.go +++ b/internal/app/service_tribe.go @@ -141,7 +141,7 @@ func (svc *TribeService) delete(ctx context.Context, serverKey string, tribes do break } - toDelete = append(toDelete, storedTribes.Delete(tribes)...) + toDelete = append(toDelete, storedTribes.Delete(serverKey, tribes)...) if err = listParams.SetIDGT(domain.NullInt{ Value: storedTribes[len(storedTribes)-1].ID(), diff --git a/internal/app/service_village.go b/internal/app/service_village.go index fe16a31..af7eb99 100644 --- a/internal/app/service_village.go +++ b/internal/app/service_village.go @@ -103,7 +103,7 @@ func (svc *VillageService) delete(ctx context.Context, serverKey string, village break } - toDelete = append(toDelete, storedVillages.Delete(villages)...) + toDelete = append(toDelete, storedVillages.Delete(serverKey, villages)...) if err = listParams.SetIDGT(domain.NullInt{ Value: storedVillages[len(storedVillages)-1].ID(), diff --git a/internal/domain/domaintest/building_info.go b/internal/domain/domaintest/building_info.go index ad47f4a..cfc8829 100644 --- a/internal/domain/domaintest/building_info.go +++ b/internal/domain/domaintest/building_info.go @@ -7,6 +7,7 @@ import ( ) func NewBuildingInfo(tb TestingTB) domain.BuildingInfo { + tb.Helper() buildingInfo, err := domain.NewBuildingInfo( domain.Building{MaxLevel: gofakeit.IntRange(1, 30)}, domain.Building{MaxLevel: gofakeit.IntRange(1, 30)}, diff --git a/internal/domain/domaintest/tribe.go b/internal/domain/domaintest/tribe.go index aac582c..5f33919 100644 --- a/internal/domain/domaintest/tribe.go +++ b/internal/domain/domaintest/tribe.go @@ -23,6 +23,7 @@ type TribeConfig struct { MostPointsAt time.Time MostVillages int MostVillagesAt time.Time + DeletedAt time.Time } func NewTribe(tb TestingTB, opts ...func(cfg *TribeConfig)) domain.Tribe { @@ -41,6 +42,7 @@ func NewTribe(tb TestingTB, opts ...func(cfg *TribeConfig)) domain.Tribe { MostPointsAt: now, MostVillages: gofakeit.IntRange(1, 10000), MostVillagesAt: now, + DeletedAt: time.Time{}, } for _, opt := range opts { @@ -67,7 +69,7 @@ func NewTribe(tb TestingTB, opts ...func(cfg *TribeConfig)) domain.Tribe { cfg.MostVillages, cfg.MostVillagesAt, now, - time.Time{}, + cfg.DeletedAt, ) require.NoError(tb, err) diff --git a/internal/domain/ennoblement_test.go b/internal/domain/ennoblement_test.go index a25ad93..0da8690 100644 --- a/internal/domain/ennoblement_test.go +++ b/internal/domain/ennoblement_test.go @@ -24,6 +24,7 @@ func TestNewCreateEnnoblementParams(t *testing.T) { res, err := domain.NewCreateEnnoblementParams(server.Key(), ennoblements) require.NoError(t, err) + assert.Len(t, res, len(ennoblements)) for i, e := range ennoblements { idx := slices.IndexFunc(res, func(params domain.CreateEnnoblementParams) bool { return params.Base().VillageID() == e.VillageID() && params.ServerKey() == server.Key() diff --git a/internal/domain/player_test.go b/internal/domain/player_test.go index 2a4da81..0bae158 100644 --- a/internal/domain/player_test.go +++ b/internal/domain/player_test.go @@ -45,7 +45,7 @@ func TestPlayers_Delete(t *testing.T) { cfg.ServerKey = server.Key() }), domaintest.NewPlayer(t, func(cfg *domaintest.PlayerConfig) { - cfg.ID = active[0].ID() + cfg.ID = active[2].ID() cfg.ServerKey = domaintest.RandServerKey() }), } diff --git a/internal/domain/tribe.go b/internal/domain/tribe.go index af98695..4d048f2 100644 --- a/internal/domain/tribe.go +++ b/internal/domain/tribe.go @@ -217,13 +217,18 @@ func (t Tribe) Base() BaseTribe { type Tribes []Tribe -// Delete finds all tribes that are not in the given slice with active tribes and returns their ids. -// Both slices must be sorted in ascending order by ID. -func (ts Tribes) Delete(active BaseTribes) []int { +// Delete finds all tribes with the given serverKey that are not in the given slice with active tribes +// and returns their ids. Both slices must be sorted in ascending order by ID +// + if vs contains tribes from different servers. they must be sorted in ascending order by server key. +func (ts Tribes) Delete(serverKey string, active BaseTribes) []int { //nolint:prealloc var toDelete []int for _, t := range ts { + if t.IsDeleted() || t.ServerKey() != serverKey { + continue + } + _, found := slices.BinarySearchFunc(active, t, func(a BaseTribe, b Tribe) int { return cmp.Compare(a.ID(), b.ID()) }) diff --git a/internal/domain/tribe_change_test.go b/internal/domain/tribe_change_test.go index 6b81023..69c6981 100644 --- a/internal/domain/tribe_change_test.go +++ b/internal/domain/tribe_change_test.go @@ -149,11 +149,6 @@ func TestNewCreateTribeChangeParamsFromPlayers(t *testing.T) { } storedPlayers := domain.Players{ - // server with random server key to verify that slice order matters - domaintest.NewPlayer(t, func(cfg *domaintest.PlayerConfig) { - cfg.ID = players[0].ID() - cfg.ServerKey = domaintest.RandServerKey() - }), // new tribe id domaintest.NewPlayer(t, func(cfg *domaintest.PlayerConfig) { cfg.ID = players[0].ID() @@ -165,6 +160,11 @@ func TestNewCreateTribeChangeParamsFromPlayers(t *testing.T) { cfg.ServerKey = server.Key() cfg.TribeID = players[1].TribeID() }), + // server with random server key to verify that slice order matters + domaintest.NewPlayer(t, func(cfg *domaintest.PlayerConfig) { + cfg.ID = players[2].ID() + cfg.ServerKey = domaintest.RandServerKey() + }), } expectedParams := []struct { @@ -176,7 +176,7 @@ func TestNewCreateTribeChangeParamsFromPlayers(t *testing.T) { { serverKey: server.Key(), playerID: players[0].ID(), - oldTribeID: storedPlayers[1].TribeID(), + oldTribeID: storedPlayers[0].TribeID(), newTribeID: players[0].TribeID(), }, { diff --git a/internal/domain/tribe_test.go b/internal/domain/tribe_test.go index 2634161..cd27cb3 100644 --- a/internal/domain/tribe_test.go +++ b/internal/domain/tribe_test.go @@ -23,9 +23,6 @@ func TestTribes_Delete(t *testing.T) { domaintest.NewBaseTribe(t), domaintest.NewBaseTribe(t), } - slices.SortFunc(active, func(a, b domain.BaseTribe) int { - return cmp.Compare(a.ID(), b.ID()) - }) tribes := domain.Tribes{ domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) { @@ -35,12 +32,28 @@ func TestTribes_Delete(t *testing.T) { domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) { cfg.ServerKey = server.Key() }), + domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) { + cfg.ServerKey = server.Key() + cfg.DeletedAt = time.Now() + }), domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) { cfg.ID = active[1].ID() cfg.ServerKey = server.Key() }), + domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) { + cfg.ID = active[2].ID() + cfg.ServerKey = domaintest.RandServerKey() + }), } + expectedIDs := []int{tribes[1].ID()} + + slices.Sort(expectedIDs) + + slices.SortFunc(active, func(a, b domain.BaseTribe) int { + return cmp.Compare(a.ID(), b.ID()) + }) + slices.SortFunc(tribes, func(a, b domain.Tribe) int { if res := cmp.Compare(a.ServerKey(), b.ServerKey()); res != 0 { return res @@ -48,7 +61,7 @@ func TestTribes_Delete(t *testing.T) { return cmp.Compare(a.ID(), b.ID()) }) - assert.Equal(t, expectedIDs, tribes.Delete(active)) + assert.Equal(t, expectedIDs, tribes.Delete(server.Key(), active)) } func TestNewCreateTribeParams(t *testing.T) { @@ -63,9 +76,6 @@ func TestNewCreateTribeParams(t *testing.T) { domaintest.NewBaseTribe(t), domaintest.NewBaseTribe(t), } - slices.SortFunc(tribes, func(a, b domain.BaseTribe) int { - return cmp.Compare(a.ID(), b.ID()) - }) storedTribes := domain.Tribes{ domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) { @@ -93,13 +103,6 @@ func TestNewCreateTribeParams(t *testing.T) { cfg.MostVillagesAt = now.Add(-time.Hour) }), } - storedTribesSorted := slices.Clone(storedTribes) - slices.SortFunc(storedTribesSorted, func(a, b domain.Tribe) int { - if res := cmp.Compare(a.ServerKey(), b.ServerKey()); res != 0 { - return res - } - return cmp.Compare(a.ID(), b.ID()) - }) expectedParams := []struct { base domain.BaseTribe @@ -143,7 +146,18 @@ func TestNewCreateTribeParams(t *testing.T) { }, } - res, err := domain.NewCreateTribeParams(server.Key(), tribes, storedTribesSorted) + slices.SortFunc(tribes, func(a, b domain.BaseTribe) int { + return cmp.Compare(a.ID(), b.ID()) + }) + + slices.SortFunc(storedTribes, func(a, b domain.Tribe) int { + if res := cmp.Compare(a.ServerKey(), b.ServerKey()); res != 0 { + return res + } + return cmp.Compare(a.ID(), b.ID()) + }) + + res, err := domain.NewCreateTribeParams(server.Key(), tribes, storedTribes) require.NoError(t, err) assert.Len(t, res, len(expectedParams)) for i, expected := range expectedParams { diff --git a/internal/domain/village.go b/internal/domain/village.go index b3de465..5e59c78 100644 --- a/internal/domain/village.go +++ b/internal/domain/village.go @@ -149,13 +149,19 @@ func (v Village) Base() BaseVillage { type Villages []Village -// Delete finds all villages that are not in the given slice with active villages and returns their ids. -// Both slices must be sorted in ascending order by ID. -func (vs Villages) Delete(active BaseVillages) []int { +// Delete finds all villages with the given serverKey that are not in the given slice with active villages +// and returns their ids. Both slices must be sorted in ascending order by ID +// + if vs contains villages from different servers. they must be sorted in ascending order by server key. +func (vs Villages) Delete(serverKey string, active BaseVillages) []int { + // villages are deleted now and then, there is no point in prereallocating these slices //nolint:prealloc var toDelete []int for _, v := range vs { + if v.ServerKey() != serverKey { + continue + } + _, found := slices.BinarySearchFunc(active, v, func(a BaseVillage, b Village) int { return cmp.Compare(a.ID(), b.ID()) }) diff --git a/internal/domain/village_test.go b/internal/domain/village_test.go index c095e5e..c780d4d 100644 --- a/internal/domain/village_test.go +++ b/internal/domain/village_test.go @@ -22,9 +22,6 @@ func TestVillages_Delete(t *testing.T) { domaintest.NewBaseVillage(t), domaintest.NewBaseVillage(t), } - slices.SortFunc(active, func(a, b domain.BaseVillage) int { - return cmp.Compare(a.ID(), b.ID()) - }) villages := domain.Villages{ domaintest.NewVillage(t, func(cfg *domaintest.VillageConfig) { @@ -38,8 +35,20 @@ func TestVillages_Delete(t *testing.T) { cfg.ID = active[1].ID() cfg.ServerKey = server.Key() }), + domaintest.NewVillage(t, func(cfg *domaintest.VillageConfig) { + cfg.ID = active[2].ID() + cfg.ServerKey = domaintest.RandServerKey() + }), } + expectedIDs := []int{villages[1].ID()} + + slices.Sort(expectedIDs) + + slices.SortFunc(active, func(a, b domain.BaseVillage) int { + return cmp.Compare(a.ID(), b.ID()) + }) + slices.SortFunc(villages, func(a, b domain.Village) int { if res := cmp.Compare(a.ServerKey(), b.ServerKey()); res != 0 { return res @@ -47,7 +56,7 @@ func TestVillages_Delete(t *testing.T) { return cmp.Compare(a.ID(), b.ID()) }) - assert.Equal(t, expectedIDs, villages.Delete(active)) + assert.Equal(t, expectedIDs, villages.Delete(server.Key(), active)) } func TestNewCreateVillageParams(t *testing.T) { @@ -66,6 +75,7 @@ func TestNewCreateVillageParams(t *testing.T) { res, err := domain.NewCreateVillageParams(server.Key(), villages) require.NoError(t, err) + assert.Len(t, res, len(villages)) for i, v := range villages { idx := slices.IndexFunc(res, func(params domain.CreateVillageParams) bool { return params.Base().ID() == v.ID() && params.ServerKey() == server.Key()