From 51a627081248ddcfc0f404748c723c3347f4b6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Wysoki=C5=84ski?= Date: Sun, 2 Jun 2024 11:51:38 +0000 Subject: [PATCH] feat: tribe - sort by most points (#55) Reviewed-on: https://gitea.dwysokinski.me/twhelp/core/pulls/55 --- api/openapi3.yml | 2 + internal/adapter/repository_bun_tribe.go | 7 +++ internal/adapter/repository_tribe_test.go | 50 ++++++++++++++++++++ internal/domain/domaintest/tribe.go | 3 ++ internal/domain/tribe.go | 29 ++++++++++++ internal/domain/tribe_test.go | 39 +++++++++++++++ internal/port/handler_http_api_tribe.go | 2 + internal/port/handler_http_api_tribe_test.go | 26 ++++++++++ 8 files changed, 158 insertions(+) diff --git a/api/openapi3.yml b/api/openapi3.yml index b391bae..637f6b8 100644 --- a/api/openapi3.yml +++ b/api/openapi3.yml @@ -1794,6 +1794,8 @@ components: - odScoreTotal:DESC - points:ASC - points:DESC + - mostPoints:ASC + - mostPoints:DESC - dominance:ASC - dominance:DESC - deletedAt:ASC diff --git a/internal/adapter/repository_bun_tribe.go b/internal/adapter/repository_bun_tribe.go index 5f2b458..9375ab9 100644 --- a/internal/adapter/repository_bun_tribe.go +++ b/internal/adapter/repository_bun_tribe.go @@ -241,6 +241,9 @@ func (a listTribesParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQuer case domain.TribeSortPointsASC, domain.TribeSortPointsDESC: el.value = cursor.Points() + case domain.TribeSortMostPointsASC, + domain.TribeSortMostPointsDESC: + el.value = cursor.MostPoints() case domain.TribeSortDominanceASC, domain.TribeSortDominanceDESC: el.value = cursor.Dominance() @@ -286,6 +289,10 @@ func (a listTribesParamsApplier) sortToColumnAndDirection( return "tribe.points", sortDirectionASC, nil case domain.TribeSortPointsDESC: return "tribe.points", sortDirectionDESC, nil + case domain.TribeSortMostPointsASC: + return "tribe.most_points", sortDirectionASC, nil + case domain.TribeSortMostPointsDESC: + return "tribe.most_points", sortDirectionDESC, nil case domain.TribeSortDominanceASC: return "tribe.dominance", sortDirectionASC, nil case domain.TribeSortDominanceDESC: diff --git a/internal/adapter/repository_tribe_test.go b/internal/adapter/repository_tribe_test.go index 14aa32f..2fe131e 100644 --- a/internal/adapter/repository_tribe_test.go +++ b/internal/adapter/repository_tribe_test.go @@ -339,6 +339,56 @@ func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) })) }, }, + { + name: "OK: sort=[mostPoints ASC, serverKey ASC, id ASC]", + params: func(t *testing.T) domain.ListTribesParams { + t.Helper() + params := domain.NewListTribesParams() + require.NoError(t, params.SetSort([]domain.TribeSort{ + domain.TribeSortMostPointsASC, + domain.TribeSortServerKeyASC, + domain.TribeSortIDASC, + })) + return params + }, + assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { + t.Helper() + tribes := res.Tribes() + assert.NotEmpty(t, tribes) + assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { + return cmp.Or( + cmp.Compare(a.MostPoints(), b.MostPoints()), + cmp.Compare(a.ServerKey(), b.ServerKey()), + cmp.Compare(a.ID(), b.ID()), + ) + })) + }, + }, + { + name: "OK: sort=[mostPoints DESC, serverKey ASC, id ASC]", + params: func(t *testing.T) domain.ListTribesParams { + t.Helper() + params := domain.NewListTribesParams() + require.NoError(t, params.SetSort([]domain.TribeSort{ + domain.TribeSortMostPointsDESC, + domain.TribeSortServerKeyASC, + domain.TribeSortIDASC, + })) + return params + }, + assertResult: func(t *testing.T, _ domain.ListTribesParams, res domain.ListTribesResult) { + t.Helper() + tribes := res.Tribes() + assert.NotEmpty(t, tribes) + assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int { + return cmp.Or( + cmp.Compare(a.MostPoints(), b.MostPoints())*-1, + cmp.Compare(a.ServerKey(), b.ServerKey()), + cmp.Compare(a.ID(), b.ID()), + ) + })) + }, + }, { name: "OK: sort=[deletedAt ASC, serverKey ASC, id ASC]", params: func(t *testing.T) domain.ListTribesParams { diff --git a/internal/domain/domaintest/tribe.go b/internal/domain/domaintest/tribe.go index 8d6c8d8..c7c8255 100644 --- a/internal/domain/domaintest/tribe.go +++ b/internal/domain/domaintest/tribe.go @@ -20,6 +20,7 @@ type TribeCursorConfig struct { ODScoreDef int ODScoreTotal int Points int + MostPoints int Dominance float64 DeletedAt time.Time } @@ -34,6 +35,7 @@ func NewTribeCursor(tb TestingTB, opts ...func(cfg *TribeCursorConfig)) domain.T ODScoreDef: gofakeit.IntRange(0, math.MaxInt), ODScoreTotal: gofakeit.IntRange(0, math.MaxInt), Points: gofakeit.IntRange(0, math.MaxInt), + MostPoints: gofakeit.IntRange(0, math.MaxInt), Dominance: gofakeit.Float64Range(0.1, 99.9), DeletedAt: time.Time{}, } @@ -49,6 +51,7 @@ func NewTribeCursor(tb TestingTB, opts ...func(cfg *TribeCursorConfig)) domain.T cfg.ODScoreDef, cfg.ODScoreTotal, cfg.Points, + cfg.MostPoints, cfg.Dominance, cfg.DeletedAt, ) diff --git a/internal/domain/tribe.go b/internal/domain/tribe.go index 4fa9c4d..7325f8a 100644 --- a/internal/domain/tribe.go +++ b/internal/domain/tribe.go @@ -207,6 +207,7 @@ func (t Tribe) ToCursor() (TribeCursor, error) { t.od.scoreDef, t.od.scoreTotal, t.points, + t.mostPoints, t.dominance, t.deletedAt, ) @@ -472,6 +473,8 @@ const ( TribeSortODScoreTotalDESC TribeSortPointsASC TribeSortPointsDESC + TribeSortMostPointsASC + TribeSortMostPointsDESC TribeSortDominanceASC TribeSortDominanceDESC TribeSortDeletedAtASC @@ -510,6 +513,10 @@ func (s TribeSort) String() string { return "points:ASC" case TribeSortPointsDESC: return "points:DESC" + case TribeSortMostPointsASC: + return "mostPoints:ASC" + case TribeSortMostPointsDESC: + return "mostPoints:DESC" case TribeSortDominanceASC: return "dominance:ASC" case TribeSortDominanceDESC: @@ -530,6 +537,7 @@ type TribeCursor struct { odScoreDef int odScoreTotal int points int + mostPoints int dominance float64 deletedAt time.Time } @@ -543,6 +551,7 @@ func NewTribeCursor( odScoreDef int, odScoreTotal int, points int, + mostPoints int, dominance float64, deletedAt time.Time, ) (TribeCursor, error) { @@ -594,6 +603,14 @@ func NewTribeCursor( } } + if err := validateIntInRange(mostPoints, 0, math.MaxInt); err != nil { + return TribeCursor{}, ValidationError{ + Model: tribeCursorModelName, + Field: "mostPoints", + Err: err, + } + } + return TribeCursor{ id: id, serverKey: serverKey, @@ -601,6 +618,7 @@ func NewTribeCursor( odScoreDef: odScoreDef, odScoreTotal: odScoreTotal, points: points, + mostPoints: mostPoints, dominance: dominance, deletedAt: deletedAt, }, nil @@ -643,6 +661,11 @@ func decodeTribeCursor(encoded string) (TribeCursor, error) { return TribeCursor{}, ErrInvalidCursor } + mostPoints, err := m.int("mostPoints") + if err != nil { + return TribeCursor{}, ErrInvalidCursor + } + dominance, err := m.float64("dominance") if err != nil { return TribeCursor{}, ErrInvalidCursor @@ -660,6 +683,7 @@ func decodeTribeCursor(encoded string) (TribeCursor, error) { odScoreDef, odScoreTotal, points, + mostPoints, dominance, deletedAt, ) @@ -694,6 +718,10 @@ func (tc TribeCursor) Points() int { return tc.points } +func (tc TribeCursor) MostPoints() int { + return tc.mostPoints +} + func (tc TribeCursor) Dominance() float64 { return tc.dominance } @@ -718,6 +746,7 @@ func (tc TribeCursor) Encode() string { {"odScoreDef", tc.odScoreDef}, {"odScoreTotal", tc.odScoreTotal}, {"points", tc.points}, + {"mostPoints", tc.mostPoints}, {"dominance", tc.dominance}, {"deletedAt", tc.deletedAt}, }) diff --git a/internal/domain/tribe_test.go b/internal/domain/tribe_test.go index d23ca75..71e4c03 100644 --- a/internal/domain/tribe_test.go +++ b/internal/domain/tribe_test.go @@ -255,6 +255,13 @@ func TestTribeSort_IsInConflict(t *testing.T) { }, expectedRes: true, }, + { + name: "OK: mostPoints:ASC mostPoints:DESC", + args: args{ + sorts: [2]domain.TribeSort{domain.TribeSortMostPointsASC, domain.TribeSortMostPointsDESC}, + }, + expectedRes: true, + }, { name: "OK: dominance:ASC dominance:DESC", args: args{ @@ -292,6 +299,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef int odScoreTotal int points int + mostPoints int dominance float64 deletedAt time.Time } @@ -312,6 +320,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef: validTribeCursor.ODScoreDef(), odScoreTotal: validTribeCursor.ODScoreTotal(), points: validTribeCursor.Points(), + mostPoints: validTribeCursor.MostPoints(), dominance: validTribeCursor.Dominance(), deletedAt: validTribeCursor.DeletedAt(), }, @@ -326,6 +335,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef: validTribeCursor.ODScoreDef(), odScoreTotal: validTribeCursor.ODScoreTotal(), points: validTribeCursor.Points(), + mostPoints: validTribeCursor.MostPoints(), dominance: validTribeCursor.Dominance(), deletedAt: validTribeCursor.DeletedAt(), }, @@ -347,6 +357,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef: validTribeCursor.ODScoreDef(), odScoreTotal: validTribeCursor.ODScoreTotal(), points: validTribeCursor.Points(), + mostPoints: validTribeCursor.MostPoints(), dominance: validTribeCursor.Dominance(), deletedAt: validTribeCursor.DeletedAt(), }, @@ -368,6 +379,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef: -1, odScoreTotal: validTribeCursor.ODScoreTotal(), points: validTribeCursor.Points(), + mostPoints: validTribeCursor.MostPoints(), dominance: validTribeCursor.Dominance(), deletedAt: validTribeCursor.DeletedAt(), }, @@ -389,6 +401,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef: validTribeCursor.ODScoreDef(), odScoreTotal: -1, points: validTribeCursor.Points(), + mostPoints: validTribeCursor.MostPoints(), dominance: validTribeCursor.Dominance(), deletedAt: validTribeCursor.DeletedAt(), }, @@ -410,6 +423,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef: validTribeCursor.ODScoreDef(), odScoreTotal: validTribeCursor.ODScoreTotal(), points: -1, + mostPoints: validTribeCursor.MostPoints(), dominance: validTribeCursor.Dominance(), deletedAt: validTribeCursor.DeletedAt(), }, @@ -422,6 +436,28 @@ func TestNewTribeCursor(t *testing.T) { }, }, }, + { + name: "ERR: points < 0", + args: args{ + id: validTribeCursor.ID(), + serverKey: validTribeCursor.ServerKey(), + odScoreAtt: validTribeCursor.ODScoreAtt(), + odScoreDef: validTribeCursor.ODScoreDef(), + odScoreTotal: validTribeCursor.ODScoreTotal(), + points: validTribeCursor.Points(), + mostPoints: -1, + dominance: validTribeCursor.Dominance(), + deletedAt: validTribeCursor.DeletedAt(), + }, + expectedErr: domain.ValidationError{ + Model: "TribeCursor", + Field: "mostPoints", + Err: domain.MinGreaterEqualError{ + Min: 0, + Current: -1, + }, + }, + }, } for _, serverKeyTest := range newServerKeyValidationTests() { @@ -434,6 +470,7 @@ func TestNewTribeCursor(t *testing.T) { odScoreDef: validTribeCursor.ODScoreDef(), odScoreTotal: validTribeCursor.ODScoreTotal(), points: validTribeCursor.Points(), + mostPoints: validTribeCursor.MostPoints(), dominance: validTribeCursor.Dominance(), deletedAt: validTribeCursor.DeletedAt(), }, @@ -456,6 +493,7 @@ func TestNewTribeCursor(t *testing.T) { tt.args.odScoreDef, tt.args.odScoreTotal, tt.args.points, + tt.args.mostPoints, tt.args.dominance, tt.args.deletedAt, ) @@ -469,6 +507,7 @@ func TestNewTribeCursor(t *testing.T) { assert.Equal(t, tt.args.odScoreDef, tc.ODScoreDef()) assert.Equal(t, tt.args.odScoreTotal, tc.ODScoreTotal()) assert.Equal(t, tt.args.points, tc.Points()) + assert.Equal(t, tt.args.mostPoints, tc.MostPoints()) assert.InDelta(t, tt.args.dominance, tc.Dominance(), 0.001) assert.Equal(t, tt.args.deletedAt, tc.DeletedAt()) assert.NotEmpty(t, tc.Encode()) diff --git a/internal/port/handler_http_api_tribe.go b/internal/port/handler_http_api_tribe.go index 9630477..aa0b3fa 100644 --- a/internal/port/handler_http_api_tribe.go +++ b/internal/port/handler_http_api_tribe.go @@ -22,6 +22,8 @@ var apiTribeSortAllowedValues = []domain.TribeSort{ domain.TribeSortODScoreTotalDESC, domain.TribeSortPointsASC, domain.TribeSortPointsDESC, + domain.TribeSortMostPointsASC, + domain.TribeSortMostPointsDESC, domain.TribeSortDominanceASC, domain.TribeSortDominanceDESC, domain.TribeSortDeletedAtASC, diff --git a/internal/port/handler_http_api_tribe_test.go b/internal/port/handler_http_api_tribe_test.go index d437ceb..f843a8b 100644 --- a/internal/port/handler_http_api_tribe_test.go +++ b/internal/port/handler_http_api_tribe_test.go @@ -176,6 +176,32 @@ func TestListTribes(t *testing.T) { })) }, }, + { + name: "OK: sort=[mostPoints:DESC]", + reqModifier: func(t *testing.T, req *http.Request) { + t.Helper() + q := req.URL.Query() + q.Set("sort", "mostPoints:DESC") + req.URL.RawQuery = q.Encode() + }, + assertResp: func(t *testing.T, _ *http.Request, resp *http.Response) { + t.Helper() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // body + body := decodeJSON[apimodel.ListTribesResponse](t, resp.Body) + assert.Zero(t, body.Cursor.Next) + assert.NotZero(t, body.Cursor.Self) + assert.NotZero(t, body.Data) + assert.True(t, slices.IsSortedFunc(body.Data, func(a, b apimodel.Tribe) int { + return cmp.Or( + cmp.Compare(a.MostPoints, b.MostPoints)*-1, + cmp.Compare(a.Id, b.Id), + ) + })) + }, + }, { name: "OK: tag", reqModifier: func(t *testing.T, req *http.Request) {