diff --git a/internal/adapter/repository_bun_server.go b/internal/adapter/repository_bun_server.go index 68761a6..9812f9b 100644 --- a/internal/adapter/repository_bun_server.go +++ b/internal/adapter/repository_bun_server.go @@ -64,18 +64,25 @@ func (repo *ServerBunRepository) CreateOrUpdate(ctx context.Context, params ...d return nil } -func (repo *ServerBunRepository) List(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) { +func (repo *ServerBunRepository) List( + ctx context.Context, + params domain.ListServersParams, +) (domain.ListServersResult, error) { var servers bunmodel.Servers - if err := repo.baseListQuery(params).Model(&servers).Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, fmt.Errorf("couldn't select servers from the db: %w", err) + if err := repo.db.NewSelect(). + Model(&servers). + Apply(listServersParamsApplier{params: params}.apply). + Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) { + return domain.ListServersResult{}, fmt.Errorf("couldn't select servers from the db: %w", err) } - return servers.ToDomain() -} + converted, err := servers.ToDomain() + if err != nil { + return domain.ListServersResult{}, err + } -func (repo *ServerBunRepository) baseListQuery(params domain.ListServersParams) *bun.SelectQuery { - return repo.db.NewSelect().Apply(listServersParamsApplier{params: params}.apply) + return domain.NewListServersResult(separateListResultAndNext(converted, params.Limit())) } func (repo *ServerBunRepository) Update(ctx context.Context, key string, params domain.UpdateServerParams) error { @@ -184,10 +191,6 @@ func (a listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery { q = q.Where("server.key IN (?)", bun.In(keys)) } - if keyGT := a.params.KeyGT(); keyGT.Valid { - q = q.Where("server.key > ?", keyGT.Value) - } - if versionCodes := a.params.VersionCodes(); len(versionCodes) > 0 { q = q.Where("server.version_code IN (?)", bun.In(versionCodes)) } @@ -229,5 +232,29 @@ func (a listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery { } } - return q.Limit(a.params.Limit()).Offset(a.params.Offset()) + if !a.params.Cursor().IsZero() { + q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { + cursorKey := a.params.Cursor().Key() + cursorOpen := a.params.Cursor().Open() + + for _, s := range a.params.Sort() { + switch s { + case domain.ServerSortKeyASC: + q = q.Where("server.key >= ?", cursorKey) + case domain.ServerSortKeyDESC: + q = q.Where("server.key <= ?", cursorKey) + case domain.ServerSortOpenASC: + q = q.Where("server.open >= ?", cursorOpen) + case domain.ServerSortOpenDESC: + q = q.Where("server.open <= ?", cursorOpen) + default: + return q.Err(errors.New("unsupported sort value")) + } + } + + return q + }) + } + + return q.Limit(a.params.Limit() + 1) } diff --git a/internal/adapter/repository_ennoblement_test.go b/internal/adapter/repository_ennoblement_test.go index ae61993..ea17d62 100644 --- a/internal/adapter/repository_ennoblement_test.go +++ b/internal/adapter/repository_ennoblement_test.go @@ -89,10 +89,10 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit t.Run("OK", func(t *testing.T) { t.Parallel() - servers, err := repos.server.List(ctx, domain.NewListServersParams()) + listServersRes, err := repos.server.List(ctx, domain.NewListServersParams()) require.NoError(t, err) - require.NotEmpty(t, servers) - server := servers[0] + require.NotEmpty(t, listServersRes) + server := listServersRes.Servers()[0] ennoblementsToCreate := domain.BaseEnnoblements{ domaintest.NewBaseEnnoblement(t), diff --git a/internal/adapter/repository_player_test.go b/internal/adapter/repository_player_test.go index 18e141a..194f41e 100644 --- a/internal/adapter/repository_player_test.go +++ b/internal/adapter/repository_player_test.go @@ -64,10 +64,10 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Run("OK", func(t *testing.T) { t.Parallel() - servers, err := repos.server.List(ctx, domain.NewListServersParams()) + listServersRes, err := repos.server.List(ctx, domain.NewListServersParams()) require.NoError(t, err) - require.NotEmpty(t, servers) - server := servers[0] + require.NotEmpty(t, listServersRes) + server := listServersRes.Servers()[0] playersToCreate := domain.BasePlayers{ domaintest.NewBasePlayer(t), @@ -325,15 +325,15 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories listServersParams := domain.NewListServersParams() require.NoError(t, listServersParams.SetSpecial(domain.NullBool{Value: false, Valid: true})) - servers, listServersErr := repos.server.List(ctx, listServersParams) + listServersRes, listServersErr := repos.server.List(ctx, listServersParams) require.NoError(t, listServersErr) - require.NotEmpty(t, servers) + require.NotEmpty(t, listServersRes) t.Run("OK", func(t *testing.T) { t.Parallel() - serverKeys := make([]string, 0, len(servers)) - for _, s := range servers { + serverKeys := make([]string, 0, len(listServersRes.Servers())) + for _, s := range listServersRes.Servers() { serverKeys = append(serverKeys, s.Key()) } @@ -386,7 +386,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Run("OK: len(ids) == 0", func(t *testing.T) { t.Parallel() - require.NoError(t, repos.player.Delete(ctx, servers[0].Key())) + require.NoError(t, repos.player.Delete(ctx, listServersRes.Servers()[0].Key())) }) }) } diff --git a/internal/adapter/repository_server_test.go b/internal/adapter/repository_server_test.go index 166f94f..e1b1e5d 100644 --- a/internal/adapter/repository_server_test.go +++ b/internal/adapter/repository_server_test.go @@ -3,7 +3,6 @@ package adapter_test import ( "cmp" "context" - "fmt" "math" "slices" "testing" @@ -39,8 +38,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories listParams := domain.NewListServersParams() require.NoError(t, listParams.SetKeys(keys)) - servers, err := repos.server.List(ctx, listParams) + res, err := repos.server.List(ctx, listParams) require.NoError(t, err) + servers := res.Servers() require.Len(t, servers, len(params)) for i, p := range params { idx := slices.IndexFunc(servers, func(server domain.Server) bool { @@ -99,11 +99,6 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories repos := newRepos(t) - servers, listServersErr := repos.server.List(ctx, domain.NewListServersParams()) - require.NoError(t, listServersErr) - require.NotEmpty(t, servers) - randServer := servers[0] - snapshotsCreatedAtLT := time.Date( 2022, time.March, @@ -116,11 +111,10 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories ) tests := []struct { - name string - params func(t *testing.T) domain.ListServersParams - assertServers func(t *testing.T, params domain.ListServersParams, servers domain.Servers) - assertError func(t *testing.T, err error) - assertTotal func(t *testing.T, params domain.ListServersParams, total int) + name string + params func(t *testing.T) domain.ListServersParams + assertResult func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) + assertError func(t *testing.T, err error) }{ { name: "OK: default params", @@ -128,8 +122,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() return domain.NewListServersParams() }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() assert.NotEmpty(t, len(servers)) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { return cmp.Compare(a.Key(), b.Key()) @@ -139,10 +134,6 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { name: "OK: sort=[open ASC, key ASC]", @@ -152,8 +143,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories require.NoError(t, params.SetSort([]domain.ServerSort{domain.ServerSortOpenASC, domain.ServerSortKeyASC})) return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() assert.NotEmpty(t, len(servers)) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { if a.Open() && !b.Open() { @@ -171,10 +163,6 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { name: "OK: sort=[open DESC, key DESC]", @@ -184,8 +172,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories require.NoError(t, params.SetSort([]domain.ServerSort{domain.ServerSortOpenDESC, domain.ServerSortKeyDESC})) return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() assert.NotEmpty(t, len(servers)) assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { if a.Open() && !b.Open() { @@ -203,22 +192,26 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { - name: fmt.Sprintf("OK: keys=[%s]", randServer.Key()), + name: "OK: keys", params: func(t *testing.T) domain.ListServersParams { t.Helper() + params := domain.NewListServersParams() - require.NoError(t, params.SetKeys([]string{randServer.Key()})) + + res, err := repos.server.List(ctx, params) + require.NoError(t, err) + require.NotEmpty(t, len(res.Servers())) + + require.NoError(t, params.SetKeys([]string{res.Servers()[0].Key()})) + return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() keys := params.Keys() assert.Len(t, servers, len(keys)) @@ -232,22 +225,26 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.Equal(t, len(params.Keys()), total) //nolint:testifylint - }, }, { - name: fmt.Sprintf("OK: versionCodes=[%s]", randServer.VersionCode()), + name: "OK: versionCodes", params: func(t *testing.T) domain.ListServersParams { t.Helper() + params := domain.NewListServersParams() - require.NoError(t, params.SetVersionCodes([]string{randServer.VersionCode()})) + + res, err := repos.server.List(ctx, params) + require.NoError(t, err) + require.NotEmpty(t, len(res.Servers())) + + require.NoError(t, params.SetVersionCodes([]string{res.Servers()[0].VersionCode()})) + return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() versionCodes := params.VersionCodes() assert.NotEmpty(t, servers) @@ -261,37 +258,6 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.Equal(t, len(params.Keys()), total) //nolint:testifylint - }, - }, - { - name: fmt.Sprintf("OK: keyGT=%s", randServer.Key()), - params: func(t *testing.T) domain.ListServersParams { - t.Helper() - params := domain.NewListServersParams() - require.NoError(t, params.SetKeyGT(domain.NullString{ - Value: randServer.Key(), - Valid: true, - })) - return params - }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { - t.Helper() - assert.NotEmpty(t, len(servers)) - for _, s := range servers { - assert.Greater(t, s.Key(), params.KeyGT().Value, s.Key()) - } - }, - assertError: func(t *testing.T, err error) { - t.Helper() - require.NoError(t, err) - }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { name: "OK: special=true", @@ -304,8 +270,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories })) return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() assert.NotEmpty(t, len(servers)) for _, s := range servers { assert.True(t, s.Special(), s.Key()) @@ -315,10 +282,6 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { name: "OK: open=false", @@ -331,8 +294,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories })) return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() assert.NotEmpty(t, len(servers)) for _, s := range servers { assert.False(t, s.Open(), s.Key()) @@ -342,10 +306,6 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { name: "OK: playerSnapshotsCreatedAtLt=" + snapshotsCreatedAtLT.Format(time.RFC3339), @@ -358,8 +318,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories })) return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() assert.NotEmpty(t, len(servers)) for _, s := range servers { assert.True(t, s.PlayerSnapshotsCreatedAt().Before(snapshotsCreatedAtLT)) @@ -369,10 +330,6 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { name: "OK: tribeSnapshotsCreatedAtLt=" + snapshotsCreatedAtLT.Format(time.RFC3339), @@ -385,8 +342,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories })) return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() + servers := res.Servers() assert.NotEmpty(t, len(servers)) for _, s := range servers { assert.True(t, s.TribeSnapshotsCreatedAt().Before(snapshotsCreatedAtLT)) @@ -396,31 +354,108 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { - t.Helper() - assert.NotEmpty(t, total) - }, }, { - name: "OK: offset=1 limit=2", + name: "OK: cursor sort=[open ASC, key ASC]", params: func(t *testing.T) domain.ListServersParams { t.Helper() + params := domain.NewListServersParams() - require.NoError(t, params.SetOffset(1)) - require.NoError(t, params.SetLimit(2)) + require.NoError(t, params.SetSort([]domain.ServerSort{domain.ServerSortOpenASC, domain.ServerSortKeyASC})) + + res, err := repos.server.List(ctx, params) + require.NoError(t, err) + require.Greater(t, len(res.Servers()), 2) + + require.NoError(t, params.SetCursor(domaintest.NewServerCursor(t, func(cfg *domaintest.ServerCursorConfig) { + cfg.Key = res.Servers()[1].Key() + cfg.Open = res.Servers()[1].Open() + }))) + return params }, - assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) { + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { t.Helper() - assert.Len(t, servers, params.Limit()) + servers := res.Servers() + assert.NotEmpty(t, len(servers)) + assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { + if a.Open() && !b.Open() { + return 1 + } + + if !a.Open() && b.Open() { + return -1 + } + + return cmp.Compare(a.Key(), b.Key()) + })) + for _, s := range res.Servers() { + assert.GreaterOrEqual(t, s.Key(), params.Cursor().Key(), s.Key()) + } }, assertError: func(t *testing.T, err error) { t.Helper() require.NoError(t, err) }, - assertTotal: func(t *testing.T, params domain.ListServersParams, total int) { + }, + { + name: "OK: cursor sort=[open DESC, key DESC]", + params: func(t *testing.T) domain.ListServersParams { t.Helper() - assert.NotEmpty(t, total) + + params := domain.NewListServersParams() + require.NoError(t, params.SetSort([]domain.ServerSort{domain.ServerSortOpenDESC, domain.ServerSortKeyDESC})) + + res, err := repos.server.List(ctx, params) + require.NoError(t, err) + require.Greater(t, len(res.Servers()), 2) + + require.NoError(t, params.SetCursor(domaintest.NewServerCursor(t, func(cfg *domaintest.ServerCursorConfig) { + cfg.Key = res.Servers()[1].Key() + cfg.Open = res.Servers()[1].Open() + }))) + + return params + }, + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { + t.Helper() + servers := res.Servers() + assert.NotEmpty(t, len(servers)) + assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int { + if a.Open() && !b.Open() { + return -1 + } + + if !a.Open() && b.Open() { + return 1 + } + + return cmp.Compare(a.Key(), b.Key()) * -1 + })) + for _, s := range res.Servers() { + assert.LessOrEqual(t, s.Key(), params.Cursor().Key(), s.Key()) + } + }, + assertError: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err) + }, + }, + { + name: "OK: limit=2", + params: func(t *testing.T) domain.ListServersParams { + t.Helper() + params := domain.NewListServersParams() + require.NoError(t, params.SetLimit(2)) + return params + }, + assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) { + t.Helper() + assert.Len(t, res.Servers(), params.Limit()) + }, + assertError: func(t *testing.T, err error) { + t.Helper() + require.NoError(t, err) }, }, } @@ -435,7 +470,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories res, err := repos.server.List(ctx, params) tt.assertError(t, err) - tt.assertServers(t, params, res) + tt.assertResult(t, params, res) }) } }) @@ -454,6 +489,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories serversBeforeUpdate, err := repos.server.List(ctx, listServersBeforeUpdateParams) require.NoError(t, err) require.NotEmpty(t, serversBeforeUpdate) + serverBeforeUpdate := serversBeforeUpdate.Servers()[0] var updateParams domain.UpdateServerParams require.NoError(t, updateParams.SetConfig(domain.NullServerConfig{ @@ -517,58 +553,59 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories Valid: true, })) - require.NoError(t, repos.server.Update(ctx, serversBeforeUpdate[0].Key(), updateParams)) + require.NoError(t, repos.server.Update(ctx, serverBeforeUpdate.Key(), updateParams)) listServersAfterUpdateParams := domain.NewListServersParams() require.NoError(t, listServersAfterUpdateParams.SetLimit(1)) - require.NoError(t, listServersAfterUpdateParams.SetKeys([]string{serversBeforeUpdate[0].Key()})) + require.NoError(t, listServersAfterUpdateParams.SetKeys([]string{serverBeforeUpdate.Key()})) serversAfterUpdate, err := repos.server.List(ctx, listServersAfterUpdateParams) require.NoError(t, err) require.NotEmpty(t, serversAfterUpdate) - assert.Equal(t, updateParams.Config().Value, serversAfterUpdate[0].Config()) - assert.Equal(t, updateParams.UnitInfo().Value, serversAfterUpdate[0].UnitInfo()) - assert.Equal(t, updateParams.BuildingInfo().Value, serversAfterUpdate[0].BuildingInfo()) - assert.Equal(t, updateParams.NumTribes().Value, serversAfterUpdate[0].NumTribes()) + serverAfterUpdate := serversAfterUpdate.Servers()[0] + assert.Equal(t, updateParams.Config().Value, serverAfterUpdate.Config()) + assert.Equal(t, updateParams.UnitInfo().Value, serverAfterUpdate.UnitInfo()) + assert.Equal(t, updateParams.BuildingInfo().Value, serverAfterUpdate.BuildingInfo()) + assert.Equal(t, updateParams.NumTribes().Value, serverAfterUpdate.NumTribes()) assert.WithinDuration( t, updateParams.TribeDataSyncedAt().Value, - serversAfterUpdate[0].TribeDataSyncedAt(), + serverAfterUpdate.TribeDataSyncedAt(), time.Minute, ) - assert.Equal(t, updateParams.NumPlayers().Value, serversAfterUpdate[0].NumPlayers()) + assert.Equal(t, updateParams.NumPlayers().Value, serverAfterUpdate.NumPlayers()) assert.WithinDuration( t, updateParams.PlayerDataSyncedAt().Value, - serversAfterUpdate[0].PlayerDataSyncedAt(), + serverAfterUpdate.PlayerDataSyncedAt(), time.Minute, ) - assert.Equal(t, updateParams.NumVillages().Value, serversAfterUpdate[0].NumVillages()) - assert.Equal(t, updateParams.NumPlayerVillages().Value, serversAfterUpdate[0].NumPlayerVillages()) - assert.Equal(t, updateParams.NumBarbarianVillages().Value, serversAfterUpdate[0].NumBarbarianVillages()) - assert.Equal(t, updateParams.NumBonusVillages().Value, serversAfterUpdate[0].NumBonusVillages()) + assert.Equal(t, updateParams.NumVillages().Value, serverAfterUpdate.NumVillages()) + assert.Equal(t, updateParams.NumPlayerVillages().Value, serverAfterUpdate.NumPlayerVillages()) + assert.Equal(t, updateParams.NumBarbarianVillages().Value, serverAfterUpdate.NumBarbarianVillages()) + assert.Equal(t, updateParams.NumBonusVillages().Value, serverAfterUpdate.NumBonusVillages()) assert.WithinDuration( t, updateParams.VillageDataSyncedAt().Value, - serversAfterUpdate[0].VillageDataSyncedAt(), + serverAfterUpdate.VillageDataSyncedAt(), time.Minute, ) assert.WithinDuration( t, updateParams.EnnoblementDataSyncedAt().Value, - serversAfterUpdate[0].EnnoblementDataSyncedAt(), + serverAfterUpdate.EnnoblementDataSyncedAt(), time.Minute, ) assert.WithinDuration( t, updateParams.TribeSnapshotsCreatedAt().Value, - serversAfterUpdate[0].TribeSnapshotsCreatedAt(), + serverAfterUpdate.TribeSnapshotsCreatedAt(), time.Minute, ) assert.WithinDuration( t, updateParams.PlayerSnapshotsCreatedAt().Value, - serversAfterUpdate[0].PlayerSnapshotsCreatedAt(), + serverAfterUpdate.PlayerSnapshotsCreatedAt(), time.Minute, ) }) diff --git a/internal/adapter/repository_test.go b/internal/adapter/repository_test.go index f889a02..fb5a589 100644 --- a/internal/adapter/repository_test.go +++ b/internal/adapter/repository_test.go @@ -17,7 +17,7 @@ type versionRepository interface { type serverRepository interface { CreateOrUpdate(ctx context.Context, params ...domain.CreateServerParams) error - List(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) + List(ctx context.Context, params domain.ListServersParams) (domain.ListServersResult, error) Update(ctx context.Context, key string, params domain.UpdateServerParams) error } diff --git a/internal/adapter/repository_tribe_test.go b/internal/adapter/repository_tribe_test.go index 0494765..13210e7 100644 --- a/internal/adapter/repository_tribe_test.go +++ b/internal/adapter/repository_tribe_test.go @@ -63,10 +63,10 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) t.Run("OK", func(t *testing.T) { t.Parallel() - servers, err := repos.server.List(ctx, domain.NewListServersParams()) + listServersRes, err := repos.server.List(ctx, domain.NewListServersParams()) require.NoError(t, err) - require.NotEmpty(t, servers) - server := servers[0] + require.NotEmpty(t, listServersRes) + server := listServersRes.Servers()[0] tribesToCreate := domain.BaseTribes{ domaintest.NewBaseTribe(t), @@ -114,9 +114,9 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) Valid: true, })) - servers, listServersErr := repos.server.List(ctx, listServersParams) + listServersRes, listServersErr := repos.server.List(ctx, listServersParams) require.NoError(t, listServersErr) - require.GreaterOrEqual(t, len(servers), 2) + require.GreaterOrEqual(t, len(listServersRes.Servers()), 2) tests := []struct { name string @@ -125,12 +125,12 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) }{ { name: "numPlayerVillages=0", - serverKey: servers[0].Key(), + serverKey: listServersRes.Servers()[0].Key(), numPlayerVillages: 0, }, { name: "numPlayerVillages=35000", - serverKey: servers[1].Key(), + serverKey: listServersRes.Servers()[1].Key(), numPlayerVillages: 35000, }, } @@ -390,15 +390,15 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) listServersParams := domain.NewListServersParams() require.NoError(t, listServersParams.SetSpecial(domain.NullBool{Value: false, Valid: true})) - servers, listServersErr := repos.server.List(ctx, listServersParams) + listServersRes, listServersErr := repos.server.List(ctx, listServersParams) require.NoError(t, listServersErr) - require.NotEmpty(t, servers) + require.NotEmpty(t, listServersRes) t.Run("OK", func(t *testing.T) { t.Parallel() - serverKeys := make([]string, 0, len(servers)) - for _, s := range servers { + serverKeys := make([]string, 0, len(listServersRes.Servers())) + for _, s := range listServersRes.Servers() { serverKeys = append(serverKeys, s.Key()) } @@ -450,7 +450,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) t.Run("OK: len(ids) == 0", func(t *testing.T) { t.Parallel() - require.NoError(t, repos.tribe.Delete(ctx, servers[0].Key())) + require.NoError(t, repos.tribe.Delete(ctx, listServersRes.Servers()[0].Key())) }) }) } diff --git a/internal/adapter/repository_village_test.go b/internal/adapter/repository_village_test.go index 24ac104..07d1e29 100644 --- a/internal/adapter/repository_village_test.go +++ b/internal/adapter/repository_village_test.go @@ -56,10 +56,10 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie t.Run("OK", func(t *testing.T) { t.Parallel() - servers, err := repos.server.List(ctx, domain.NewListServersParams()) + listServersRes, err := repos.server.List(ctx, domain.NewListServersParams()) require.NoError(t, err) - require.NotEmpty(t, servers) - server := servers[0] + require.NotEmpty(t, listServersRes) + server := listServersRes.Servers()[0] villagesToCreate := domain.BaseVillages{ domaintest.NewBaseVillage(t), @@ -263,15 +263,15 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie listServersParams := domain.NewListServersParams() require.NoError(t, listServersParams.SetSpecial(domain.NullBool{Value: false, Valid: true})) - servers, listServersErr := repos.server.List(ctx, listServersParams) + listServersRes, listServersErr := repos.server.List(ctx, listServersParams) require.NoError(t, listServersErr) - require.NotEmpty(t, servers) + require.NotEmpty(t, listServersRes) t.Run("OK", func(t *testing.T) { t.Parallel() - serverKeys := make([]string, 0, len(servers)) - for _, s := range servers { + serverKeys := make([]string, 0, len(listServersRes.Servers())) + for _, s := range listServersRes.Servers() { serverKeys = append(serverKeys, s.Key()) } @@ -311,7 +311,7 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie t.Run("OK: len(ids) == 0", func(t *testing.T) { t.Parallel() - require.NoError(t, repos.village.Delete(ctx, servers[0].Key())) + require.NoError(t, repos.village.Delete(ctx, listServersRes.Servers()[0].Key())) }) }) } diff --git a/internal/app/service_server.go b/internal/app/service_server.go index 1b9ed9c..5e18f5e 100644 --- a/internal/app/service_server.go +++ b/internal/app/service_server.go @@ -10,7 +10,7 @@ import ( type ServerRepository interface { CreateOrUpdate(ctx context.Context, params ...domain.CreateServerParams) error - List(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) + List(ctx context.Context, params domain.ListServersParams) (domain.ListServersResult, error) Update(ctx context.Context, key string, params domain.UpdateServerParams) error } @@ -101,11 +101,8 @@ func (svc *ServerService) ListAllOpen(ctx context.Context, versionCode string) ( } // ListAll retrieves all servers from the database based on the given params in an optimal way. -// You can't specify a custom limit/offset/sort/keyGT for this operation. +// You can't specify a custom limit/cursor/sort for this operation. func (svc *ServerService) ListAll(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) { - if err := params.SetOffset(0); err != nil { - return nil, err - } if err := params.SetLimit(domain.ServerListMaxLimit); err != nil { return nil, err } @@ -116,21 +113,18 @@ func (svc *ServerService) ListAll(ctx context.Context, params domain.ListServers var servers domain.Servers for { - ss, err := svc.repo.List(ctx, params) + res, err := svc.repo.List(ctx, params) if err != nil { return nil, err } - if len(ss) == 0 { + servers = append(servers, res.Servers()...) + + if res.Next().IsZero() { return servers, nil } - servers = append(servers, ss...) - - if err = params.SetKeyGT(domain.NullString{ - Value: ss[len(ss)-1].Key(), - Valid: true, - }); err != nil { + if err = params.SetCursor(res.Next()); err != nil { return nil, err } } diff --git a/internal/domain/domaintest/server.go b/internal/domain/domaintest/server.go index f4d0442..b1c130c 100644 --- a/internal/domain/domaintest/server.go +++ b/internal/domain/domaintest/server.go @@ -13,6 +13,29 @@ func RandServerKey() string { return gofakeit.LetterN(5) } +type ServerCursorConfig struct { + Key string + Open bool +} + +func NewServerCursor(tb TestingTB, opts ...func(cfg *ServerCursorConfig)) domain.ServerCursor { + tb.Helper() + + cfg := &ServerCursorConfig{ + Key: RandServerKey(), + Open: gofakeit.Bool(), + } + + for _, opt := range opts { + opt(cfg) + } + + vc, err := domain.NewServerCursor(cfg.Key, cfg.Open) + require.NoError(tb, err) + + return vc +} + type ServerConfig struct { Key string VersionCode string diff --git a/internal/domain/server.go b/internal/domain/server.go index 8021437..c633802 100644 --- a/internal/domain/server.go +++ b/internal/domain/server.go @@ -5,6 +5,7 @@ import ( "math" "net/url" "slices" + "strconv" "time" ) @@ -200,6 +201,10 @@ func (s Server) EnnoblementDataSyncedAt() time.Time { return s.ennoblementDataSyncedAt } +func (s Server) IsZero() bool { + return s == Server{} +} + func (s Server) Base() BaseServer { return BaseServer{ key: s.key, @@ -464,17 +469,77 @@ const ( ServerSortOpenDESC ) +type ServerCursor struct { + key string + open bool +} + +const serverCursorModelName = "ServerCursor" + +func NewServerCursor(key string, open bool) (ServerCursor, error) { + if err := validateServerKey(key); err != nil { + return ServerCursor{}, ValidationError{ + Model: serverCursorModelName, + Field: "key", + Err: err, + } + } + + return ServerCursor{key: key, open: open}, nil +} + +func decodeServerCursor(encoded string) (ServerCursor, error) { + m, err := decodeCursor(encoded) + if err != nil { + return ServerCursor{}, err + } + + open, err := strconv.ParseBool(m["open"]) + if err != nil { + return ServerCursor{}, ErrInvalidCursor + } + + vc, err := NewServerCursor(m["key"], open) + if err != nil { + return ServerCursor{}, ErrInvalidCursor + } + + return vc, nil +} + +func (sc ServerCursor) Key() string { + return sc.key +} + +func (sc ServerCursor) Open() bool { + return sc.open +} + +func (sc ServerCursor) IsZero() bool { + return sc == ServerCursor{} +} + +func (sc ServerCursor) Encode() string { + if sc.IsZero() { + return "" + } + + return encodeCursor([][2]string{ + {"key", sc.key}, + {"open", strconv.FormatBool(sc.open)}, + }) +} + type ListServersParams struct { keys []string - keyGT NullString versionCodes []string open NullBool special NullBool tribeSnapshotsCreatedAtLT NullTime playerSnapshotsCreatedAtLT NullTime sort []ServerSort + cursor ServerCursor limit int - offset int } const ( @@ -502,15 +567,6 @@ func (params *ListServersParams) SetKeys(keys []string) error { return nil } -func (params *ListServersParams) KeyGT() NullString { - return params.keyGT -} - -func (params *ListServersParams) SetKeyGT(keyGT NullString) error { - params.keyGT = keyGT - return nil -} - func (params *ListServersParams) VersionCodes() []string { return params.versionCodes } @@ -579,6 +635,30 @@ func (params *ListServersParams) SetSort(sort []ServerSort) error { return nil } +func (params *ListServersParams) Cursor() ServerCursor { + return params.cursor +} + +func (params *ListServersParams) SetCursor(cursor ServerCursor) error { + params.cursor = cursor + return nil +} + +func (params *ListServersParams) SetEncodedCursor(encoded string) error { + decoded, err := decodeServerCursor(encoded) + if err != nil { + return ValidationError{ + Model: listServersParamsModelName, + Field: "cursor", + Err: err, + } + } + + params.cursor = decoded + + return nil +} + func (params *ListServersParams) Limit() int { return params.limit } @@ -597,22 +677,55 @@ func (params *ListServersParams) SetLimit(limit int) error { return nil } -func (params *ListServersParams) Offset() int { - return params.offset +type ListServersResult struct { + servers Servers + self ServerCursor + next ServerCursor } -func (params *ListServersParams) SetOffset(offset int) error { - if err := validateIntInRange(offset, 0, math.MaxInt); err != nil { - return ValidationError{ - Model: listServersParamsModelName, - Field: "offset", - Err: err, +const listServersResultModelName = "ListServersResult" + +func NewListServersResult(servers Servers, next Server) (ListServersResult, error) { + var err error + res := ListServersResult{ + servers: servers, + } + + if len(servers) > 0 { + res.self, err = NewServerCursor(servers[0].Key(), servers[0].Open()) + if err != nil { + return ListServersResult{}, ValidationError{ + Model: listServersResultModelName, + Field: "self", + Err: err, + } } } - params.offset = offset + if !next.IsZero() { + res.next, err = NewServerCursor(next.Key(), next.Open()) + if err != nil { + return ListServersResult{}, ValidationError{ + Model: listServersResultModelName, + Field: "next", + Err: err, + } + } + } - return nil + return res, nil +} + +func (res ListServersResult) Servers() Servers { + return res.servers +} + +func (res ListServersResult) Self() ServerCursor { + return res.self +} + +func (res ListServersResult) Next() ServerCursor { + return res.next } type ServerNotFoundError struct { diff --git a/internal/domain/server_test.go b/internal/domain/server_test.go index 19c5f80..82ed1d3 100644 --- a/internal/domain/server_test.go +++ b/internal/domain/server_test.go @@ -187,6 +187,65 @@ func TestUpdateServerParams_SetNumTribes(t *testing.T) { } } +func TestNewServerCursor(t *testing.T) { + t.Parallel() + + validServerCursor := domaintest.NewServerCursor(t) + + type args struct { + key string + open bool + } + + type test struct { + name string + args args + expectedErr error + } + + tests := []test{ + { + name: "OK", + args: args{ + key: validServerCursor.Key(), + open: validServerCursor.Open(), + }, + expectedErr: nil, + }, + } + + for _, serverKeyTest := range newServerKeyValidationTests() { + tests = append(tests, test{ + name: serverKeyTest.name, + args: args{ + key: serverKeyTest.key, + }, + expectedErr: domain.ValidationError{ + Model: "ServerCursor", + Field: "key", + Err: serverKeyTest.expectedErr, + }, + }) + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + sc, err := domain.NewServerCursor(tt.args.key, tt.args.open) + require.ErrorIs(t, err, tt.expectedErr) + if tt.expectedErr != nil { + return + } + assert.Equal(t, tt.args.key, sc.Key()) + assert.Equal(t, tt.args.open, sc.Open()) + assert.NotEmpty(t, sc.Encode()) + }) + } +} + func TestListServersParams_SetSort(t *testing.T) { t.Parallel() @@ -260,6 +319,90 @@ func TestListServersParams_SetSort(t *testing.T) { } } +func TestListServersParams_SetEncodedCursor(t *testing.T) { + t.Parallel() + + validCursor := domaintest.NewServerCursor(t) + + type args struct { + cursor string + } + + tests := []struct { + name string + args args + expectedCursor domain.ServerCursor + expectedErr error + }{ + { + name: "OK", + args: args{ + cursor: validCursor.Encode(), + }, + expectedCursor: validCursor, + }, + { + name: "ERR: len(cursor) < 1", + args: args{ + cursor: "", + }, + expectedErr: domain.ValidationError{ + Model: "ListServersParams", + Field: "cursor", + Err: domain.LenOutOfRangeError{ + Min: 1, + Max: 1000, + Current: 0, + }, + }, + }, + { + name: "ERR: len(cursor) > 1000", + args: args{ + cursor: gofakeit.LetterN(1001), + }, + expectedErr: domain.ValidationError{ + Model: "ListServersParams", + Field: "cursor", + Err: domain.LenOutOfRangeError{ + Min: 1, + Max: 1000, + Current: 1001, + }, + }, + }, + { + name: "ERR: malformed base64", + args: args{ + cursor: "112345", + }, + expectedErr: domain.ValidationError{ + Model: "ListServersParams", + Field: "cursor", + Err: domain.ErrInvalidCursor, + }, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + params := domain.NewListServersParams() + + require.ErrorIs(t, params.SetEncodedCursor(tt.args.cursor), tt.expectedErr) + if tt.expectedErr != nil { + return + } + assert.Equal(t, tt.expectedCursor.Key(), params.Cursor().Key()) + assert.Equal(t, tt.expectedCursor.Open(), params.Cursor().Open()) + assert.Equal(t, tt.args.cursor, params.Cursor().Encode()) + }) + } +} + func TestListServersParams_SetLimit(t *testing.T) { t.Parallel() @@ -325,55 +468,48 @@ func TestListServersParams_SetLimit(t *testing.T) { } } -func TestListServersParams_SetOffset(t *testing.T) { +func TestNewListServersResult(t *testing.T) { t.Parallel() - type args struct { - offset int + servers := domain.Servers{ + domaintest.NewServer(t), + domaintest.NewServer(t), + domaintest.NewServer(t), } + next := domaintest.NewServer(t) - tests := []struct { - name string - args args - expectedErr error - }{ - { - name: "OK", - args: args{ - offset: 100, - }, - }, - { - name: "ERR: offset < 0", - args: args{ - offset: -1, - }, - expectedErr: domain.ValidationError{ - Model: "ListServersParams", - Field: "offset", - Err: domain.MinGreaterEqualError{ - Min: 0, - Current: -1, - }, - }, - }, - } + t.Run("OK: with next", func(t *testing.T) { + t.Parallel() - for _, tt := range tests { - tt := tt + res, err := domain.NewListServersResult(servers, next) + require.NoError(t, err) + assert.Equal(t, servers, res.Servers()) + assert.Equal(t, servers[0].Key(), res.Self().Key()) + assert.Equal(t, servers[0].Open(), res.Self().Open()) + assert.Equal(t, next.Key(), res.Next().Key()) + assert.Equal(t, next.Open(), res.Next().Open()) + }) - t.Run(tt.name, func(t *testing.T) { - t.Parallel() + t.Run("OK: without next", func(t *testing.T) { + t.Parallel() - params := domain.NewListServersParams() + res, err := domain.NewListServersResult(servers, domain.Server{}) + require.NoError(t, err) + assert.Equal(t, servers, res.Servers()) + assert.Equal(t, servers[0].Key(), res.Self().Key()) + assert.Equal(t, servers[0].Open(), res.Self().Open()) + assert.True(t, res.Next().IsZero()) + }) - require.ErrorIs(t, params.SetOffset(tt.args.offset), tt.expectedErr) - if tt.expectedErr != nil { - return - } - assert.Equal(t, tt.args.offset, params.Offset()) - }) - } + t.Run("OK: 0 versions", func(t *testing.T) { + t.Parallel() + + res, err := domain.NewListServersResult(nil, domain.Server{}) + require.NoError(t, err) + assert.Zero(t, res.Servers()) + assert.True(t, res.Self().IsZero()) + assert.True(t, res.Next().IsZero()) + }) } type serverKeyValidationTest struct { diff --git a/internal/domain/utils.go b/internal/domain/utils.go index 32a0f1a..7e59d31 100644 --- a/internal/domain/utils.go +++ b/internal/domain/utils.go @@ -23,33 +23,42 @@ const ( cursorKeyValueSeparator = "=" ) -func encodeCursor(m map[string]string) string { - if len(m) == 0 { +func encodeCursor(s [][2]string) string { + if len(s) == 0 { return "" } - n := len(cursorSeparator) * (len(m) - 1) - for k, v := range m { - n += len(k) + len(v) + len(cursorKeyValueSeparator) + n := len(cursorSeparator) * (len(s) - 1) + for _, el := range s { + n += len(el[0]) + len(el[1]) + len(cursorKeyValueSeparator) } var b bytes.Buffer b.Grow(n) - for k, v := range m { + for _, el := range s { if b.Len() > 0 { _, _ = b.WriteString(cursorSeparator) } - _, _ = b.WriteString(k) + _, _ = b.WriteString(el[0]) _, _ = b.WriteString(cursorKeyValueSeparator) - _, _ = b.WriteString(v) + _, _ = b.WriteString(el[1]) } return base64.StdEncoding.EncodeToString(b.Bytes()) } +const ( + encodedCursorMinLength = 1 + encodedCursorMaxLength = 1000 +) + func decodeCursor(s string) (map[string]string, error) { + if err := validateStringLen(s, encodedCursorMinLength, encodedCursorMaxLength); err != nil { + return nil, err + } + decodedBytes, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, ErrInvalidCursor diff --git a/internal/domain/version.go b/internal/domain/version.go index f7d244e..c27c24d 100644 --- a/internal/domain/version.go +++ b/internal/domain/version.go @@ -125,16 +125,7 @@ func NewVersionCursor(code string) (VersionCursor, error) { }, nil } -const ( - encodedVersionCursorMinLength = 1 - encodedVersionCursorMaxLength = 1000 -) - func decodeVersionCursor(encoded string) (VersionCursor, error) { - if err := validateStringLen(encoded, encodedVersionCursorMinLength, encodedVersionCursorMaxLength); err != nil { - return VersionCursor{}, err - } - m, err := decodeCursor(encoded) if err != nil { return VersionCursor{}, err @@ -161,8 +152,8 @@ func (vc VersionCursor) Encode() string { return "" } - return encodeCursor(map[string]string{ - "code": vc.code, + return encodeCursor([][2]string{ + {"code", vc.code}, }) } diff --git a/internal/domain/version_test.go b/internal/domain/version_test.go index 5e8eec3..43825a4 100644 --- a/internal/domain/version_test.go +++ b/internal/domain/version_test.go @@ -214,7 +214,6 @@ func TestListVersionsParams_SetEncodedCursor(t *testing.T) { require.ErrorIs(t, params.SetEncodedCursor(tt.args.cursor), tt.expectedErr) if tt.expectedErr != nil { - fmt.Println(tt.expectedErr.Error()) return } assert.Equal(t, tt.expectedCursor.Code(), params.Cursor().Code()) diff --git a/internal/service/data_sync_test.go b/internal/service/data_sync_test.go index 6f3c78d..2b93082 100644 --- a/internal/service/data_sync_test.go +++ b/internal/service/data_sync_test.go @@ -215,19 +215,16 @@ func TestDataSync(t *testing.T) { allServers := make(domain.Servers, 0, len(expectedServers)) for { - servers, err := serverRepo.List(ctx, listParams) + res, err := serverRepo.List(ctx, listParams) require.NoError(collect, err) - if len(servers) == 0 { + allServers = append(allServers, res.Servers()...) + + if res.Next().IsZero() { break } - allServers = append(allServers, servers...) - - require.NoError(collect, listParams.SetKeyGT(domain.NullString{ - Value: servers[len(servers)-1].Key(), - Valid: true, - })) + require.NoError(collect, listParams.SetCursor(res.Next())) } if !assert.Len(collect, allServers, len(expectedServers)) { diff --git a/internal/service/ennoblement_sync_test.go b/internal/service/ennoblement_sync_test.go index dc4d75f..ed869b7 100644 --- a/internal/service/ennoblement_sync_test.go +++ b/internal/service/ennoblement_sync_test.go @@ -173,21 +173,18 @@ func TestEnnoblementSync(t *testing.T) { require.NoError(collect, listParams.SetLimit(domain.ServerListMaxLimit)) for { - servers, err := serverRepo.List(ctx, listParams) + res, err := serverRepo.List(ctx, listParams) require.NoError(collect, err) - if len(servers) == 0 { - break - } - - for _, s := range servers { + for _, s := range res.Servers() { assert.WithinDuration(collect, time.Now(), s.EnnoblementDataSyncedAt(), time.Minute) } - require.NoError(collect, listParams.SetKeyGT(domain.NullString{ - Value: servers[len(servers)-1].Key(), - Valid: true, - })) + if res.Next().IsZero() { + break + } + + require.NoError(collect, listParams.SetCursor(res.Next())) } }, 30*time.Second, time.Second, "servers") diff --git a/internal/service/snapshot_creation_test.go b/internal/service/snapshot_creation_test.go index 75cc1b2..3417162 100644 --- a/internal/service/snapshot_creation_test.go +++ b/internal/service/snapshot_creation_test.go @@ -157,22 +157,19 @@ func TestSnapshotCreation(t *testing.T) { require.NoError(collect, listParams.SetLimit(domain.ServerListMaxLimit)) for { - servers, err := serverRepo.List(ctx, listParams) + res, err := serverRepo.List(ctx, listParams) require.NoError(collect, err) - if len(servers) == 0 { - return - } - - for _, s := range servers { + for _, s := range res.Servers() { assert.WithinDuration(collect, time.Now(), s.PlayerSnapshotsCreatedAt(), time.Minute, s.Key()) assert.WithinDuration(collect, time.Now(), s.TribeSnapshotsCreatedAt(), time.Minute, s.Key()) } - require.NoError(collect, listParams.SetKeyGT(domain.NullString{ - Value: servers[len(servers)-1].Key(), - Valid: true, - })) + if res.Next().IsZero() { + return + } + + require.NoError(collect, listParams.SetCursor(res.Next())) } }, 30*time.Second, 500*time.Millisecond, "servers")