refactor: tribe change - cursor pagination #25
|
@ -52,17 +52,22 @@ func (repo *TribeChangeBunRepository) Create(ctx context.Context, params ...doma
|
||||||
func (repo *TribeChangeBunRepository) List(
|
func (repo *TribeChangeBunRepository) List(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
params domain.ListTribeChangesParams,
|
params domain.ListTribeChangesParams,
|
||||||
) (domain.TribeChanges, error) {
|
) (domain.ListTribeChangesResult, error) {
|
||||||
var tribeChanges bunmodel.TribeChanges
|
var tribeChanges bunmodel.TribeChanges
|
||||||
|
|
||||||
if err := repo.db.NewSelect().
|
if err := repo.db.NewSelect().
|
||||||
Model(&tribeChanges).
|
Model(&tribeChanges).
|
||||||
Apply(listTribeChangesParamsApplier{params: params}.apply).
|
Apply(listTribeChangesParamsApplier{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 tribe changes from the db: %w", err)
|
return domain.ListTribeChangesResult{}, fmt.Errorf("couldn't select tribe changes from the db: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tribeChanges.ToDomain()
|
converted, err := tribeChanges.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return domain.ListTribeChangesResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.NewListTribeChangesResult(separateListResultAndNext(converted, params.Limit()))
|
||||||
}
|
}
|
||||||
|
|
||||||
type listTribeChangesParamsApplier struct {
|
type listTribeChangesParamsApplier struct {
|
||||||
|
@ -94,5 +99,57 @@ func (a listTribeChangesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.Limit(a.params.Limit()).Offset(a.params.Offset())
|
return q.Limit(a.params.Limit() + 1).Apply(a.applyCursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a listTribeChangesParamsApplier) 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.TribeChangeSortIDASC:
|
||||||
|
el.value = cursor.ID()
|
||||||
|
el.unique = true
|
||||||
|
el.column = "tc.id"
|
||||||
|
el.direction = sortDirectionASC
|
||||||
|
case domain.TribeChangeSortIDDESC:
|
||||||
|
el.value = cursor.ID()
|
||||||
|
el.unique = true
|
||||||
|
el.column = "tc.id"
|
||||||
|
el.direction = sortDirectionDESC
|
||||||
|
case domain.TribeChangeSortServerKeyASC:
|
||||||
|
el.value = cursor.ServerKey()
|
||||||
|
el.column = "tc.server_key"
|
||||||
|
el.direction = sortDirectionASC
|
||||||
|
case domain.TribeChangeSortServerKeyDESC:
|
||||||
|
el.value = cursor.ServerKey()
|
||||||
|
el.column = "tc.server_key"
|
||||||
|
el.direction = sortDirectionDESC
|
||||||
|
case domain.TribeChangeSortCreatedAtASC:
|
||||||
|
el.value = cursor.CreatedAt()
|
||||||
|
el.column = "tc.created_at"
|
||||||
|
el.direction = sortDirectionASC
|
||||||
|
case domain.TribeChangeSortCreatedAtDESC:
|
||||||
|
el.value = cursor.CreatedAt()
|
||||||
|
el.column = "tc.created_at"
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,12 +431,9 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
|
|
||||||
require.NoError(t, params.SetSort([]domain.EnnoblementSort{domain.EnnoblementSortIDASC}))
|
require.NoError(t, params.SetSort([]domain.EnnoblementSort{domain.EnnoblementSortIDASC}))
|
||||||
require.NoError(t, params.SetServerKeys([]string{res.Ennoblements()[1].ServerKey()}))
|
require.NoError(t, params.SetServerKeys([]string{res.Ennoblements()[1].ServerKey()}))
|
||||||
require.NoError(
|
cursor, err := res.Ennoblements()[1].ToCursor()
|
||||||
t,
|
require.NoError(t, err)
|
||||||
params.SetCursor(domaintest.NewEnnoblementCursor(t, func(cfg *domaintest.EnnoblementCursorConfig) {
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
cfg.ID = res.Ennoblements()[1].ID()
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -475,13 +472,9 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Ennoblements()), 2)
|
require.Greater(t, len(res.Ennoblements()), 2)
|
||||||
|
|
||||||
require.NoError(
|
cursor, err := res.Ennoblements()[1].ToCursor()
|
||||||
t,
|
require.NoError(t, err)
|
||||||
params.SetCursor(domaintest.NewEnnoblementCursor(t, func(cfg *domaintest.EnnoblementCursorConfig) {
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
cfg.ID = res.Ennoblements()[1].ID()
|
|
||||||
cfg.ServerKey = res.Ennoblements()[1].ServerKey()
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -520,13 +513,9 @@ func testEnnoblementRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Ennoblements()), 2)
|
require.Greater(t, len(res.Ennoblements()), 2)
|
||||||
|
|
||||||
require.NoError(
|
cursor, err := res.Ennoblements()[1].ToCursor()
|
||||||
t,
|
require.NoError(t, err)
|
||||||
params.SetCursor(domaintest.NewEnnoblementCursor(t, func(cfg *domaintest.EnnoblementCursorConfig) {
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
cfg.ID = res.Ennoblements()[1].ID()
|
|
||||||
cfg.ServerKey = res.Ennoblements()[1].ServerKey()
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
|
|
@ -531,9 +531,9 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
|
|
||||||
require.NoError(t, params.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}))
|
require.NoError(t, params.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}))
|
||||||
require.NoError(t, params.SetServerKeys([]string{res.Players()[1].ServerKey()}))
|
require.NoError(t, params.SetServerKeys([]string{res.Players()[1].ServerKey()}))
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewPlayerCursor(t, func(cfg *domaintest.PlayerCursorConfig) {
|
cursor, err := res.Players()[1].ToCursor()
|
||||||
cfg.ID = res.Players()[1].ID()
|
require.NoError(t, err)
|
||||||
})))
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -572,10 +572,9 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Players()), 2)
|
require.Greater(t, len(res.Players()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewPlayerCursor(t, func(cfg *domaintest.PlayerCursorConfig) {
|
cursor, err := res.Players()[1].ToCursor()
|
||||||
cfg.ID = res.Players()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Players()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -614,10 +613,9 @@ func testPlayerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Players()), 2)
|
require.Greater(t, len(res.Players()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewPlayerCursor(t, func(cfg *domaintest.PlayerCursorConfig) {
|
cursor, err := res.Players()[1].ToCursor()
|
||||||
cfg.ID = res.Players()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Players()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
|
|
@ -369,9 +369,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Servers()), 2)
|
require.Greater(t, len(res.Servers()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewServerCursor(t, func(cfg *domaintest.ServerCursorConfig) {
|
cursor, err := res.Servers()[1].ToCursor()
|
||||||
cfg.Key = res.Servers()[1].Key()
|
require.NoError(t, err)
|
||||||
})))
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -403,10 +403,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Servers()), 2)
|
require.Greater(t, len(res.Servers()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewServerCursor(t, func(cfg *domaintest.ServerCursorConfig) {
|
cursor, err := res.Servers()[1].ToCursor()
|
||||||
cfg.Key = res.Servers()[1].Key()
|
require.NoError(t, err)
|
||||||
cfg.Open = res.Servers()[1].Open()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -446,10 +445,9 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Servers()), 2)
|
require.Greater(t, len(res.Servers()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewServerCursor(t, func(cfg *domaintest.ServerCursorConfig) {
|
cursor, err := res.Servers()[1].ToCursor()
|
||||||
cfg.Key = res.Servers()[1].Key()
|
require.NoError(t, err)
|
||||||
cfg.Open = res.Servers()[1].Open()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
|
|
@ -56,7 +56,7 @@ type ennoblementRepository interface {
|
||||||
|
|
||||||
type tribeChangeRepository interface {
|
type tribeChangeRepository interface {
|
||||||
Create(ctx context.Context, params ...domain.CreateTribeChangeParams) error
|
Create(ctx context.Context, params ...domain.CreateTribeChangeParams) error
|
||||||
List(ctx context.Context, params domain.ListTribeChangesParams) (domain.TribeChanges, error)
|
List(ctx context.Context, params domain.ListTribeChangesParams) (domain.ListTribeChangesResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type tribeSnapshotRepository interface {
|
type tribeSnapshotRepository interface {
|
||||||
|
|
|
@ -32,8 +32,9 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
listParams := domain.NewListTribeChangesParams()
|
listParams := domain.NewListTribeChangesParams()
|
||||||
require.NoError(t, listParams.SetServerKeys([]string{params[0].ServerKey()}))
|
require.NoError(t, listParams.SetServerKeys([]string{params[0].ServerKey()}))
|
||||||
|
|
||||||
tribeChanges, err := repos.tribeChange.List(ctx, listParams)
|
res, err := repos.tribeChange.List(ctx, listParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
tribeChanges := res.TribeChanges()
|
||||||
for i, p := range params {
|
for i, p := range params {
|
||||||
idx := slices.IndexFunc(tribeChanges, func(tc domain.TribeChange) bool {
|
idx := slices.IndexFunc(tribeChanges, func(tc domain.TribeChange) bool {
|
||||||
return tc.ServerKey() == p.ServerKey() &&
|
return tc.ServerKey() == p.ServerKey() &&
|
||||||
|
@ -60,8 +61,9 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
listParams := domain.NewListTribeChangesParams()
|
listParams := domain.NewListTribeChangesParams()
|
||||||
require.NoError(t, listParams.SetServerKeys([]string{params[0].ServerKey()}))
|
require.NoError(t, listParams.SetServerKeys([]string{params[0].ServerKey()}))
|
||||||
|
|
||||||
tribeChanges, err := repos.tribeChange.List(ctx, listParams)
|
res, err := repos.tribeChange.List(ctx, listParams)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
tribeChanges := res.TribeChanges()
|
||||||
|
|
||||||
m := make(map[string][]int)
|
m := make(map[string][]int)
|
||||||
|
|
||||||
|
@ -139,17 +141,11 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
|
|
||||||
repos := newRepos(t)
|
repos := newRepos(t)
|
||||||
|
|
||||||
tribeChanges, listTribeChangesErr := repos.tribeChange.List(ctx, domain.NewListTribeChangesParams())
|
|
||||||
require.NoError(t, listTribeChangesErr)
|
|
||||||
require.NotEmpty(t, tribeChanges)
|
|
||||||
randTribeChange := tribeChanges[0]
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
params func(t *testing.T) domain.ListTribeChangesParams
|
params func(t *testing.T) domain.ListTribeChangesParams
|
||||||
assertTribeChanges func(t *testing.T, params domain.ListTribeChangesParams, tribeChanges domain.TribeChanges)
|
assertResult func(t *testing.T, params domain.ListTribeChangesParams, res domain.ListTribeChangesResult)
|
||||||
assertError func(t *testing.T, err error)
|
assertError func(t *testing.T, err error)
|
||||||
assertTotal func(t *testing.T, params domain.ListTribeChangesParams, total int)
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "OK: default params",
|
name: "OK: default params",
|
||||||
|
@ -157,8 +153,9 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return domain.NewListTribeChangesParams()
|
return domain.NewListTribeChangesParams()
|
||||||
},
|
},
|
||||||
assertTribeChanges: func(t *testing.T, _ domain.ListTribeChangesParams, tribeChanges domain.TribeChanges) {
|
assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
tribeChanges := res.TribeChanges()
|
||||||
assert.NotEmpty(t, len(tribeChanges))
|
assert.NotEmpty(t, len(tribeChanges))
|
||||||
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
||||||
return cmp.Or(
|
return cmp.Or(
|
||||||
|
@ -167,15 +164,13 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
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.ListTribeChangesParams, total int) {
|
|
||||||
t.Helper()
|
|
||||||
assert.NotEmpty(t, total)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OK: sort=[serverKey DESC, createdAt DESC]",
|
name: "OK: sort=[serverKey DESC, createdAt DESC]",
|
||||||
|
@ -188,8 +183,9 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
}))
|
}))
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
assertTribeChanges: func(t *testing.T, _ domain.ListTribeChangesParams, tribeChanges domain.TribeChanges) {
|
assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
tribeChanges := res.TribeChanges()
|
||||||
assert.NotEmpty(t, len(tribeChanges))
|
assert.NotEmpty(t, len(tribeChanges))
|
||||||
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
||||||
return cmp.Or(
|
return cmp.Or(
|
||||||
|
@ -202,10 +198,6 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
},
|
},
|
||||||
assertTotal: func(t *testing.T, _ domain.ListTribeChangesParams, total int) {
|
|
||||||
t.Helper()
|
|
||||||
assert.NotEmpty(t, total)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OK: sort=[id ASC]",
|
name: "OK: sort=[id ASC]",
|
||||||
|
@ -217,8 +209,9 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
}))
|
}))
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
assertTribeChanges: func(t *testing.T, _ domain.ListTribeChangesParams, tribeChanges domain.TribeChanges) {
|
assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
tribeChanges := res.TribeChanges()
|
||||||
assert.NotEmpty(t, len(tribeChanges))
|
assert.NotEmpty(t, len(tribeChanges))
|
||||||
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
||||||
return cmp.Compare(a.ID(), b.ID())
|
return cmp.Compare(a.ID(), b.ID())
|
||||||
|
@ -228,10 +221,6 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
},
|
},
|
||||||
assertTotal: func(t *testing.T, _ domain.ListTribeChangesParams, total int) {
|
|
||||||
t.Helper()
|
|
||||||
assert.NotEmpty(t, total)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OK: sort=[id DESC]",
|
name: "OK: sort=[id DESC]",
|
||||||
|
@ -243,8 +232,9 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
}))
|
}))
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
assertTribeChanges: func(t *testing.T, _ domain.ListTribeChangesParams, tribeChanges domain.TribeChanges) {
|
assertResult: func(t *testing.T, _ domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
tribeChanges := res.TribeChanges()
|
||||||
assert.NotEmpty(t, len(tribeChanges))
|
assert.NotEmpty(t, len(tribeChanges))
|
||||||
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
assert.True(t, slices.IsSortedFunc(tribeChanges, func(a, b domain.TribeChange) int {
|
||||||
return cmp.Compare(a.ID(), b.ID()) * -1
|
return cmp.Compare(a.ID(), b.ID()) * -1
|
||||||
|
@ -254,25 +244,31 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
},
|
},
|
||||||
assertTotal: func(t *testing.T, _ domain.ListTribeChangesParams, total int) {
|
|
||||||
t.Helper()
|
|
||||||
assert.NotEmpty(t, total)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: fmt.Sprintf("OK: serverKeys=[%s]", randTribeChange.ServerKey()),
|
name: "OK: serverKeys",
|
||||||
params: func(t *testing.T) domain.ListTribeChangesParams {
|
params: func(t *testing.T) domain.ListTribeChangesParams {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
params := domain.NewListTribeChangesParams()
|
params := domain.NewListTribeChangesParams()
|
||||||
require.NoError(t, params.SetServerKeys([]string{randTribeChange.ServerKey()}))
|
|
||||||
|
res, err := repos.tribeChange.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, res.TribeChanges())
|
||||||
|
randTC := res.TribeChanges()[0]
|
||||||
|
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{randTC.ServerKey()}))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
assertTribeChanges: func(t *testing.T, params domain.ListTribeChangesParams, tribeChanges domain.TribeChanges) {
|
assertResult: func(t *testing.T, params domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
serverKeys := params.ServerKeys()
|
serverKeys := params.ServerKeys()
|
||||||
|
|
||||||
for _, tc := range tribeChanges {
|
tcs := res.TribeChanges()
|
||||||
|
assert.NotZero(t, tcs)
|
||||||
|
for _, tc := range tcs {
|
||||||
assert.True(t, slices.Contains(serverKeys, tc.ServerKey()))
|
assert.True(t, slices.Contains(serverKeys, tc.ServerKey()))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -280,31 +276,145 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
},
|
},
|
||||||
assertTotal: func(t *testing.T, _ domain.ListTribeChangesParams, total int) {
|
|
||||||
t.Helper()
|
|
||||||
assert.NotEmpty(t, total)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OK: offset=1 limit=2",
|
name: "OK: cursor serverKeys sort=[id ASC]",
|
||||||
params: func(t *testing.T) domain.ListTribeChangesParams {
|
params: func(t *testing.T) domain.ListTribeChangesParams {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
params := domain.NewListTribeChangesParams()
|
params := domain.NewListTribeChangesParams()
|
||||||
require.NoError(t, params.SetOffset(1))
|
|
||||||
require.NoError(t, params.SetLimit(2))
|
res, err := repos.tribeChange.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Greater(t, len(res.TribeChanges()), 2)
|
||||||
|
|
||||||
|
require.NoError(t, params.SetSort([]domain.TribeChangeSort{domain.TribeChangeSortIDASC}))
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{res.TribeChanges()[1].ServerKey()}))
|
||||||
|
cursor, err := res.TribeChanges()[1].ToCursor()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
assertTribeChanges: func(t *testing.T, params domain.ListTribeChangesParams, tribeChanges domain.TribeChanges) {
|
assertResult: func(t *testing.T, params domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.Len(t, tribeChanges, params.Limit())
|
|
||||||
|
serverKeys := params.ServerKeys()
|
||||||
|
|
||||||
|
tcs := res.TribeChanges()
|
||||||
|
assert.NotEmpty(t, len(tcs))
|
||||||
|
for _, tc := range tcs {
|
||||||
|
assert.GreaterOrEqual(t, tc.ID(), params.Cursor().ID())
|
||||||
|
assert.True(t, slices.Contains(serverKeys, tc.ServerKey()))
|
||||||
|
}
|
||||||
|
assert.True(t, slices.IsSortedFunc(tcs, func(a, b domain.TribeChange) int {
|
||||||
|
return cmp.Compare(a.ID(), b.ID())
|
||||||
|
}))
|
||||||
},
|
},
|
||||||
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.ListTribeChangesParams, total int) {
|
},
|
||||||
|
{
|
||||||
|
name: "OK: cursor sort=[serverKey ASC, id ASC]",
|
||||||
|
params: func(t *testing.T) domain.ListTribeChangesParams {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.NotEmpty(t, total)
|
|
||||||
|
params := domain.NewListTribeChangesParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.TribeChangeSort{
|
||||||
|
domain.TribeChangeSortServerKeyASC,
|
||||||
|
domain.TribeChangeSortIDASC,
|
||||||
|
}))
|
||||||
|
|
||||||
|
res, err := repos.tribeChange.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Greater(t, len(res.TribeChanges()), 2)
|
||||||
|
|
||||||
|
cursor, err := res.TribeChanges()[1].ToCursor()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
|
t.Helper()
|
||||||
|
tcs := res.TribeChanges()
|
||||||
|
assert.NotEmpty(t, len(tcs))
|
||||||
|
assert.True(t, slices.IsSortedFunc(tcs, func(a, b domain.TribeChange) int {
|
||||||
|
return cmp.Or(
|
||||||
|
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||||
|
cmp.Compare(a.ID(), b.ID()),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
assert.GreaterOrEqual(t, tcs[0].ID(), params.Cursor().ID())
|
||||||
|
for _, tc := range tcs {
|
||||||
|
assert.GreaterOrEqual(t, tc.ServerKey(), params.Cursor().ServerKey())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: cursor sort=[serverKey DESC, id DESC]",
|
||||||
|
params: func(t *testing.T) domain.ListTribeChangesParams {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
params := domain.NewListTribeChangesParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.TribeChangeSort{
|
||||||
|
domain.TribeChangeSortServerKeyDESC,
|
||||||
|
domain.TribeChangeSortIDDESC,
|
||||||
|
}))
|
||||||
|
|
||||||
|
res, err := repos.tribeChange.List(ctx, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Greater(t, len(res.TribeChanges()), 2)
|
||||||
|
|
||||||
|
cursor, err := res.TribeChanges()[1].ToCursor()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
|
t.Helper()
|
||||||
|
tcs := res.TribeChanges()
|
||||||
|
assert.NotEmpty(t, len(tcs))
|
||||||
|
assert.True(t, slices.IsSortedFunc(tcs, func(a, b domain.TribeChange) int {
|
||||||
|
return cmp.Or(
|
||||||
|
cmp.Compare(a.ServerKey(), b.ServerKey()),
|
||||||
|
cmp.Compare(a.ID(), b.ID()),
|
||||||
|
) * -1
|
||||||
|
}))
|
||||||
|
assert.LessOrEqual(t, tcs[0].ID(), params.Cursor().ID())
|
||||||
|
for _, tc := range tcs {
|
||||||
|
assert.LessOrEqual(t, tc.ServerKey(), params.Cursor().ServerKey())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: limit=2",
|
||||||
|
params: func(t *testing.T) domain.ListTribeChangesParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListTribeChangesParams()
|
||||||
|
require.NoError(t, params.SetLimit(2))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertResult: func(t *testing.T, params domain.ListTribeChangesParams, res domain.ListTribeChangesResult) {
|
||||||
|
t.Helper()
|
||||||
|
assert.Len(t, res.TribeChanges(), 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)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -317,7 +427,7 @@ func testTribeChangeRepository(t *testing.T, newRepos func(t *testing.T) reposit
|
||||||
|
|
||||||
res, err := repos.tribeChange.List(ctx, params)
|
res, err := repos.tribeChange.List(ctx, params)
|
||||||
tt.assertError(t, err)
|
tt.assertError(t, err)
|
||||||
tt.assertTribeChanges(t, params, res)
|
tt.assertResult(t, params, res)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -556,9 +556,9 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
||||||
|
|
||||||
require.NoError(t, params.SetSort([]domain.TribeSort{domain.TribeSortIDASC}))
|
require.NoError(t, params.SetSort([]domain.TribeSort{domain.TribeSortIDASC}))
|
||||||
require.NoError(t, params.SetServerKeys([]string{res.Tribes()[1].ServerKey()}))
|
require.NoError(t, params.SetServerKeys([]string{res.Tribes()[1].ServerKey()}))
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewTribeCursor(t, func(cfg *domaintest.TribeCursorConfig) {
|
cursor, err := res.Tribes()[1].ToCursor()
|
||||||
cfg.ID = res.Tribes()[1].ID()
|
require.NoError(t, err)
|
||||||
})))
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -597,10 +597,9 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Tribes()), 2)
|
require.Greater(t, len(res.Tribes()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewTribeCursor(t, func(cfg *domaintest.TribeCursorConfig) {
|
cursor, err := res.Tribes()[1].ToCursor()
|
||||||
cfg.ID = res.Tribes()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Tribes()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -639,10 +638,9 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Tribes()), 2)
|
require.Greater(t, len(res.Tribes()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewTribeCursor(t, func(cfg *domaintest.TribeCursorConfig) {
|
cursor, err := res.Tribes()[1].ToCursor()
|
||||||
cfg.ID = res.Tribes()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Tribes()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -682,11 +680,9 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Tribes()), 2)
|
require.Greater(t, len(res.Tribes()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewTribeCursor(t, func(cfg *domaintest.TribeCursorConfig) {
|
cursor, err := res.Tribes()[1].ToCursor()
|
||||||
cfg.ID = res.Tribes()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Tribes()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
cfg.Points = res.Tribes()[1].Points()
|
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -728,11 +724,9 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Tribes()), 2)
|
require.Greater(t, len(res.Tribes()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewTribeCursor(t, func(cfg *domaintest.TribeCursorConfig) {
|
cursor, err := res.Tribes()[1].ToCursor()
|
||||||
cfg.ID = res.Tribes()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Tribes()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
cfg.DeletedAt = res.Tribes()[1].DeletedAt()
|
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -80,9 +79,9 @@ func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Versions()), 2)
|
require.Greater(t, len(res.Versions()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewVersionCursor(t, func(cfg *domaintest.VersionCursorConfig) {
|
cursor, err := res.Versions()[1].ToCursor()
|
||||||
cfg.Code = res.Versions()[1].Code()
|
require.NoError(t, err)
|
||||||
})))
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -113,9 +112,9 @@ func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Versions()), 2)
|
require.Greater(t, len(res.Versions()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewVersionCursor(t, func(cfg *domaintest.VersionCursorConfig) {
|
cursor, err := res.Versions()[1].ToCursor()
|
||||||
cfg.Code = res.Versions()[1].Code()
|
require.NoError(t, err)
|
||||||
})))
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
|
|
@ -331,9 +331,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
|
|
||||||
require.NoError(t, params.SetSort([]domain.VillageSort{domain.VillageSortIDASC}))
|
require.NoError(t, params.SetSort([]domain.VillageSort{domain.VillageSortIDASC}))
|
||||||
require.NoError(t, params.SetServerKeys([]string{res.Villages()[1].ServerKey()}))
|
require.NoError(t, params.SetServerKeys([]string{res.Villages()[1].ServerKey()}))
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewVillageCursor(t, func(cfg *domaintest.VillageCursorConfig) {
|
cursor, err := res.Villages()[1].ToCursor()
|
||||||
cfg.ID = res.Villages()[1].ID()
|
require.NoError(t, err)
|
||||||
})))
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -372,10 +372,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Villages()), 2)
|
require.Greater(t, len(res.Villages()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewVillageCursor(t, func(cfg *domaintest.VillageCursorConfig) {
|
cursor, err := res.Villages()[1].ToCursor()
|
||||||
cfg.ID = res.Villages()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Villages()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
@ -414,10 +413,9 @@ func testVillageRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Greater(t, len(res.Villages()), 2)
|
require.Greater(t, len(res.Villages()), 2)
|
||||||
|
|
||||||
require.NoError(t, params.SetCursor(domaintest.NewVillageCursor(t, func(cfg *domaintest.VillageCursorConfig) {
|
cursor, err := res.Villages()[1].ToCursor()
|
||||||
cfg.ID = res.Villages()[1].ID()
|
require.NoError(t, err)
|
||||||
cfg.ServerKey = res.Villages()[1].ServerKey()
|
require.NoError(t, params.SetCursor(cursor))
|
||||||
})))
|
|
||||||
|
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,9 +4,39 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/brianvoe/gofakeit/v7"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TribeChangeCursorConfig struct {
|
||||||
|
ID int
|
||||||
|
ServerKey string
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTribeChangeCursor(tb TestingTB, opts ...func(cfg *TribeChangeCursorConfig)) domain.TribeChangeCursor {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
cfg := &TribeChangeCursorConfig{
|
||||||
|
ID: RandID(),
|
||||||
|
ServerKey: RandServerKey(),
|
||||||
|
CreatedAt: gofakeit.Date(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
tcc, err := domain.NewTribeChangeCursor(
|
||||||
|
cfg.ID,
|
||||||
|
cfg.ServerKey,
|
||||||
|
cfg.CreatedAt,
|
||||||
|
)
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
return tcc
|
||||||
|
}
|
||||||
|
|
||||||
type TribeChangeConfig struct {
|
type TribeChangeConfig struct {
|
||||||
ID int
|
ID int
|
||||||
ServerKey string
|
ServerKey string
|
||||||
|
|
|
@ -81,6 +81,14 @@ func (tc TribeChange) CreatedAt() time.Time {
|
||||||
return tc.createdAt
|
return tc.createdAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc TribeChange) ToCursor() (TribeChangeCursor, error) {
|
||||||
|
return NewTribeChangeCursor(tc.id, tc.serverKey, tc.createdAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TribeChange) IsZero() bool {
|
||||||
|
return tc == TribeChange{}
|
||||||
|
}
|
||||||
|
|
||||||
type TribeChanges []TribeChange
|
type TribeChanges []TribeChange
|
||||||
|
|
||||||
type CreateTribeChangeParams struct {
|
type CreateTribeChangeParams struct {
|
||||||
|
@ -241,11 +249,105 @@ func (s TribeChangeSort) String() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TribeChangeCursor struct {
|
||||||
|
id int
|
||||||
|
serverKey string
|
||||||
|
createdAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
const tribeChangeCursorModelName = "TribeChangeCursor"
|
||||||
|
|
||||||
|
func NewTribeChangeCursor(id int, serverKey string, createdAt time.Time) (TribeChangeCursor, error) {
|
||||||
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
||||||
|
return TribeChangeCursor{}, ValidationError{
|
||||||
|
Model: tribeChangeCursorModelName,
|
||||||
|
Field: "id",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateServerKey(serverKey); err != nil {
|
||||||
|
return TribeChangeCursor{}, ValidationError{
|
||||||
|
Model: tribeChangeCursorModelName,
|
||||||
|
Field: "serverKey",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TribeChangeCursor{
|
||||||
|
id: id,
|
||||||
|
serverKey: serverKey,
|
||||||
|
createdAt: createdAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func decodeTribeChangeCursor(encoded string) (TribeChangeCursor, error) {
|
||||||
|
m, err := decodeCursor(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return TribeChangeCursor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := m.int("id")
|
||||||
|
if err != nil {
|
||||||
|
return TribeChangeCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
serverKey, err := m.string("serverKey")
|
||||||
|
if err != nil {
|
||||||
|
return TribeChangeCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
createdAt, err := m.time("createdAt")
|
||||||
|
if err != nil {
|
||||||
|
return TribeChangeCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
ec, err := NewTribeChangeCursor(
|
||||||
|
id,
|
||||||
|
serverKey,
|
||||||
|
createdAt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return TribeChangeCursor{}, ErrInvalidCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
return ec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tcc TribeChangeCursor) ID() int {
|
||||||
|
return tcc.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tcc TribeChangeCursor) ServerKey() string {
|
||||||
|
return tcc.serverKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tcc TribeChangeCursor) CreatedAt() time.Time {
|
||||||
|
return tcc.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tcc TribeChangeCursor) IsZero() bool {
|
||||||
|
return tcc == TribeChangeCursor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tcc TribeChangeCursor) Encode() string {
|
||||||
|
if tcc.IsZero() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeCursor([]keyValuePair{
|
||||||
|
{"id", tcc.id},
|
||||||
|
{"serverKey", tcc.serverKey},
|
||||||
|
{"createdAt", tcc.createdAt},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type ListTribeChangesParams struct {
|
type ListTribeChangesParams struct {
|
||||||
serverKeys []string
|
serverKeys []string
|
||||||
sort []TribeChangeSort
|
sort []TribeChangeSort
|
||||||
|
cursor TribeChangeCursor
|
||||||
limit int
|
limit int
|
||||||
offset int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -308,6 +410,30 @@ func (params *ListTribeChangesParams) SetSort(sort []TribeChangeSort) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (params *ListTribeChangesParams) Cursor() TribeChangeCursor {
|
||||||
|
return params.cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribeChangesParams) SetCursor(cursor TribeChangeCursor) error {
|
||||||
|
params.cursor = cursor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribeChangesParams) SetEncodedCursor(encoded string) error {
|
||||||
|
decoded, err := decodeTribeChangeCursor(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listTribeChangesParamsModelName,
|
||||||
|
Field: "cursor",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.cursor = decoded
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (params *ListTribeChangesParams) Limit() int {
|
func (params *ListTribeChangesParams) Limit() int {
|
||||||
return params.limit
|
return params.limit
|
||||||
}
|
}
|
||||||
|
@ -326,20 +452,53 @@ func (params *ListTribeChangesParams) SetLimit(limit int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListTribeChangesParams) Offset() int {
|
type ListTribeChangesResult struct {
|
||||||
return params.offset
|
tribeChanges TribeChanges
|
||||||
|
self TribeChangeCursor
|
||||||
|
next TribeChangeCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListTribeChangesParams) SetOffset(offset int) error {
|
const listTribeChangesResultModelName = "ListTribeChangesResult"
|
||||||
if err := validateIntInRange(offset, 0, math.MaxInt); err != nil {
|
|
||||||
return ValidationError{
|
func NewListTribeChangesResult(ennoblements TribeChanges, next TribeChange) (ListTribeChangesResult, error) {
|
||||||
Model: listTribeChangesParamsModelName,
|
var err error
|
||||||
Field: "offset",
|
res := ListTribeChangesResult{
|
||||||
|
tribeChanges: ennoblements,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ennoblements) > 0 {
|
||||||
|
res.self, err = ennoblements[0].ToCursor()
|
||||||
|
if err != nil {
|
||||||
|
return ListTribeChangesResult{}, ValidationError{
|
||||||
|
Model: listTribeChangesResultModelName,
|
||||||
|
Field: "self",
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
params.offset = offset
|
|
||||||
|
if !next.IsZero() {
|
||||||
return nil
|
res.next, err = next.ToCursor()
|
||||||
|
if err != nil {
|
||||||
|
return ListTribeChangesResult{}, ValidationError{
|
||||||
|
Model: listTribeChangesResultModelName,
|
||||||
|
Field: "next",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListTribeChangesResult) TribeChanges() TribeChanges {
|
||||||
|
return res.tribeChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListTribeChangesResult) Self() TribeChangeCursor {
|
||||||
|
return res.self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (res ListTribeChangesResult) Next() TribeChangeCursor {
|
||||||
|
return res.next
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
@ -279,6 +281,87 @@ func TestTribeChangeSort_IsInConflict(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewTribeChangeCursor(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
validTribeChangeCursor := domaintest.NewTribeChangeCursor(t)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
id int
|
||||||
|
serverKey string
|
||||||
|
createdAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
id: validTribeChangeCursor.ID(),
|
||||||
|
serverKey: validTribeChangeCursor.ServerKey(),
|
||||||
|
createdAt: validTribeChangeCursor.CreatedAt(),
|
||||||
|
},
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: id < 1",
|
||||||
|
args: args{
|
||||||
|
id: 0,
|
||||||
|
serverKey: validTribeChangeCursor.ServerKey(),
|
||||||
|
createdAt: validTribeChangeCursor.CreatedAt(),
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "TribeChangeCursor",
|
||||||
|
Field: "id",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 1,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, serverKeyTest := range newServerKeyValidationTests() {
|
||||||
|
tests = append(tests, test{
|
||||||
|
name: serverKeyTest.name,
|
||||||
|
args: args{
|
||||||
|
id: validTribeChangeCursor.ID(),
|
||||||
|
serverKey: serverKeyTest.key,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "TribeChangeCursor",
|
||||||
|
Field: "serverKey",
|
||||||
|
Err: serverKeyTest.expectedErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tcc, err := domain.NewTribeChangeCursor(
|
||||||
|
tt.args.id,
|
||||||
|
tt.args.serverKey,
|
||||||
|
tt.args.createdAt,
|
||||||
|
)
|
||||||
|
require.ErrorIs(t, err, tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.id, tcc.ID())
|
||||||
|
assert.Equal(t, tt.args.serverKey, tcc.ServerKey())
|
||||||
|
assert.Equal(t, tt.args.createdAt, tcc.CreatedAt())
|
||||||
|
assert.NotEmpty(t, tcc.Encode())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListTribeChangesParams_SetServerKeys(t *testing.T) {
|
func TestListTribeChangesParams_SetServerKeys(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -422,6 +505,86 @@ func TestListTribeChangesParams_SetSort(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListTribeChangesParams_SetEncodedCursor(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
validCursor := domaintest.NewTribeChangeCursor(t)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
cursor string
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedCursor domain.TribeChangeCursor
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
cursor: validCursor.Encode(),
|
||||||
|
},
|
||||||
|
expectedCursor: validCursor,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(cursor) < 1",
|
||||||
|
args: args{
|
||||||
|
cursor: "",
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribeChangesParams",
|
||||||
|
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: "ListTribeChangesParams",
|
||||||
|
Field: "cursor",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 1000,
|
||||||
|
Current: 1001,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: malformed base64",
|
||||||
|
args: args{
|
||||||
|
cursor: "112345",
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribeChangesParams",
|
||||||
|
Field: "cursor",
|
||||||
|
Err: domain.ErrInvalidCursor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListTribeChangesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetEncodedCursor(tt.args.cursor), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.cursor, params.Cursor().Encode())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListTribeChangesParams_SetLimit(t *testing.T) {
|
func TestListTribeChangesParams_SetLimit(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -485,51 +648,49 @@ func TestListTribeChangesParams_SetLimit(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListTribeChangesParams_SetOffset(t *testing.T) {
|
func TestNewListTribeChangesResult(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
type args struct {
|
tcs := domain.TribeChanges{
|
||||||
offset int
|
domaintest.NewTribeChange(t),
|
||||||
|
domaintest.NewTribeChange(t),
|
||||||
|
domaintest.NewTribeChange(t),
|
||||||
}
|
}
|
||||||
|
next := domaintest.NewTribeChange(t)
|
||||||
|
|
||||||
tests := []struct {
|
t.Run("OK: with next", func(t *testing.T) {
|
||||||
name string
|
|
||||||
args args
|
|
||||||
expectedErr error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "OK",
|
|
||||||
args: args{
|
|
||||||
offset: 100,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ERR: offset < 0",
|
|
||||||
args: args{
|
|
||||||
offset: -1,
|
|
||||||
},
|
|
||||||
expectedErr: domain.ValidationError{
|
|
||||||
Model: "ListTribeChangesParams",
|
|
||||||
Field: "offset",
|
|
||||||
Err: domain.MinGreaterEqualError{
|
|
||||||
Min: 0,
|
|
||||||
Current: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
params := domain.NewListTribeChangesParams()
|
res, err := domain.NewListTribeChangesResult(tcs, next)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tcs, res.TribeChanges())
|
||||||
|
assert.Equal(t, tcs[0].ID(), res.Self().ID())
|
||||||
|
assert.Equal(t, tcs[0].ServerKey(), res.Self().ServerKey())
|
||||||
|
assert.Equal(t, tcs[0].CreatedAt(), res.Self().CreatedAt())
|
||||||
|
assert.Equal(t, next.ID(), res.Next().ID())
|
||||||
|
assert.Equal(t, next.ServerKey(), res.Next().ServerKey())
|
||||||
|
assert.Equal(t, next.CreatedAt(), res.Next().CreatedAt())
|
||||||
|
})
|
||||||
|
|
||||||
require.ErrorIs(t, params.SetOffset(tt.args.offset), tt.expectedErr)
|
t.Run("OK: without next", func(t *testing.T) {
|
||||||
if tt.expectedErr != nil {
|
t.Parallel()
|
||||||
return
|
|
||||||
}
|
res, err := domain.NewListTribeChangesResult(tcs, domain.TribeChange{})
|
||||||
assert.Equal(t, tt.args.offset, params.Offset())
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tcs, res.TribeChanges())
|
||||||
|
assert.Equal(t, tcs[0].ID(), res.Self().ID())
|
||||||
|
assert.Equal(t, tcs[0].ServerKey(), res.Self().ServerKey())
|
||||||
|
assert.Equal(t, tcs[0].CreatedAt(), res.Self().CreatedAt())
|
||||||
|
assert.True(t, res.Next().IsZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: 0 tribe changes", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewListTribeChangesResult(nil, domain.TribeChange{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Zero(t, res.TribeChanges())
|
||||||
|
assert.True(t, res.Self().IsZero())
|
||||||
|
assert.True(t, res.Next().IsZero())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -467,16 +467,16 @@ func TestDataSync(t *testing.T) {
|
||||||
allTribeChanges := make(domain.TribeChanges, 0, len(expectedTribeChanges))
|
allTribeChanges := make(domain.TribeChanges, 0, len(expectedTribeChanges))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
tcs, err := tribeChangeRepo.List(ctx, listParams)
|
res, err := tribeChangeRepo.List(ctx, listParams)
|
||||||
require.NoError(collect, err)
|
require.NoError(collect, err)
|
||||||
|
|
||||||
if len(tcs) == 0 {
|
allTribeChanges = append(allTribeChanges, res.TribeChanges()...)
|
||||||
|
|
||||||
|
if res.Next().IsZero() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
allTribeChanges = append(allTribeChanges, tcs...)
|
require.NoError(collect, listParams.SetCursor(res.Next()))
|
||||||
|
|
||||||
require.NoError(collect, listParams.SetOffset(listParams.Offset()+domain.TribeChangeListMaxLimit))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Len(collect, allTribeChanges, len(expectedTribeChanges)) {
|
if !assert.Len(collect, allTribeChanges, len(expectedTribeChanges)) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user