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
|
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
|
var villages bunmodel.Villages
|
||||||
|
|
||||||
if err := repo.db.NewSelect().
|
if err := repo.db.NewSelect().
|
||||||
Model(&villages).
|
Model(&villages).
|
||||||
Apply(listVillagesParamsApplier{params: params}.apply).
|
Apply(listVillagesParamsApplier{params: params}.apply).
|
||||||
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
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 {
|
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))
|
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 {
|
if serverKeys := a.params.ServerKeys(); len(serverKeys) > 0 {
|
||||||
q = q.Where("village.server_key IN (?)", bun.In(serverKeys))
|
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)
|
res, err := repos.player.List(ctx, params)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, len(res.Players()))
|
require.NotEmpty(t, res.Players())
|
||||||
randPlayer := res.Players()[0]
|
randPlayer := res.Players()[0]
|
||||||
|
|
||||||
require.NoError(t, params.SetIDs([]int{randPlayer.ID()}))
|
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)
|
res, err := repos.player.List(ctx, params)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, len(res.Players()))
|
require.NotEmpty(t, res.Players())
|
||||||
randPlayer := res.Players()[0]
|
randPlayer := res.Players()[0]
|
||||||
|
|
||||||
require.NoError(t, params.SetNames([]string{randPlayer.Name()}))
|
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()
|
players := res.Players()
|
||||||
assert.NotEmpty(t, len(players))
|
assert.NotEmpty(t, len(players))
|
||||||
for _, p := range res.Players() {
|
for _, p := range players {
|
||||||
assert.GreaterOrEqual(t, p.ID(), params.Cursor().ID())
|
assert.GreaterOrEqual(t, p.ID(), params.Cursor().ID())
|
||||||
assert.True(t, slices.Contains(serverKeys, p.ServerKey()))
|
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())
|
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())
|
assert.GreaterOrEqual(t, p.ServerKey(), params.Cursor().ServerKey())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -632,7 +632,7 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
) * -1
|
) * -1
|
||||||
}))
|
}))
|
||||||
assert.LessOrEqual(t, players[0].ID(), params.Cursor().ID())
|
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())
|
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) {
|
assertResult: func(t *testing.T, params domain.ListPlayersParams, res domain.ListPlayersResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.Len(t, res.Players(), params.Limit())
|
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) {
|
assertError: func(t *testing.T, err error) {
|
||||||
t.Helper()
|
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) {
|
assertResult: func(t *testing.T, params domain.ListServersParams, res domain.ListServersResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.Len(t, res.Servers(), params.Limit())
|
assert.Len(t, res.Servers(), params.Limit())
|
||||||
|
assert.False(t, res.Self().IsZero())
|
||||||
assert.False(t, res.Next().IsZero())
|
assert.False(t, res.Next().IsZero())
|
||||||
},
|
},
|
||||||
assertError: func(t *testing.T, err error) {
|
assertError: func(t *testing.T, err error) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ type playerRepository interface {
|
||||||
|
|
||||||
type villageRepository interface {
|
type villageRepository interface {
|
||||||
CreateOrUpdate(ctx context.Context, params ...domain.CreateVillageParams) error
|
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
|
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)
|
res, err := repos.tribe.List(ctx, params)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, len(res.Tribes()))
|
require.NotEmpty(t, res.Tribes())
|
||||||
randTribe := res.Tribes()[0]
|
randTribe := res.Tribes()[0]
|
||||||
|
|
||||||
require.NoError(t, params.SetIDs([]int{randTribe.ID()}))
|
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)
|
res, err := repos.tribe.List(ctx, params)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, len(res.Tribes()))
|
require.NotEmpty(t, res.Tribes())
|
||||||
randTribe := res.Tribes()[0]
|
randTribe := res.Tribes()[0]
|
||||||
|
|
||||||
require.NoError(t, params.SetTags([]string{randTribe.Tag()}))
|
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())
|
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())
|
assert.GreaterOrEqual(t, tr.ServerKey(), params.Cursor().ServerKey())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -657,7 +657,7 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
||||||
) * -1
|
) * -1
|
||||||
}))
|
}))
|
||||||
assert.LessOrEqual(t, tribes[0].ID(), params.Cursor().ID())
|
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())
|
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].ID(), params.Cursor().ID())
|
||||||
assert.GreaterOrEqual(t, tribes[0].ServerKey(), params.Cursor().ServerKey())
|
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())
|
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].ID(), params.Cursor().ID())
|
||||||
assert.GreaterOrEqual(t, tribes[0].ServerKey(), params.Cursor().ServerKey())
|
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())
|
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) {
|
assertResult: func(t *testing.T, params domain.ListTribesParams, res domain.ListTribesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.Len(t, res.Tribes(), params.Limit())
|
assert.Len(t, res.Tribes(), params.Limit())
|
||||||
|
assert.False(t, res.Self().IsZero())
|
||||||
assert.False(t, res.Next().IsZero())
|
assert.False(t, res.Next().IsZero())
|
||||||
},
|
},
|
||||||
assertError: func(t *testing.T, err error) {
|
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) {
|
assertResult: func(t *testing.T, params domain.ListVersionsParams, res domain.ListVersionsResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.Len(t, res.Versions(), params.Limit())
|
assert.Len(t, res.Versions(), params.Limit())
|
||||||
|
assert.False(t, res.Self().IsZero())
|
||||||
assert.False(t, res.Next().IsZero())
|
assert.False(t, res.Next().IsZero())
|
||||||
},
|
},
|
||||||
assertError: func(t *testing.T, err error) {
|
assertError: func(t *testing.T, err error) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package adapter_test
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"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.SetIDs(ids))
|
||||||
require.NoError(t, listParams.SetServerKeys([]string{params[0].ServerKey()}))
|
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)
|
require.NoError(t, err)
|
||||||
|
villages := res.Villages()
|
||||||
assert.Len(t, villages, len(params))
|
assert.Len(t, villages, len(params))
|
||||||
for i, p := range params {
|
for i, p := range params {
|
||||||
idx := slices.IndexFunc(villages, func(village domain.Village) bool {
|
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)
|
repos := newRepos(t)
|
||||||
|
|
||||||
villages, listVillagesErr := repos.village.List(ctx, domain.NewListVillagesParams())
|
|
||||||
require.NoError(t, listVillagesErr)
|
|
||||||
require.NotEmpty(t, villages)
|
|
||||||
randVillage := villages[0]
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
params func(t *testing.T) domain.ListVillagesParams
|
params func(t *testing.T) domain.ListVillagesParams
|
||||||
assertVillages func(t *testing.T, params domain.ListVillagesParams, villages domain.Villages)
|
assertResult func(t *testing.T, params domain.ListVillagesParams, res domain.ListVillagesResult)
|
||||||
assertError func(t *testing.T, err error)
|
assertError func(t *testing.T, err error)
|
||||||
assertTotal func(t *testing.T, params domain.ListVillagesParams, total int)
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "OK: default params",
|
name: "OK: default params",
|
||||||
|
@ -115,8 +109,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return domain.NewListVillagesParams()
|
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()
|
t.Helper()
|
||||||
|
villages := res.Villages()
|
||||||
assert.NotEmpty(t, len(villages))
|
assert.NotEmpty(t, len(villages))
|
||||||
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
||||||
return cmp.Or(
|
return cmp.Or(
|
||||||
|
@ -124,15 +119,13 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
cmp.Compare(a.ID(), b.ID()),
|
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) {
|
assertError: func(t *testing.T, err error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
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]",
|
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}))
|
require.NoError(t, params.SetSort([]domain.VillageSort{domain.VillageSortServerKeyDESC, domain.VillageSortIDDESC}))
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
assertVillages: func(t *testing.T, _ domain.ListVillagesParams, villages domain.Villages) {
|
assertResult: func(t *testing.T, _ domain.ListVillagesParams, res domain.ListVillagesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
villages := res.Villages()
|
||||||
assert.NotEmpty(t, len(villages))
|
assert.NotEmpty(t, len(villages))
|
||||||
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
assert.True(t, slices.IsSortedFunc(villages, func(a, b domain.Village) int {
|
||||||
return cmp.Or(
|
return cmp.Or(
|
||||||
|
@ -156,26 +150,32 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
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 {
|
params: func(t *testing.T) domain.ListVillagesParams {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
params := domain.NewListVillagesParams()
|
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.SetIDs([]int{randVillage.ID()}))
|
||||||
require.NoError(t, params.SetServerKeys([]string{randVillage.ServerKey()}))
|
require.NoError(t, params.SetServerKeys([]string{randVillage.ServerKey()}))
|
||||||
|
|
||||||
return params
|
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()
|
t.Helper()
|
||||||
|
|
||||||
ids := params.IDs()
|
ids := params.IDs()
|
||||||
serverKeys := params.ServerKeys()
|
serverKeys := params.ServerKeys()
|
||||||
|
|
||||||
|
villages := res.Villages()
|
||||||
|
assert.Len(t, villages, len(ids))
|
||||||
for _, v := range villages {
|
for _, v := range villages {
|
||||||
assert.True(t, slices.Contains(ids, v.ID()))
|
assert.True(t, slices.Contains(ids, v.ID()))
|
||||||
assert.True(t, slices.Contains(serverKeys, v.ServerKey()))
|
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()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
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()
|
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 {
|
params: func(t *testing.T) domain.ListVillagesParams {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
params := domain.NewListVillagesParams()
|
params := domain.NewListVillagesParams()
|
||||||
require.NoError(t, params.SetIDGT(domain.NullInt{
|
require.NoError(t, params.SetSort([]domain.VillageSort{
|
||||||
V: randVillage.ID(),
|
domain.VillageSortServerKeyASC,
|
||||||
Valid: true,
|
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
|
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()
|
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 {
|
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) {
|
assertError: func(t *testing.T, err error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
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 {
|
params: func(t *testing.T) domain.ListVillagesParams {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
params := domain.NewListVillagesParams()
|
params := domain.NewListVillagesParams()
|
||||||
require.NoError(t, params.SetOffset(1))
|
require.NoError(t, params.SetSort([]domain.VillageSort{
|
||||||
require.NoError(t, params.SetLimit(2))
|
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
|
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()
|
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) {
|
assertError: func(t *testing.T, err error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
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()
|
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)
|
res, err := repos.village.List(ctx, params)
|
||||||
tt.assertError(t, err)
|
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()
|
listVillagesParams := domain.NewListVillagesParams()
|
||||||
require.NoError(t, listVillagesParams.SetServerKeys(serverKeys))
|
require.NoError(t, listVillagesParams.SetServerKeys(serverKeys))
|
||||||
|
|
||||||
villagesBeforeDelete, err := repos.village.List(ctx, listVillagesParams)
|
res, err := repos.village.List(ctx, listVillagesParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
villagesBeforeDelete := res.Villages()
|
||||||
|
|
||||||
var serverKey string
|
var serverKey string
|
||||||
var ids []int
|
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...))
|
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)
|
require.NoError(t, err)
|
||||||
|
villagesAfterDelete := res.Villages()
|
||||||
assert.Len(t, villagesAfterDelete, len(villagesBeforeDelete)-len(idsToDelete))
|
assert.Len(t, villagesAfterDelete, len(villagesBeforeDelete)-len(idsToDelete))
|
||||||
for _, v := range villagesAfterDelete {
|
for _, v := range villagesAfterDelete {
|
||||||
if v.ServerKey() == serverKey && slices.Contains(ids, v.ID()) {
|
if v.ServerKey() == serverKey && slices.Contains(ids, v.ID()) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
type VillageRepository interface {
|
type VillageRepository interface {
|
||||||
CreateOrUpdate(ctx context.Context, params ...domain.CreateVillageParams) error
|
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
|
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
|
var toDelete []int
|
||||||
|
|
||||||
for {
|
for {
|
||||||
storedVillages, err := svc.repo.List(ctx, listParams)
|
res, err := svc.repo.List(ctx, listParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(storedVillages) == 0 {
|
toDelete = append(toDelete, res.Villages().Delete(serverKey, villages)...)
|
||||||
|
|
||||||
|
if res.Next().IsZero() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
toDelete = append(toDelete, storedVillages.Delete(serverKey, villages)...)
|
if err = listParams.SetCursor(res.Next()); err != nil {
|
||||||
|
|
||||||
if err = listParams.SetIDGT(domain.NullInt{
|
|
||||||
V: storedVillages[len(storedVillages)-1].ID(),
|
|
||||||
Valid: true,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return svc.repo.Delete(ctx, serverKey, toDelete...)
|
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"
|
"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 {
|
type VillageConfig struct {
|
||||||
ID int
|
ID int
|
||||||
ServerKey string
|
ServerKey string
|
||||||
|
|
|
@ -412,6 +412,33 @@ const (
|
||||||
PlayerSortDeletedAtDESC
|
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).
|
// IsInConflict returns true if two sorts can't be used together (e.g. PlayerSortIDASC and PlayerSortIDDESC).
|
||||||
func (s PlayerSort) IsInConflict(s2 PlayerSort) bool {
|
func (s PlayerSort) IsInConflict(s2 PlayerSort) bool {
|
||||||
ss := []PlayerSort{s, s2}
|
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 {
|
type PlayerCursor struct {
|
||||||
id int
|
id int
|
||||||
serverKey string
|
serverKey string
|
||||||
|
|
|
@ -510,7 +510,7 @@ func TestNewPlayerCursor(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
tc, err := domain.NewPlayerCursor(
|
pc, err := domain.NewPlayerCursor(
|
||||||
tt.args.id,
|
tt.args.id,
|
||||||
tt.args.serverKey,
|
tt.args.serverKey,
|
||||||
tt.args.odScoreAtt,
|
tt.args.odScoreAtt,
|
||||||
|
@ -524,15 +524,15 @@ func TestNewPlayerCursor(t *testing.T) {
|
||||||
if tt.expectedErr != nil {
|
if tt.expectedErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.Equal(t, tt.args.id, tc.ID())
|
assert.Equal(t, tt.args.id, pc.ID())
|
||||||
assert.Equal(t, tt.args.serverKey, tc.ServerKey())
|
assert.Equal(t, tt.args.serverKey, pc.ServerKey())
|
||||||
assert.Equal(t, tt.args.odScoreAtt, tc.ODScoreAtt())
|
assert.Equal(t, tt.args.odScoreAtt, pc.ODScoreAtt())
|
||||||
assert.Equal(t, tt.args.odScoreDef, tc.ODScoreDef())
|
assert.Equal(t, tt.args.odScoreDef, pc.ODScoreDef())
|
||||||
assert.Equal(t, tt.args.odScoreSup, tc.ODScoreSup())
|
assert.Equal(t, tt.args.odScoreSup, pc.ODScoreSup())
|
||||||
assert.Equal(t, tt.args.odScoreTotal, tc.ODScoreTotal())
|
assert.Equal(t, tt.args.odScoreTotal, pc.ODScoreTotal())
|
||||||
assert.Equal(t, tt.args.points, tc.Points())
|
assert.Equal(t, tt.args.points, pc.Points())
|
||||||
assert.Equal(t, tt.args.deletedAt, tc.DeletedAt())
|
assert.Equal(t, tt.args.deletedAt, pc.DeletedAt())
|
||||||
assert.NotEmpty(t, tc.Encode())
|
assert.NotEmpty(t, pc.Encode())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -450,6 +450,33 @@ const (
|
||||||
TribeSortDeletedAtDESC
|
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).
|
// IsInConflict returns true if two sorts can't be used together (e.g. TribeSortIDASC and TribeSortIDDESC).
|
||||||
func (s TribeSort) IsInConflict(s2 TribeSort) bool {
|
func (s TribeSort) IsInConflict(s2 TribeSort) bool {
|
||||||
ss := []TribeSort{s, s2}
|
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 {
|
type TribeCursor struct {
|
||||||
id int
|
id int
|
||||||
serverKey string
|
serverKey string
|
||||||
|
|
|
@ -31,7 +31,7 @@ type keyValuePair struct {
|
||||||
value any
|
value any
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kvp keyValuePair) valueString() string {
|
func (kvp keyValuePair) valueToString() string {
|
||||||
switch v := kvp.value.(type) {
|
switch v := kvp.value.(type) {
|
||||||
case string:
|
case string:
|
||||||
return v
|
return v
|
||||||
|
@ -59,7 +59,7 @@ func encodeCursor(kvps []keyValuePair) string {
|
||||||
n := len(cursorSeparator) * (len(kvps) - 1)
|
n := len(cursorSeparator) * (len(kvps) - 1)
|
||||||
|
|
||||||
for i, kvp := range kvps {
|
for i, kvp := range kvps {
|
||||||
value := kvp.valueString()
|
value := kvp.valueToString()
|
||||||
n += len(kvp.key) + len(value) + len(cursorKeyValueSeparator)
|
n += len(kvp.key) + len(value) + len(cursorKeyValueSeparator)
|
||||||
values[i] = value
|
values[i] = value
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,10 @@ func (v Village) Base() BaseVillage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v Village) IsZero() bool {
|
||||||
|
return v == Village{}
|
||||||
|
}
|
||||||
|
|
||||||
type Villages []Village
|
type Villages []Village
|
||||||
|
|
||||||
// Delete finds all villages with the given serverKey that are not in the given slice with active villages
|
// 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 {
|
type ListVillagesParams struct {
|
||||||
ids []int
|
ids []int
|
||||||
idGT NullInt
|
|
||||||
serverKeys []string
|
serverKeys []string
|
||||||
sort []VillageSort
|
sort []VillageSort
|
||||||
|
cursor VillageCursor
|
||||||
limit int
|
limit int
|
||||||
offset int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -293,26 +377,6 @@ func (params *ListVillagesParams) SetIDs(ids []int) error {
|
||||||
return nil
|
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 {
|
func (params *ListVillagesParams) ServerKeys() []string {
|
||||||
return params.serverKeys
|
return params.serverKeys
|
||||||
}
|
}
|
||||||
|
@ -357,6 +421,30 @@ func (params *ListVillagesParams) SetSort(sort []VillageSort) error {
|
||||||
return nil
|
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 {
|
func (params *ListVillagesParams) Limit() int {
|
||||||
return params.limit
|
return params.limit
|
||||||
}
|
}
|
||||||
|
@ -375,20 +463,53 @@ func (params *ListVillagesParams) SetLimit(limit int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListVillagesParams) Offset() int {
|
type ListVillagesResult struct {
|
||||||
return params.offset
|
villages Villages
|
||||||
|
self VillageCursor
|
||||||
|
next VillageCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListVillagesParams) SetOffset(offset int) error {
|
const listVillagesResultModelName = "ListVillagesResult"
|
||||||
if err := validateIntInRange(offset, 0, math.MaxInt); err != nil {
|
|
||||||
return ValidationError{
|
func NewListVillagesResult(villages Villages, next Village) (ListVillagesResult, error) {
|
||||||
Model: listVillagesParamsModelName,
|
var err error
|
||||||
Field: "offset",
|
res := ListVillagesResult{
|
||||||
Err: err,
|
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"
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
||||||
|
"github.com/brianvoe/gofakeit/v7"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func TestListVillagesParams_SetIDs(t *testing.T) {
|
||||||
t.Parallel()
|
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) {
|
func TestListVillagesParams_SetServerKeys(t *testing.T) {
|
||||||
t.Parallel()
|
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) {
|
func TestListVillagesParams_SetLimit(t *testing.T) {
|
||||||
t.Parallel()
|
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()
|
t.Parallel()
|
||||||
|
|
||||||
type args struct {
|
villages := domain.Villages{
|
||||||
offset int
|
domaintest.NewVillage(t),
|
||||||
|
domaintest.NewVillage(t),
|
||||||
|
domaintest.NewVillage(t),
|
||||||
}
|
}
|
||||||
|
next := domaintest.NewVillage(t)
|
||||||
|
|
||||||
tests := []struct {
|
t.Run("OK: with next", func(t *testing.T) {
|
||||||
name string
|
t.Parallel()
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
res, err := domain.NewListVillagesResult(villages, next)
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
require.NoError(t, err)
|
||||||
t.Parallel()
|
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)
|
res, err := domain.NewListVillagesResult(villages, domain.Village{})
|
||||||
if tt.expectedErr != nil {
|
require.NoError(t, err)
|
||||||
return
|
assert.Equal(t, villages, res.Villages())
|
||||||
}
|
assert.Equal(t, villages[0].ID(), res.Self().ID())
|
||||||
assert.Equal(t, tt.args.offset, params.Offset())
|
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))
|
allVillages := make(domain.Villages, 0, len(expectedVillages))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
villages, err := villageRepo.List(ctx, listParams)
|
res, err := villageRepo.List(ctx, listParams)
|
||||||
require.NoError(collect, err)
|
require.NoError(collect, err)
|
||||||
|
|
||||||
if len(villages) == 0 {
|
allVillages = append(allVillages, res.Villages()...)
|
||||||
|
|
||||||
|
if res.Next().IsZero() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
allVillages = append(allVillages, villages...)
|
require.NoError(collect, listParams.SetCursor(res.Next()))
|
||||||
|
|
||||||
require.NoError(collect, listParams.SetIDGT(domain.NullInt{
|
|
||||||
V: villages[len(villages)-1].ID(),
|
|
||||||
Valid: true,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Len(collect, allVillages, len(expectedVillages)) {
|
if !assert.Len(collect, allVillages, len(expectedVillages)) {
|
||||||
|
|
Loading…
Reference in New Issue