feat: tribe - sort by most points (#55)
All checks were successful
ci/woodpecker/push/govulncheck Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/tag/deployment Pipeline was successful
ci/woodpecker/cron/govulncheck Pipeline was successful

Reviewed-on: #55
This commit is contained in:
Dawid Wysokiński 2024-06-02 11:51:38 +00:00
parent 4db3fee03f
commit 51a6270812
8 changed files with 158 additions and 0 deletions

View File

@ -1794,6 +1794,8 @@ components:
- odScoreTotal:DESC
- points:ASC
- points:DESC
- mostPoints:ASC
- mostPoints:DESC
- dominance:ASC
- dominance:DESC
- deletedAt:ASC

View File

@ -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:

View File

@ -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 {

View File

@ -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,
)

View File

@ -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},
})

View File

@ -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())

View File

@ -22,6 +22,8 @@ var apiTribeSortAllowedValues = []domain.TribeSort{
domain.TribeSortODScoreTotalDESC,
domain.TribeSortPointsASC,
domain.TribeSortPointsDESC,
domain.TribeSortMostPointsASC,
domain.TribeSortMostPointsDESC,
domain.TribeSortDominanceASC,
domain.TribeSortDominanceDESC,
domain.TribeSortDeletedAtASC,

View File

@ -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) {