This repository has been archived on 2024-04-06. You can view files and clone it, but cannot push or open issues or pull requests.
core-old/internal/service/tribe_test.go
Dawid Wysokiński 644fd55274
All checks were successful
continuous-integration/drone/push Build is passing
refactor: tribe service - simplify tests
2023-03-01 07:06:10 +01:00

587 lines
15 KiB
Go

package service_test
import (
"context"
"errors"
"fmt"
"testing"
"time"
"gitea.dwysokinski.me/twhelp/core/internal/tw"
"github.com/google/go-cmp/cmp"
"gitea.dwysokinski.me/twhelp/core/internal/service"
"github.com/stretchr/testify/assert"
"gitea.dwysokinski.me/twhelp/core/internal/service/internal/mock"
"gitea.dwysokinski.me/twhelp/core/internal/domain"
)
func TestTribe_Refresh(t *testing.T) {
t.Parallel()
now := time.Now()
tests := []struct {
name string
serverKey string
serverURL string
clientTribes []tw.Tribe
existingTribes []domain.Tribe
expectedCreateTribeParams []domain.CreateTribeParams
expectedDeletedTribes []int64
}{
{
name: "OK: create a new tribe",
serverKey: "pl169",
serverURL: "https://pl169.plemiona.pl",
clientTribes: []tw.Tribe{
{
OpponentsDefeated: tw.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 2,
NumVillages: 3,
Points: 4,
AllPoints: 5,
Rank: 6,
ProfileURL: "profile-999",
},
},
expectedCreateTribeParams: []domain.CreateTribeParams{
{
OpponentsDefeated: domain.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 2,
NumVillages: 3,
Points: 4,
AllPoints: 5,
Rank: 6,
ProfileURL: "profile-999",
BestRank: 6,
BestRankAt: now,
MostPoints: 5,
MostPointsAt: now,
MostVillages: 3,
MostVillagesAt: now,
ServerKey: "pl169",
},
},
},
{
name: "OK: Update existing tribe",
serverKey: "pl169",
serverURL: "https://pl169.plemiona.pl",
clientTribes: []tw.Tribe{
{
OpponentsDefeated: tw.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 2,
NumVillages: 3,
Points: 4,
AllPoints: 5,
Rank: 6,
ProfileURL: "profile-999",
},
},
existingTribes: []domain.Tribe{
{
OpponentsDefeated: domain.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 4,
NumVillages: 5,
Points: 5,
AllPoints: 6,
Rank: 7,
Dominance: 0.1234,
ProfileURL: "profile-999",
BestRank: 1,
BestRankAt: now.Add(-20 * time.Hour),
MostPoints: 33333,
MostPointsAt: now.Add(-20 * time.Hour),
MostVillages: 333,
MostVillagesAt: now.Add(-20 * time.Hour),
ServerKey: "pl169",
CreatedAt: now.Add(-24 * time.Hour),
},
},
expectedCreateTribeParams: []domain.CreateTribeParams{
{
OpponentsDefeated: domain.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 2,
NumVillages: 3,
Points: 4,
AllPoints: 5,
Rank: 6,
ProfileURL: "profile-999",
BestRank: 1, // new.Rank >= old.BestRank, so BestRank remains unchanged
BestRankAt: now.Add(-20 * time.Hour),
MostPoints: 33333, // new.AllPoints <= old.MostPoints, so MostPoints remains unchanged
MostPointsAt: now.Add(-20 * time.Hour),
MostVillages: 333, // new.NumVillages <= old.MostVillages, so MostVillages remains unchanged
MostVillagesAt: now.Add(-20 * time.Hour),
ServerKey: "pl169",
},
},
},
{
name: "OK: Update existing tribe + update best rank/most points/most villages",
serverKey: "pl169",
serverURL: "https://pl169.plemiona.pl",
clientTribes: []tw.Tribe{
{
OpponentsDefeated: tw.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 9,
NumVillages: 335,
Points: 35555,
AllPoints: 35556,
Rank: 1,
ProfileURL: "profile-999",
},
},
existingTribes: []domain.Tribe{
{
OpponentsDefeated: domain.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 8,
NumVillages: 333,
Points: 33332,
AllPoints: 33333,
Rank: 2,
Dominance: 0.1234,
ProfileURL: "profile-999",
BestRank: 2,
BestRankAt: now.Add(-1 * time.Hour),
MostPoints: 33333,
MostPointsAt: now.Add(-1 * time.Hour),
MostVillages: 333,
MostVillagesAt: now.Add(-1 * time.Hour),
ServerKey: "pl169",
CreatedAt: now.Add(-24 * time.Hour),
},
},
expectedCreateTribeParams: []domain.CreateTribeParams{
{
OpponentsDefeated: domain.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 9,
NumVillages: 335,
Points: 35555,
AllPoints: 35556,
Rank: 1,
ProfileURL: "profile-999",
BestRank: 1, // BestRank is updated because of new.Rank < old.BestRank
BestRankAt: now,
MostPoints: 35556, // MostPoints is updated because of new.AllPoints > old.MostPoints
MostPointsAt: now,
MostVillages: 335, // MostVillages is updated because of new.NumVillages > old.MostVillages
MostVillagesAt: now,
ServerKey: "pl169",
},
},
},
{
name: "OK: Delete no longer existing players",
clientTribes: nil,
existingTribes: []domain.Tribe{
{
OpponentsDefeated: domain.OpponentsDefeated{
RankAtt: 1,
ScoreAtt: 2,
RankDef: 3,
ScoreDef: 4,
RankSup: 0,
ScoreSup: 0,
RankTotal: 7,
ScoreTotal: 8,
},
ID: 999,
Name: "name 999",
Tag: "tag 999",
NumMembers: 8,
NumVillages: 333,
Points: 33332,
AllPoints: 33333,
Rank: 2,
Dominance: 0.1234,
ProfileURL: "profile-999",
BestRank: 2,
BestRankAt: now.Add(-1 * time.Hour),
MostPoints: 33333,
MostPointsAt: now.Add(-1 * time.Hour),
MostVillages: 333,
MostVillagesAt: now.Add(-1 * time.Hour),
ServerKey: "pl169",
CreatedAt: now.Add(-24 * time.Hour),
},
},
expectedCreateTribeParams: nil,
expectedDeletedTribes: []int64{999},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
client := &mock.FakeTribeGetter{}
client.GetTribesCalls(func(ctx context.Context, baseURL string) ([]tw.Tribe, error) {
if baseURL != tt.serverURL {
return nil, errors.New("incorrect baseURL")
}
return tt.clientTribes, nil
})
repo := &mock.FakeTribeRepository{}
repo.CreateOrUpdateReturns(nil)
repo.ListReturns(tt.existingTribes, nil)
repo.DeleteCalls(func(ctx context.Context, key string, _ ...int64) error {
if key != tt.serverKey {
return errors.New("incorrect server key")
}
return nil
})
numTribes, err := service.NewTribe(repo, client).Refresh(context.Background(), tt.serverKey, tt.serverURL)
assert.NoError(t, err)
assert.EqualValues(t, len(tt.clientTribes), numTribes)
var argCreateTribeParams []domain.CreateTribeParams
for i := 0; i < repo.CreateOrUpdateCallCount(); i++ {
_, params := repo.CreateOrUpdateArgsForCall(i)
argCreateTribeParams = append(argCreateTribeParams, params...)
}
assert.Empty(t, cmp.Diff(
tt.expectedCreateTribeParams,
argCreateTribeParams,
cmp.Comparer(func(x time.Time, y time.Time) bool {
dt := x.Sub(y)
return dt > -time.Second || dt < time.Second
}),
))
var argDeleteIDs []int64
for i := 0; i < repo.DeleteCallCount(); i++ {
_, _, ids := repo.DeleteArgsForCall(i)
argDeleteIDs = append(argDeleteIDs, ids...)
}
assert.Empty(t, cmp.Diff(tt.expectedDeletedTribes, argDeleteIDs))
})
}
}
func TestTribe_List_ListCount(t *testing.T) {
t.Parallel()
var defaultLimit int32 = 200
defaultSort := []domain.TribeSort{
{
By: domain.TribeSortByID,
Direction: domain.SortDirectionASC,
},
}
tests := []struct {
name string
params domain.ListTribesParams
expectedParams domain.ListTribesParams
expectedErr error
}{
{
name: "OK: default limit/sort",
params: domain.ListTribesParams{},
expectedParams: domain.ListTribesParams{
Sort: defaultSort,
Pagination: domain.Pagination{
Limit: defaultLimit,
},
},
},
{
name: "OK: custom pagination",
params: domain.ListTribesParams{
Pagination: domain.Pagination{
Limit: 199,
Offset: 1,
},
},
expectedParams: domain.ListTribesParams{
Sort: defaultSort,
Pagination: domain.Pagination{
Limit: 199,
Offset: 1,
},
},
},
{
name: "OK: custom sort",
params: domain.ListTribesParams{
Sort: []domain.TribeSort{
{By: domain.TribeSortByPoints, Direction: domain.SortDirectionDESC},
},
},
expectedParams: domain.ListTribesParams{
Sort: []domain.TribeSort{
{By: domain.TribeSortByPoints, Direction: domain.SortDirectionDESC},
},
Pagination: domain.Pagination{
Limit: defaultLimit,
},
},
},
{
name: "ERR: params.Pagination.Limit < 0",
params: domain.ListTribesParams{
Pagination: domain.Pagination{
Limit: -1,
},
},
expectedErr: domain.ValidationError{
Field: "limit",
Err: domain.MinError{
Min: 1,
},
},
},
{
name: "ERR: params.Pagination.Limit > 200",
params: domain.ListTribesParams{
Pagination: domain.Pagination{
Limit: 201,
},
},
expectedErr: domain.ValidationError{
Field: "limit",
Err: domain.MaxError{
Max: 200,
},
},
},
{
name: "ERR: params.Pagination.Offset < 0",
params: domain.ListTribesParams{
Pagination: domain.Pagination{
Offset: -1,
},
},
expectedErr: domain.ValidationError{
Field: "offset",
Err: domain.MinError{
Min: 0,
},
},
},
{
name: "ERR: len(params.Sort) > 3",
params: domain.ListTribesParams{
Sort: []domain.TribeSort{
{
By: domain.TribeSortByID,
Direction: domain.SortDirectionASC,
},
{
By: domain.TribeSortByScoreDef,
Direction: domain.SortDirectionDESC,
},
{
By: domain.TribeSortByScoreTotal,
Direction: domain.SortDirectionASC,
},
{
By: domain.TribeSortByScoreAtt,
Direction: domain.SortDirectionASC,
},
},
},
expectedErr: domain.ValidationError{
Field: "sort",
Err: domain.MaxLengthError{
Max: 3,
},
},
},
{
name: "ERR: len(params.Tags) > 20",
params: domain.ListTribesParams{
Tags: make([]string, 21),
},
expectedErr: domain.ValidationError{
Field: "tag",
Err: domain.MaxLengthError{
Max: 20,
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
repo := &mock.FakeTribeRepository{}
repo.ListCountCalls(func(_ context.Context, params domain.ListTribesParams) ([]domain.Tribe, int64, error) {
if diff := cmp.Diff(params, tt.expectedParams); diff != "" {
return nil, 0, fmt.Errorf("validation failed: %s", diff)
}
return make([]domain.Tribe, params.Pagination.Limit), int64(params.Pagination.Limit), nil
})
repo.ListCalls(func(ctx context.Context, params domain.ListTribesParams) ([]domain.Tribe, error) {
tribes, _, err := repo.ListCount(ctx, params)
return tribes, err
})
svc := service.NewTribe(repo, &mock.FakeTribeGetter{})
tribesListCount, count, err := svc.ListCount(context.Background(), tt.params)
assert.ErrorIs(t, err, tt.expectedErr)
assert.EqualValues(t, tt.expectedParams.Pagination.Limit, count)
assert.Len(t, tribesListCount, int(tt.expectedParams.Pagination.Limit))
tribesList, err := svc.List(context.Background(), tt.params)
assert.ErrorIs(t, err, tt.expectedErr)
assert.Equal(t, tribesListCount, tribesList)
})
}
}
func TestTribe_GetByServerKeyAndID(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
tribe := domain.Tribe{
ID: 123,
ServerKey: "pl151",
}
repo := &mock.FakeTribeRepository{}
repo.ListReturns([]domain.Tribe{tribe}, nil)
repo.ListCalls(func(ctx context.Context, params domain.ListTribesParams) ([]domain.Tribe, error) {
if diff := cmp.Diff(domain.ListTribesParams{
IDs: []int64{tribe.ID},
ServerKeys: []string{tribe.ServerKey},
Pagination: domain.Pagination{
Limit: 1,
},
}, params); diff != "" {
return nil, fmt.Errorf("validation failed: %s", diff)
}
return []domain.Tribe{tribe}, nil
})
res, err := service.NewTribe(repo, &mock.FakeTribeGetter{}).
GetByServerKeyAndID(context.Background(), tribe.ServerKey, tribe.ID)
assert.NoError(t, err)
assert.Equal(t, tribe, res)
})
t.Run("ERR: tribe not found", func(t *testing.T) {
t.Parallel()
repo := &mock.FakeTribeRepository{}
repo.ListCountReturns(nil, 0, nil)
var id int64 = 123
res, err := service.NewTribe(repo, &mock.FakeTribeGetter{}).
GetByServerKeyAndID(context.Background(), "pl151", id)
assert.ErrorIs(t, err, domain.TribeNotFoundError{ID: id})
assert.Zero(t, res)
})
}