refactor: village - cursor pagination (#16)
Reviewed-on: twhelp/corev3#16
This commit is contained in:
parent
e3bb2eb5c4
commit
847cf220da
|
@ -76,17 +76,25 @@ func (repo *VillageBunRepository) CreateOrUpdate(ctx context.Context, params ...
|
|||
return nil
|
||||
}
|
||||
|
||||
func (repo *VillageBunRepository) List(ctx context.Context, params domain.ListVillagesParams) (domain.Villages, error) {
|
||||
func (repo *VillageBunRepository) List(
|
||||
ctx context.Context,
|
||||
params domain.ListVillagesParams,
|
||||
) (domain.ListVillagesResult, error) {
|
||||
var villages bunmodel.Villages
|
||||
|
||||
if err := repo.db.NewSelect().
|
||||
Model(&villages).
|
||||
Apply(listVillagesParamsApplier{params: params}.apply).
|
||||
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("couldn't select villages from the db: %w", err)
|
||||
return domain.ListVillagesResult{}, fmt.Errorf("couldn't select villages from the db: %w", err)
|
||||
}
|
||||
|
||||
return villages.ToDomain()
|
||||
converted, err := villages.ToDomain()
|
||||
if err != nil {
|
||||
return domain.ListVillagesResult{}, err
|
||||
}
|
||||
|
||||
return domain.NewListVillagesResult(separateListResultAndNext(converted, params.Limit()))
|
||||
}
|
||||
|
||||
func (repo *VillageBunRepository) Delete(ctx context.Context, serverKey string, ids ...int) error {
|
||||
|
@ -116,10 +124,6 @@ func (a listVillagesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
|||
q = q.Where("village.id IN (?)", bun.In(ids))
|
||||
}
|
||||
|
||||
if idGT := a.params.IDGT(); idGT.Valid {
|
||||
q = q.Where("village.id > ?", idGT.V)
|
||||
}
|
||||
|
||||
if serverKeys := a.params.ServerKeys(); len(serverKeys) > 0 {
|
||||
q = q.Where("village.server_key IN (?)", bun.In(serverKeys))
|
||||
}
|
||||
|
@ -139,5 +143,49 @@ func (a listVillagesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
|||
}
|
||||
}
|
||||
|
||||
return q.Limit(a.params.Limit()).Offset(a.params.Offset())
|
||||
return q.Limit(a.params.Limit() + 1).Apply(a.applyCursor)
|
||||
}
|
||||
|
||||
func (a listVillagesParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
cursor := a.params.Cursor()
|
||||
|
||||
if cursor.IsZero() {
|
||||
return q
|
||||
}
|
||||
|
||||
sort := a.params.Sort()
|
||||
cursorApplier := cursorPaginationApplier{
|
||||
data: make([]cursorPaginationApplierDataElement, 0, len(sort)),
|
||||
}
|
||||
|
||||
for _, s := range sort {
|
||||
var el cursorPaginationApplierDataElement
|
||||
|
||||
switch s {
|
||||
case domain.VillageSortIDASC:
|
||||
el.value = cursor.ID()
|
||||
el.unique = true
|
||||
el.column = "village.id"
|
||||
el.direction = sortDirectionASC
|
||||
case domain.VillageSortIDDESC:
|
||||
el.value = cursor.ID()
|
||||
el.unique = true
|
||||
el.column = "village.id"
|
||||
el.direction = sortDirectionDESC
|
||||
case domain.VillageSortServerKeyASC:
|
||||
el.value = cursor.ServerKey()
|
||||
el.column = "village.server_key"
|
||||
el.direction = sortDirectionASC
|
||||
case domain.VillageSortServerKeyDESC:
|
||||
el.value = cursor.ServerKey()
|
||||
el.column = "village.server_key"
|
||||
el.direction = sortDirectionDESC
|
||||
default:
|
||||
return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue))
|
||||
}
|
||||
|
||||
cursorApplier.data = append(cursorApplier.data, el)
|
||||
}
|
||||
|
||||
return q.Apply(cursorApplier.apply)
|
||||
}
|
||||
|
|
|
@ -371,7 +371,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
|
||||
res, err := repos.player.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, len(res.Players()))
|
||||
require.NotEmpty(t, res.Players())
|
||||
randPlayer := res.Players()[0]
|
||||
|
||||
require.NoError(t, params.SetIDs([]int{randPlayer.ID()}))
|
||||
|
@ -406,7 +406,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
|
||||
res, err := repos.player.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, len(res.Players()))
|
||||
require.NotEmpty(t, res.Players())
|
||||
randPlayer := res.Players()[0]
|
||||
|
||||
require.NoError(t, params.SetNames([]string{randPlayer.Name()}))
|
||||
|
@ -544,7 +544,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
|
||||
players := res.Players()
|
||||
assert.NotEmpty(t, len(players))
|
||||
for _, p := range res.Players() {
|
||||
for _, p := range players {
|
||||
assert.GreaterOrEqual(t, p.ID(), params.Cursor().ID())
|
||||
assert.True(t, slices.Contains(serverKeys, p.ServerKey()))
|
||||
}
|
||||
|
@ -590,7 +590,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
)
|
||||
}))
|
||||
assert.GreaterOrEqual(t, players[0].ID(), params.Cursor().ID())
|
||||
for _, p := range res.Players() {
|
||||
for _, p := range players {
|
||||
assert.GreaterOrEqual(t, p.ServerKey(), params.Cursor().ServerKey())
|
||||
}
|
||||
},
|
||||
|
@ -632,7 +632,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
) * -1
|
||||
}))
|
||||
assert.LessOrEqual(t, players[0].ID(), params.Cursor().ID())
|
||||
for _, p := range res.Players() {
|
||||
for _, p := range players {
|
||||
assert.LessOrEqual(t, p.ServerKey(), params.Cursor().ServerKey())
|
||||
}
|
||||
},
|
||||
|
@ -652,6 +652,8 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
assertResult: func(t *testing.T, params domain.ListPlayersParams, res domain.ListPlayersResult) {
|
||||
t.Helper()
|
||||
assert.Len(t, res.Players(), params.Limit())
|
||||
assert.False(t, res.Self().IsZero())
|
||||
assert.False(t, res.Next().IsZero())
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
|
|
|
@ -488,6 +488,7 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
|||
assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) {
|
||||
t.Helper()
|
||||
assert.Len(t, res.Servers(), params.Limit())
|
||||
assert.False(t, res.Self().IsZero())
|
||||
assert.False(t, res.Next().IsZero())
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
|
|
|
@ -37,7 +37,7 @@ type playerRepository interface {
|
|||
|
||||
type villageRepository interface {
|
||||
CreateOrUpdate(ctx context.Context, params ...domain.CreateVillageParams) error
|
||||
List(ctx context.Context, params domain.ListVillagesParams) (domain.Villages, error)
|
||||
List(ctx context.Context, params domain.ListVillagesParams) (domain.ListVillagesResult, error)
|
||||
Delete(ctx context.Context, serverKey string, ids ...int) error
|
||||
}
|
||||
|
||||
|
|
|
@ -434,7 +434,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
|||
|
||||
res, err := repos.tribe.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, len(res.Tribes()))
|
||||
require.NotEmpty(t, res.Tribes())
|
||||
randTribe := res.Tribes()[0]
|
||||
|
||||
require.NoError(t, params.SetIDs([]int{randTribe.ID()}))
|
||||
|
@ -469,7 +469,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
|||
|
||||
res, err := repos.tribe.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, len(res.Tribes()))
|
||||
require.NotEmpty(t, res.Tribes())
|
||||
randTribe := res.Tribes()[0]
|
||||
|
||||
require.NoError(t, params.SetTags([]string{randTribe.Tag()}))
|
||||
|
@ -615,7 +615,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
|||
)
|
||||
}))
|
||||
assert.GreaterOrEqual(t, tribes[0].ID(), params.Cursor().ID())
|
||||
for _, tr := range res.Tribes() {
|
||||
for _, tr := range tribes {
|
||||
assert.GreaterOrEqual(t, tr.ServerKey(), params.Cursor().ServerKey())
|
||||
}
|
||||
},
|
||||
|
@ -657,7 +657,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
|||
) * -1
|
||||
}))
|
||||
assert.LessOrEqual(t, tribes[0].ID(), params.Cursor().ID())
|
||||
for _, tr := range res.Tribes() {
|
||||
for _, tr := range tribes {
|
||||
assert.LessOrEqual(t, tr.ServerKey(), params.Cursor().ServerKey())
|
||||
}
|
||||
},
|
||||
|
@ -703,7 +703,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
|||
}))
|
||||
assert.GreaterOrEqual(t, tribes[0].ID(), params.Cursor().ID())
|
||||
assert.GreaterOrEqual(t, tribes[0].ServerKey(), params.Cursor().ServerKey())
|
||||
for _, tr := range res.Tribes() {
|
||||
for _, tr := range tribes {
|
||||
assert.LessOrEqual(t, tr.Points(), params.Cursor().Points())
|
||||
}
|
||||
},
|
||||
|
@ -749,7 +749,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
|||
}))
|
||||
assert.GreaterOrEqual(t, tribes[0].ID(), params.Cursor().ID())
|
||||
assert.GreaterOrEqual(t, tribes[0].ServerKey(), params.Cursor().ServerKey())
|
||||
for _, tr := range res.Tribes() {
|
||||
for _, tr := range tribes {
|
||||
assert.GreaterOrEqual(t, tr.DeletedAt(), params.Cursor().DeletedAt())
|
||||
}
|
||||
},
|
||||
|
@ -769,6 +769,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
|||
assertResult: func(t *testing.T, params domain.ListTribesParams, res domain.ListTribesResult) {
|
||||
t.Helper()
|
||||
assert.Len(t, res.Tribes(), params.Limit())
|
||||
assert.False(t, res.Self().IsZero())
|
||||
assert.False(t, res.Next().IsZero())
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
|
|
|
@ -175,6 +175,7 @@ func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
assertResult: func(t *testing.T, params domain.ListVersionsParams, res domain.ListVersionsResult) {
|
||||
t.Helper()
|
||||
assert.Len(t, res.Versions(), params.Limit())
|
||||
assert.False(t, res.Self().IsZero())
|
||||
assert.False(t, res.Next().IsZero())
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
|
|
|
@ -3,7 +3,6 @@ package adapter_test
|
|||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
"testing"
|
||||
|
@ -38,8 +37,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
require.NoError(t, listParams.SetIDs(ids))
|
||||
require.NoError(t, listParams.SetServerKeys([]string{params[0].ServerKey()}))
|
||||
|
||||
villages, err := repos.village.List(ctx, listParams)
|
||||
res, err := repos.village.List(ctx, listParams)
|
||||
require.NoError(t, err)
|
||||
villages := res.Villages()
|
||||
assert.Len(t, villages, len(params))
|
||||
for i, p := range params {
|
||||
idx := slices.IndexFunc(villages, func(village domain.Village) bool {
|
||||
|
@ -97,17 +97,11 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
|
||||
repos := newRepos(t)
|
||||
|
||||
villages, listVillagesErr := repos.village.List(ctx, domain.NewListVillagesParams())
|
||||
require.NoError(t, listVillagesErr)
|
||||
require.NotEmpty(t, villages)
|
||||
randVillage := villages[0]
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params func(t *testing.T) domain.ListVillagesParams
|
||||
assertVillages func(t *testing.T, params domain.ListVillagesParams, villages domain.Villages)
|
||||
assertError func(t *testing.T, err error)
|
||||
assertTotal func(t *testing.T, params domain.ListVillagesParams, total int)
|
||||
name string
|
||||
params func(t *testing.T) domain.ListVillagesParams
|
||||
assertResult func(t *testing.T, params domain.ListVillagesParams, res domain.ListVillagesResult)
|
||||
assertError func(t *testing.T, err error)
|
||||
}{
|
||||
{
|
||||
name: "OK: default params",
|
||||
|
@ -115,8 +109,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
t.Helper()
|
||||
return domain.NewListVillagesParams()
|
||||
},
|
||||
assertVillages: func(t *testing.T, _ domain.ListVillagesParams, villages domain.Villages) {
|
||||
assertResult: func(t *testing.T, _ domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||
t.Helper()
|
||||
villages := res.Villages()
|
||||
assert.NotEmpty(t, len(villages))
|
||||
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
||||
return cmp.Or(
|
||||
|
@ -124,15 +119,13 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
cmp.Compare(a.ID(), b.ID()),
|
||||
)
|
||||
}))
|
||||
assert.False(t, res.Self().IsZero())
|
||||
assert.True(t, res.Next().IsZero())
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, _ domain.ListVillagesParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: sort=[serverKey DESC, id DESC]",
|
||||
|
@ -142,8 +135,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
require.NoError(t, params.SetSort([]domain.VillageSort{domain.VillageSortServerKeyDESC, domain.VillageSortIDDESC}))
|
||||
return params
|
||||
},
|
||||
assertVillages: func(t *testing.T, _ domain.ListVillagesParams, villages domain.Villages) {
|
||||
assertResult: func(t *testing.T, _ domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||
t.Helper()
|
||||
villages := res.Villages()
|
||||
assert.NotEmpty(t, len(villages))
|
||||
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
||||
return cmp.Or(
|
||||
|
@ -156,26 +150,32 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, _ domain.ListVillagesParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fmt.Sprintf("OK: ids=[%d] serverKeys=[%s]", randVillage.ID(), randVillage.ServerKey()),
|
||||
name: "OK: ids serverKeys",
|
||||
params: func(t *testing.T) domain.ListVillagesParams {
|
||||
t.Helper()
|
||||
|
||||
params := domain.NewListVillagesParams()
|
||||
|
||||
res, err := repos.village.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, res.Villages())
|
||||
randVillage := res.Villages()[0]
|
||||
|
||||
require.NoError(t, params.SetIDs([]int{randVillage.ID()}))
|
||||
require.NoError(t, params.SetServerKeys([]string{randVillage.ServerKey()}))
|
||||
|
||||
return params
|
||||
},
|
||||
assertVillages: func(t *testing.T, params domain.ListVillagesParams, villages domain.Villages) {
|
||||
assertResult: func(t *testing.T, params domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||
t.Helper()
|
||||
|
||||
ids := params.IDs()
|
||||
serverKeys := params.ServerKeys()
|
||||
|
||||
villages := res.Villages()
|
||||
assert.Len(t, villages, len(ids))
|
||||
for _, v := range villages {
|
||||
assert.True(t, slices.Contains(ids, v.ID()))
|
||||
assert.True(t, slices.Contains(serverKeys, v.ServerKey()))
|
||||
|
@ -185,58 +185,147 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, _ domain.ListVillagesParams, total int) {
|
||||
},
|
||||
{
|
||||
name: "OK: cursor serverKeys sort=[id ASC]",
|
||||
params: func(t *testing.T) domain.ListVillagesParams {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
|
||||
params := domain.NewListVillagesParams()
|
||||
|
||||
res, err := repos.village.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, len(res.Villages()), 2)
|
||||
|
||||
require.NoError(t, params.SetSort([]domain.VillageSort{domain.VillageSortIDASC}))
|
||||
require.NoError(t, params.SetServerKeys([]string{res.Villages()[1].ServerKey()}))
|
||||
require.NoError(t, params.SetCursor(domaintest.NewVillageCursor(t, func(cfg *domaintest.VillageCursorConfig) {
|
||||
cfg.ID = res.Villages()[1].ID()
|
||||
})))
|
||||
|
||||
return params
|
||||
},
|
||||
assertResult: func(t *testing.T, params domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||
t.Helper()
|
||||
|
||||
serverKeys := params.ServerKeys()
|
||||
|
||||
villages := res.Villages()
|
||||
assert.NotEmpty(t, len(villages))
|
||||
for _, v := range villages {
|
||||
assert.GreaterOrEqual(t, v.ID(), params.Cursor().ID())
|
||||
assert.True(t, slices.Contains(serverKeys, v.ServerKey()))
|
||||
}
|
||||
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
}))
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: fmt.Sprintf("OK: idGT=%d", randVillage.ID()),
|
||||
name: "OK: cursor sort=[serverKey ASC, id ASC]",
|
||||
params: func(t *testing.T) domain.ListVillagesParams {
|
||||
t.Helper()
|
||||
|
||||
params := domain.NewListVillagesParams()
|
||||
require.NoError(t, params.SetIDGT(domain.NullInt{
|
||||
V: randVillage.ID(),
|
||||
Valid: true,
|
||||
require.NoError(t, params.SetSort([]domain.VillageSort{
|
||||
domain.VillageSortServerKeyASC,
|
||||
domain.VillageSortIDASC,
|
||||
}))
|
||||
|
||||
res, err := repos.village.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, len(res.Villages()), 2)
|
||||
|
||||
require.NoError(t, params.SetCursor(domaintest.NewVillageCursor(t, func(cfg *domaintest.VillageCursorConfig) {
|
||||
cfg.ID = res.Villages()[1].ID()
|
||||
cfg.ServerKey = res.Villages()[1].ServerKey()
|
||||
})))
|
||||
|
||||
return params
|
||||
},
|
||||
assertVillages: func(t *testing.T, params domain.ListVillagesParams, villages domain.Villages) {
|
||||
assertResult: func(t *testing.T, params domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, villages)
|
||||
villages := res.Villages()
|
||||
assert.NotEmpty(t, len(villages))
|
||||
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
||||
return cmp.Or(
|
||||
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||
cmp.Compare(a.ID(), b.ID()),
|
||||
)
|
||||
}))
|
||||
assert.GreaterOrEqual(t, villages[0].ID(), params.Cursor().ID())
|
||||
for _, v := range villages {
|
||||
assert.Greater(t, v.ID(), params.IDGT().V, v.ID())
|
||||
assert.GreaterOrEqual(t, v.ServerKey(), params.Cursor().ServerKey())
|
||||
}
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, _ domain.ListVillagesParams, total int) {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK: offset=1 limit=2",
|
||||
name: "OK: cursor sort=[serverKey DESC, id DESC]",
|
||||
params: func(t *testing.T) domain.ListVillagesParams {
|
||||
t.Helper()
|
||||
|
||||
params := domain.NewListVillagesParams()
|
||||
require.NoError(t, params.SetOffset(1))
|
||||
require.NoError(t, params.SetLimit(2))
|
||||
require.NoError(t, params.SetSort([]domain.VillageSort{
|
||||
domain.VillageSortServerKeyDESC,
|
||||
domain.VillageSortIDDESC,
|
||||
}))
|
||||
|
||||
res, err := repos.village.List(ctx, params)
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, len(res.Villages()), 2)
|
||||
|
||||
require.NoError(t, params.SetCursor(domaintest.NewVillageCursor(t, func(cfg *domaintest.VillageCursorConfig) {
|
||||
cfg.ID = res.Villages()[1].ID()
|
||||
cfg.ServerKey = res.Villages()[1].ServerKey()
|
||||
})))
|
||||
|
||||
return params
|
||||
},
|
||||
assertVillages: func(t *testing.T, params domain.ListVillagesParams, villages domain.Villages) {
|
||||
assertResult: func(t *testing.T, params domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||
t.Helper()
|
||||
assert.Len(t, villages, params.Limit())
|
||||
villages := res.Villages()
|
||||
assert.NotEmpty(t, len(villages))
|
||||
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
||||
return cmp.Or(
|
||||
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||
cmp.Compare(a.ID(), b.ID()),
|
||||
) * -1
|
||||
}))
|
||||
assert.LessOrEqual(t, villages[0].ID(), params.Cursor().ID())
|
||||
for _, v := range villages {
|
||||
assert.LessOrEqual(t, v.ServerKey(), params.Cursor().ServerKey())
|
||||
}
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
assertTotal: func(t *testing.T, _ domain.ListVillagesParams, total int) {
|
||||
},
|
||||
{
|
||||
name: "OK: limit=2",
|
||||
params: func(t *testing.T) domain.ListVillagesParams {
|
||||
t.Helper()
|
||||
assert.NotEmpty(t, total)
|
||||
params := domain.NewListVillagesParams()
|
||||
require.NoError(t, params.SetLimit(2))
|
||||
return params
|
||||
},
|
||||
assertResult: func(t *testing.T, params domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||
t.Helper()
|
||||
assert.Len(t, res.Villages(), params.Limit())
|
||||
assert.False(t, res.Self().IsZero())
|
||||
assert.False(t, res.Next().IsZero())
|
||||
},
|
||||
assertError: func(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -249,7 +338,7 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
|
||||
res, err := repos.village.List(ctx, params)
|
||||
tt.assertError(t, err)
|
||||
tt.assertVillages(t, params, res)
|
||||
tt.assertResult(t, params, res)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -276,8 +365,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
listVillagesParams := domain.NewListVillagesParams()
|
||||
require.NoError(t, listVillagesParams.SetServerKeys(serverKeys))
|
||||
|
||||
villagesBeforeDelete, err := repos.village.List(ctx, listVillagesParams)
|
||||
res, err := repos.village.List(ctx, listVillagesParams)
|
||||
require.NoError(t, err)
|
||||
villagesBeforeDelete := res.Villages()
|
||||
|
||||
var serverKey string
|
||||
var ids []int
|
||||
|
@ -296,8 +386,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
|||
|
||||
require.NoError(t, repos.village.Delete(ctx, serverKey, idsToDelete...))
|
||||
|
||||
villagesAfterDelete, err := repos.village.List(ctx, listVillagesParams)
|
||||
res, err = repos.village.List(ctx, listVillagesParams)
|
||||
require.NoError(t, err)
|
||||
villagesAfterDelete := res.Villages()
|
||||
assert.Len(t, villagesAfterDelete, len(villagesBeforeDelete)-len(idsToDelete))
|
||||
for _, v := range villagesAfterDelete {
|
||||
if v.ServerKey() == serverKey && slices.Contains(ids, v.ID()) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
type VillageRepository interface {
|
||||
CreateOrUpdate(ctx context.Context, params ...domain.CreateVillageParams) error
|
||||
List(ctx context.Context, params domain.ListVillagesParams) (domain.Villages, error)
|
||||
List(ctx context.Context, params domain.ListVillagesParams) (domain.ListVillagesResult, error)
|
||||
Delete(ctx context.Context, serverKey string, ids ...int) error
|
||||
}
|
||||
|
||||
|
@ -94,24 +94,28 @@ func (svc *VillageService) delete(ctx context.Context, serverKey string, village
|
|||
var toDelete []int
|
||||
|
||||
for {
|
||||
storedVillages, err := svc.repo.List(ctx, listParams)
|
||||
res, err := svc.repo.List(ctx, listParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(storedVillages) == 0 {
|
||||
toDelete = append(toDelete, res.Villages().Delete(serverKey, villages)...)
|
||||
|
||||
if res.Next().IsZero() {
|
||||
break
|
||||
}
|
||||
|
||||
toDelete = append(toDelete, storedVillages.Delete(serverKey, villages)...)
|
||||
|
||||
if err = listParams.SetIDGT(domain.NullInt{
|
||||
V: storedVillages[len(storedVillages)-1].ID(),
|
||||
Valid: true,
|
||||
}); err != nil {
|
||||
if err = listParams.SetCursor(res.Next()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return svc.repo.Delete(ctx, serverKey, toDelete...)
|
||||
}
|
||||
|
||||
func (svc *VillageService) List(
|
||||
ctx context.Context,
|
||||
params domain.ListVillagesParams,
|
||||
) (domain.ListVillagesResult, error) {
|
||||
return svc.repo.List(ctx, params)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,32 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type VillageCursorConfig struct {
|
||||
ID int
|
||||
ServerKey string
|
||||
}
|
||||
|
||||
func NewVillageCursor(tb TestingTB, opts ...func(cfg *VillageCursorConfig)) domain.VillageCursor {
|
||||
tb.Helper()
|
||||
|
||||
cfg := &VillageCursorConfig{
|
||||
ID: RandID(),
|
||||
ServerKey: RandServerKey(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
|
||||
pc, err := domain.NewVillageCursor(
|
||||
cfg.ID,
|
||||
cfg.ServerKey,
|
||||
)
|
||||
require.NoError(tb, err)
|
||||
|
||||
return pc
|
||||
}
|
||||
|
||||
type VillageConfig struct {
|
||||
ID int
|
||||
ServerKey string
|
||||
|
|
|
@ -412,6 +412,33 @@ const (
|
|||
PlayerSortDeletedAtDESC
|
||||
)
|
||||
|
||||
func newPlayerSortFromString(s string) (PlayerSort, error) {
|
||||
allowed := []PlayerSort{
|
||||
PlayerSortODScoreAttASC,
|
||||
PlayerSortODScoreAttDESC,
|
||||
PlayerSortODScoreDefASC,
|
||||
PlayerSortODScoreDefDESC,
|
||||
PlayerSortODScoreSupASC,
|
||||
PlayerSortODScoreSupDESC,
|
||||
PlayerSortODScoreTotalASC,
|
||||
PlayerSortODScoreTotalDESC,
|
||||
PlayerSortPointsASC,
|
||||
PlayerSortPointsDESC,
|
||||
PlayerSortDeletedAtASC,
|
||||
PlayerSortDeletedAtDESC,
|
||||
}
|
||||
|
||||
for _, a := range allowed {
|
||||
if strings.EqualFold(a.String(), s) {
|
||||
return a, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, UnsupportedSortStringError{
|
||||
Sort: s,
|
||||
}
|
||||
}
|
||||
|
||||
// IsInConflict returns true if two sorts can't be used together (e.g. PlayerSortIDASC and PlayerSortIDDESC).
|
||||
func (s PlayerSort) IsInConflict(s2 PlayerSort) bool {
|
||||
ss := []PlayerSort{s, s2}
|
||||
|
@ -460,33 +487,6 @@ func (s PlayerSort) String() string {
|
|||
}
|
||||
}
|
||||
|
||||
func newPlayerSortFromString(s string) (PlayerSort, error) {
|
||||
allowed := []PlayerSort{
|
||||
PlayerSortODScoreAttASC,
|
||||
PlayerSortODScoreAttDESC,
|
||||
PlayerSortODScoreDefASC,
|
||||
PlayerSortODScoreDefDESC,
|
||||
PlayerSortODScoreSupASC,
|
||||
PlayerSortODScoreSupDESC,
|
||||
PlayerSortODScoreTotalASC,
|
||||
PlayerSortODScoreTotalDESC,
|
||||
PlayerSortPointsASC,
|
||||
PlayerSortPointsDESC,
|
||||
PlayerSortDeletedAtASC,
|
||||
PlayerSortDeletedAtDESC,
|
||||
}
|
||||
|
||||
for _, a := range allowed {
|
||||
if strings.EqualFold(a.String(), s) {
|
||||
return a, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, UnsupportedSortStringError{
|
||||
Sort: s,
|
||||
}
|
||||
}
|
||||
|
||||
type PlayerCursor struct {
|
||||
id int
|
||||
serverKey string
|
||||
|
|
|
@ -510,7 +510,7 @@ func TestNewPlayerCursor(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tc, err := domain.NewPlayerCursor(
|
||||
pc, err := domain.NewPlayerCursor(
|
||||
tt.args.id,
|
||||
tt.args.serverKey,
|
||||
tt.args.odScoreAtt,
|
||||
|
@ -524,15 +524,15 @@ func TestNewPlayerCursor(t *testing.T) {
|
|||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.args.id, tc.ID())
|
||||
assert.Equal(t, tt.args.serverKey, tc.ServerKey())
|
||||
assert.Equal(t, tt.args.odScoreAtt, tc.ODScoreAtt())
|
||||
assert.Equal(t, tt.args.odScoreDef, tc.ODScoreDef())
|
||||
assert.Equal(t, tt.args.odScoreSup, tc.ODScoreSup())
|
||||
assert.Equal(t, tt.args.odScoreTotal, tc.ODScoreTotal())
|
||||
assert.Equal(t, tt.args.points, tc.Points())
|
||||
assert.Equal(t, tt.args.deletedAt, tc.DeletedAt())
|
||||
assert.NotEmpty(t, tc.Encode())
|
||||
assert.Equal(t, tt.args.id, pc.ID())
|
||||
assert.Equal(t, tt.args.serverKey, pc.ServerKey())
|
||||
assert.Equal(t, tt.args.odScoreAtt, pc.ODScoreAtt())
|
||||
assert.Equal(t, tt.args.odScoreDef, pc.ODScoreDef())
|
||||
assert.Equal(t, tt.args.odScoreSup, pc.ODScoreSup())
|
||||
assert.Equal(t, tt.args.odScoreTotal, pc.ODScoreTotal())
|
||||
assert.Equal(t, tt.args.points, pc.Points())
|
||||
assert.Equal(t, tt.args.deletedAt, pc.DeletedAt())
|
||||
assert.NotEmpty(t, pc.Encode())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -450,6 +450,33 @@ const (
|
|||
TribeSortDeletedAtDESC
|
||||
)
|
||||
|
||||
func newTribeSortFromString(s string) (TribeSort, error) {
|
||||
allowed := []TribeSort{
|
||||
TribeSortODScoreAttASC,
|
||||
TribeSortODScoreAttDESC,
|
||||
TribeSortODScoreDefASC,
|
||||
TribeSortODScoreDefDESC,
|
||||
TribeSortODScoreTotalASC,
|
||||
TribeSortODScoreTotalDESC,
|
||||
TribeSortPointsASC,
|
||||
TribeSortPointsDESC,
|
||||
TribeSortDominanceASC,
|
||||
TribeSortDominanceDESC,
|
||||
TribeSortDeletedAtASC,
|
||||
TribeSortDeletedAtDESC,
|
||||
}
|
||||
|
||||
for _, a := range allowed {
|
||||
if strings.EqualFold(a.String(), s) {
|
||||
return a, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, UnsupportedSortStringError{
|
||||
Sort: s,
|
||||
}
|
||||
}
|
||||
|
||||
// IsInConflict returns true if two sorts can't be used together (e.g. TribeSortIDASC and TribeSortIDDESC).
|
||||
func (s TribeSort) IsInConflict(s2 TribeSort) bool {
|
||||
ss := []TribeSort{s, s2}
|
||||
|
@ -498,33 +525,6 @@ func (s TribeSort) String() string {
|
|||
}
|
||||
}
|
||||
|
||||
func newTribeSortFromString(s string) (TribeSort, error) {
|
||||
allowed := []TribeSort{
|
||||
TribeSortODScoreAttASC,
|
||||
TribeSortODScoreAttDESC,
|
||||
TribeSortODScoreDefASC,
|
||||
TribeSortODScoreDefDESC,
|
||||
TribeSortODScoreTotalASC,
|
||||
TribeSortODScoreTotalDESC,
|
||||
TribeSortPointsASC,
|
||||
TribeSortPointsDESC,
|
||||
TribeSortDominanceASC,
|
||||
TribeSortDominanceDESC,
|
||||
TribeSortDeletedAtASC,
|
||||
TribeSortDeletedAtDESC,
|
||||
}
|
||||
|
||||
for _, a := range allowed {
|
||||
if strings.EqualFold(a.String(), s) {
|
||||
return a, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, UnsupportedSortStringError{
|
||||
Sort: s,
|
||||
}
|
||||
}
|
||||
|
||||
type TribeCursor struct {
|
||||
id int
|
||||
serverKey string
|
||||
|
|
|
@ -31,7 +31,7 @@ type keyValuePair struct {
|
|||
value any
|
||||
}
|
||||
|
||||
func (kvp keyValuePair) valueString() string {
|
||||
func (kvp keyValuePair) valueToString() string {
|
||||
switch v := kvp.value.(type) {
|
||||
case string:
|
||||
return v
|
||||
|
@ -59,7 +59,7 @@ func encodeCursor(kvps []keyValuePair) string {
|
|||
n := len(cursorSeparator) * (len(kvps) - 1)
|
||||
|
||||
for i, kvp := range kvps {
|
||||
value := kvp.valueString()
|
||||
value := kvp.valueToString()
|
||||
n += len(kvp.key) + len(value) + len(cursorKeyValueSeparator)
|
||||
values[i] = value
|
||||
}
|
||||
|
|
|
@ -147,6 +147,10 @@ func (v Village) Base() BaseVillage {
|
|||
}
|
||||
}
|
||||
|
||||
func (v Village) IsZero() bool {
|
||||
return v == Village{}
|
||||
}
|
||||
|
||||
type Villages []Village
|
||||
|
||||
// Delete finds all villages with the given serverKey that are not in the given slice with active villages
|
||||
|
@ -248,13 +252,93 @@ func (s VillageSort) String() string {
|
|||
}
|
||||
}
|
||||
|
||||
type VillageCursor struct {
|
||||
id int
|
||||
serverKey string
|
||||
}
|
||||
|
||||
const villageCursorModelName = "VillageCursor"
|
||||
|
||||
func NewVillageCursor(id int, serverKey string) (VillageCursor, error) {
|
||||
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
||||
return VillageCursor{}, ValidationError{
|
||||
Model: villageCursorModelName,
|
||||
Field: "id",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateServerKey(serverKey); err != nil {
|
||||
return VillageCursor{}, ValidationError{
|
||||
Model: villageCursorModelName,
|
||||
Field: "serverKey",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
return VillageCursor{
|
||||
id: id,
|
||||
serverKey: serverKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func decodeVillageCursor(encoded string) (VillageCursor, error) {
|
||||
m, err := decodeCursor(encoded)
|
||||
if err != nil {
|
||||
return VillageCursor{}, err
|
||||
}
|
||||
|
||||
id, err := m.int("id")
|
||||
if err != nil {
|
||||
return VillageCursor{}, ErrInvalidCursor
|
||||
}
|
||||
|
||||
serverKey, err := m.string("serverKey")
|
||||
if err != nil {
|
||||
return VillageCursor{}, ErrInvalidCursor
|
||||
}
|
||||
|
||||
vc, err := NewVillageCursor(
|
||||
id,
|
||||
serverKey,
|
||||
)
|
||||
if err != nil {
|
||||
return VillageCursor{}, ErrInvalidCursor
|
||||
}
|
||||
|
||||
return vc, nil
|
||||
}
|
||||
|
||||
func (vc VillageCursor) ID() int {
|
||||
return vc.id
|
||||
}
|
||||
|
||||
func (vc VillageCursor) ServerKey() string {
|
||||
return vc.serverKey
|
||||
}
|
||||
|
||||
func (vc VillageCursor) IsZero() bool {
|
||||
return vc == VillageCursor{}
|
||||
}
|
||||
|
||||
func (vc VillageCursor) Encode() string {
|
||||
if vc.IsZero() {
|
||||
return ""
|
||||
}
|
||||
|
||||
return encodeCursor([]keyValuePair{
|
||||
{"id", vc.id},
|
||||
{"serverKey", vc.serverKey},
|
||||
})
|
||||
}
|
||||
|
||||
type ListVillagesParams struct {
|
||||
ids []int
|
||||
idGT NullInt
|
||||
serverKeys []string
|
||||
sort []VillageSort
|
||||
cursor VillageCursor
|
||||
limit int
|
||||
offset int
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -293,26 +377,6 @@ func (params *ListVillagesParams) SetIDs(ids []int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) IDGT() NullInt {
|
||||
return params.idGT
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) SetIDGT(idGT NullInt) error {
|
||||
if idGT.Valid {
|
||||
if err := validateIntInRange(idGT.V, 0, math.MaxInt); err != nil {
|
||||
return ValidationError{
|
||||
Model: listVillagesParamsModelName,
|
||||
Field: "idGT",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.idGT = idGT
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) ServerKeys() []string {
|
||||
return params.serverKeys
|
||||
}
|
||||
|
@ -357,6 +421,30 @@ func (params *ListVillagesParams) SetSort(sort []VillageSort) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) Cursor() VillageCursor {
|
||||
return params.cursor
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) SetCursor(cursor VillageCursor) error {
|
||||
params.cursor = cursor
|
||||
return nil
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) SetEncodedCursor(encoded string) error {
|
||||
decoded, err := decodeVillageCursor(encoded)
|
||||
if err != nil {
|
||||
return ValidationError{
|
||||
Model: listVillagesParamsModelName,
|
||||
Field: "cursor",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
params.cursor = decoded
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) Limit() int {
|
||||
return params.limit
|
||||
}
|
||||
|
@ -375,20 +463,53 @@ func (params *ListVillagesParams) SetLimit(limit int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) Offset() int {
|
||||
return params.offset
|
||||
type ListVillagesResult struct {
|
||||
villages Villages
|
||||
self VillageCursor
|
||||
next VillageCursor
|
||||
}
|
||||
|
||||
func (params *ListVillagesParams) SetOffset(offset int) error {
|
||||
if err := validateIntInRange(offset, 0, math.MaxInt); err != nil {
|
||||
return ValidationError{
|
||||
Model: listVillagesParamsModelName,
|
||||
Field: "offset",
|
||||
Err: err,
|
||||
const listVillagesResultModelName = "ListVillagesResult"
|
||||
|
||||
func NewListVillagesResult(villages Villages, next Village) (ListVillagesResult, error) {
|
||||
var err error
|
||||
res := ListVillagesResult{
|
||||
villages: villages,
|
||||
}
|
||||
|
||||
if len(villages) > 0 {
|
||||
res.self, err = NewVillageCursor(villages[0].ID(), villages[0].ServerKey())
|
||||
if err != nil {
|
||||
return ListVillagesResult{}, ValidationError{
|
||||
Model: listVillagesResultModelName,
|
||||
Field: "self",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.offset = offset
|
||||
if !next.IsZero() {
|
||||
res.next, err = NewVillageCursor(next.ID(), next.ServerKey())
|
||||
if err != nil {
|
||||
return ListVillagesResult{}, ValidationError{
|
||||
Model: listVillagesResultModelName,
|
||||
Field: "next",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (res ListVillagesResult) Villages() Villages {
|
||||
return res.villages
|
||||
}
|
||||
|
||||
func (res ListVillagesResult) Self() VillageCursor {
|
||||
return res.self
|
||||
}
|
||||
|
||||
func (res ListVillagesResult) Next() VillageCursor {
|
||||
return res.next
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
||||
"github.com/brianvoe/gofakeit/v7"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -146,6 +147,82 @@ func TestVillageSort_IsInConflict(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNewVillageCursor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
validVillageCursor := domaintest.NewVillageCursor(t)
|
||||
|
||||
type args struct {
|
||||
id int
|
||||
serverKey string
|
||||
}
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
args args
|
||||
expectedErr error
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
name: "OK",
|
||||
args: args{
|
||||
id: validVillageCursor.ID(),
|
||||
serverKey: validVillageCursor.ServerKey(),
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "ERR: id < 1",
|
||||
args: args{
|
||||
id: 0,
|
||||
serverKey: validVillageCursor.ServerKey(),
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "VillageCursor",
|
||||
Field: "id",
|
||||
Err: domain.MinGreaterEqualError{
|
||||
Min: 1,
|
||||
Current: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, serverKeyTest := range newServerKeyValidationTests() {
|
||||
tests = append(tests, test{
|
||||
name: serverKeyTest.name,
|
||||
args: args{
|
||||
id: validVillageCursor.ID(),
|
||||
serverKey: serverKeyTest.key,
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "VillageCursor",
|
||||
Field: "serverKey",
|
||||
Err: serverKeyTest.expectedErr,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
vc, err := domain.NewVillageCursor(
|
||||
tt.args.id,
|
||||
tt.args.serverKey,
|
||||
)
|
||||
require.ErrorIs(t, err, tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.args.id, vc.ID())
|
||||
assert.Equal(t, tt.args.serverKey, vc.ServerKey())
|
||||
assert.NotEmpty(t, vc.Encode())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListVillagesParams_SetIDs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -206,61 +283,6 @@ func TestListVillagesParams_SetIDs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestListVillagesParams_SetIDGT(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
idGT domain.NullInt
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "OK",
|
||||
args: args{
|
||||
idGT: domain.NullInt{
|
||||
V: domaintest.RandID(),
|
||||
Valid: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: value < 0",
|
||||
args: args{
|
||||
idGT: domain.NullInt{
|
||||
V: -1,
|
||||
Valid: true,
|
||||
},
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListVillagesParams",
|
||||
Field: "idGT",
|
||||
Err: domain.MinGreaterEqualError{
|
||||
Min: 0,
|
||||
Current: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
params := domain.NewListVillagesParams()
|
||||
|
||||
require.ErrorIs(t, params.SetIDGT(tt.args.idGT), tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.args.idGT, params.IDGT())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListVillagesParams_SetServerKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -403,6 +425,87 @@ func TestListVillagesParams_SetSort(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestListVillagesParams_SetEncodedCursor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
validCursor := domaintest.NewVillageCursor(t)
|
||||
|
||||
type args struct {
|
||||
cursor string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedCursor domain.VillageCursor
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "OK",
|
||||
args: args{
|
||||
cursor: validCursor.Encode(),
|
||||
},
|
||||
expectedCursor: validCursor,
|
||||
},
|
||||
{
|
||||
name: "ERR: len(cursor) < 1",
|
||||
args: args{
|
||||
cursor: "",
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListVillagesParams",
|
||||
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: "ListVillagesParams",
|
||||
Field: "cursor",
|
||||
Err: domain.LenOutOfRangeError{
|
||||
Min: 1,
|
||||
Max: 1000,
|
||||
Current: 1001,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERR: malformed base64",
|
||||
args: args{
|
||||
cursor: "112345",
|
||||
},
|
||||
expectedErr: domain.ValidationError{
|
||||
Model: "ListVillagesParams",
|
||||
Field: "cursor",
|
||||
Err: domain.ErrInvalidCursor,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
params := domain.NewListVillagesParams()
|
||||
|
||||
require.ErrorIs(t, params.SetEncodedCursor(tt.args.cursor), tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.expectedCursor, params.Cursor())
|
||||
assert.Equal(t, tt.args.cursor, params.Cursor().Encode())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListVillagesParams_SetLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -466,51 +569,46 @@ func TestListVillagesParams_SetLimit(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestListVillagesParams_SetOffset(t *testing.T) {
|
||||
func TestNewListVillagesResult(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
offset int
|
||||
villages := domain.Villages{
|
||||
domaintest.NewVillage(t),
|
||||
domaintest.NewVillage(t),
|
||||
domaintest.NewVillage(t),
|
||||
}
|
||||
next := domaintest.NewVillage(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: "ListVillagesParams",
|
||||
Field: "offset",
|
||||
Err: domain.MinGreaterEqualError{
|
||||
Min: 0,
|
||||
Current: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Run("OK: with next", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
res, err := domain.NewListVillagesResult(villages, next)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, villages, res.Villages())
|
||||
assert.Equal(t, villages[0].ID(), res.Self().ID())
|
||||
assert.Equal(t, villages[0].ServerKey(), res.Self().ServerKey())
|
||||
assert.Equal(t, next.ID(), res.Next().ID())
|
||||
assert.Equal(t, next.ServerKey(), res.Next().ServerKey())
|
||||
})
|
||||
|
||||
params := domain.NewListVillagesParams()
|
||||
t.Run("OK: without next", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.ErrorIs(t, params.SetOffset(tt.args.offset), tt.expectedErr)
|
||||
if tt.expectedErr != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.args.offset, params.Offset())
|
||||
})
|
||||
}
|
||||
res, err := domain.NewListVillagesResult(villages, domain.Village{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, villages, res.Villages())
|
||||
assert.Equal(t, villages[0].ID(), res.Self().ID())
|
||||
assert.Equal(t, villages[0].ServerKey(), res.Self().ServerKey())
|
||||
assert.True(t, res.Next().IsZero())
|
||||
})
|
||||
|
||||
t.Run("OK: 0 villages", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
res, err := domain.NewListVillagesResult(nil, domain.Village{})
|
||||
require.NoError(t, err)
|
||||
assert.Zero(t, res.Villages())
|
||||
assert.True(t, res.Self().IsZero())
|
||||
assert.True(t, res.Next().IsZero())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -421,19 +421,16 @@ func TestDataSync(t *testing.T) {
|
|||
allVillages := make(domain.Villages, 0, len(expectedVillages))
|
||||
|
||||
for {
|
||||
villages, err := villageRepo.List(ctx, listParams)
|
||||
res, err := villageRepo.List(ctx, listParams)
|
||||
require.NoError(collect, err)
|
||||
|
||||
if len(villages) == 0 {
|
||||
allVillages = append(allVillages, res.Villages()...)
|
||||
|
||||
if res.Next().IsZero() {
|
||||
break
|
||||
}
|
||||
|
||||
allVillages = append(allVillages, villages...)
|
||||
|
||||
require.NoError(collect, listParams.SetIDGT(domain.NullInt{
|
||||
V: villages[len(villages)-1].ID(),
|
||||
Valid: true,
|
||||
}))
|
||||
require.NoError(collect, listParams.SetCursor(res.Next()))
|
||||
}
|
||||
|
||||
if !assert.Len(collect, allVillages, len(expectedVillages)) {
|
||||
|
|
Loading…
Reference in New Issue