refactor: server - cursor pagination (#56)
Reviewed-on: twhelp/corev3#56
This commit is contained in:
parent
b6d55b1741
commit
f07451351b
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
Loading…
Reference in New Issue