parent
d787edde00
commit
b4e95f3267
1
go.mod
1
go.mod
|
@ -15,6 +15,7 @@ require (
|
||||||
github.com/ory/dockertest/v3 v3.10.0
|
github.com/ory/dockertest/v3 v3.10.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/uptrace/bun v1.1.16
|
github.com/uptrace/bun v1.1.16
|
||||||
|
github.com/uptrace/bun/dbfixture v1.1.16
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.16
|
github.com/uptrace/bun/dialect/pgdialect v1.1.16
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.1.16
|
github.com/uptrace/bun/dialect/sqlitedialect v1.1.16
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.1.16
|
github.com/uptrace/bun/driver/pgdriver v1.1.16
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -149,6 +149,8 @@ github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYm
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A=
|
github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A=
|
||||||
github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8=
|
github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8=
|
||||||
|
github.com/uptrace/bun/dbfixture v1.1.16 h1:CEdQaeptGRRvwVRhn9PMrawo+yaK+HzAU8/O0h463hA=
|
||||||
|
github.com/uptrace/bun/dbfixture v1.1.16/go.mod h1:kw11JRwFD3eAHs3Lyq7sHJ6i2KWngUlZQsrvyRy+dqY=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.16 h1:eUPZ+YCJ69BA+W1X1ZmpOJSkv1oYtinr0zCXf7zCo5g=
|
github.com/uptrace/bun/dialect/pgdialect v1.1.16 h1:eUPZ+YCJ69BA+W1X1ZmpOJSkv1oYtinr0zCXf7zCo5g=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.16/go.mod h1:KQjfx/r6JM0OXfbv0rFrxAbdkPD7idK8VitnjIV9fZI=
|
github.com/uptrace/bun/dialect/pgdialect v1.1.16/go.mod h1:KQjfx/r6JM0OXfbv0rFrxAbdkPD7idK8VitnjIV9fZI=
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.1.16 h1:gbc9BP/e4sNOB9VBj+Si46dpOz2oktmZPidkda92GYY=
|
github.com/uptrace/bun/dialect/sqlitedialect v1.1.16 h1:gbc9BP/e4sNOB9VBj+Si46dpOz2oktmZPidkda92GYY=
|
||||||
|
|
47
internal/adapter/adaptertest/fixture.go
Normal file
47
internal/adapter/adaptertest/fixture.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package adaptertest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io/fs"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter/internal/bunmodel"
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dbfixture"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Fixture struct {
|
||||||
|
f *dbfixture.Fixture
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFixture(bunDB *bun.DB) *Fixture {
|
||||||
|
bunDB.RegisterModel(
|
||||||
|
(*bunmodel.Version)(nil),
|
||||||
|
(*bunmodel.Server)(nil),
|
||||||
|
)
|
||||||
|
return &Fixture{
|
||||||
|
f: dbfixture.New(bunDB),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:revive
|
||||||
|
func (f *Fixture) Load(tb TestingTB, ctx context.Context, fsys fs.FS, names ...string) {
|
||||||
|
tb.Helper()
|
||||||
|
require.NoError(tb, f.f.Load(ctx, fsys, names...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fixture) Server(tb TestingTB, id string) domain.Server {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
row, err := f.f.Row("Server." + id)
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
s, ok := row.(*bunmodel.Server)
|
||||||
|
require.True(tb, ok)
|
||||||
|
|
||||||
|
converted, err := s.ToDomain()
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
return converted
|
||||||
|
}
|
|
@ -9,14 +9,13 @@ import (
|
||||||
"github.com/uptrace/bun/driver/sqliteshim"
|
"github.com/uptrace/bun/driver/sqliteshim"
|
||||||
)
|
)
|
||||||
|
|
||||||
const sqliteMaxIdleConns = 1000
|
|
||||||
|
|
||||||
// NewBunDBSQLite initializes a new instance of *bun.DB, which is ready for use (all required migrations are applied).
|
// NewBunDBSQLite initializes a new instance of *bun.DB, which is ready for use (all required migrations are applied).
|
||||||
// Data is stored in memory (https://www.sqlite.org/inmemorydb.html).
|
// Data is stored in memory (https://www.sqlite.org/inmemorydb.html).
|
||||||
func NewBunDBSQLite(tb TestingTB) *bun.DB {
|
func NewBunDBSQLite(tb TestingTB) *bun.DB {
|
||||||
sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared")
|
sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:")
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
sqldb.SetMaxIdleConns(sqliteMaxIdleConns)
|
sqldb.SetMaxOpenConns(1)
|
||||||
|
sqldb.SetMaxIdleConns(1)
|
||||||
sqldb.SetConnMaxLifetime(0)
|
sqldb.SetConnMaxLifetime(0)
|
||||||
|
|
||||||
db := bun.NewDB(sqldb, sqlitedialect.New())
|
db := bun.NewDB(sqldb, sqlitedialect.New())
|
||||||
|
|
|
@ -3,7 +3,6 @@ package adaptertest
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -26,8 +25,8 @@ func runMigrations(tb TestingTB, db *bun.DB) {
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
require.NoError(tb, migrator.Init(ctx), "couldn't init migrator")
|
require.NoError(tb, migrator.Init(ctx), "couldn't init migrator")
|
||||||
|
|
||||||
_, err := migrator.Migrate(ctx)
|
_, err := migrator.Migrate(ctx)
|
||||||
fmt.Println(err)
|
|
||||||
require.NoError(tb, err, "couldn't apply migrations")
|
require.NoError(tb, err, "couldn't apply migrations")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
91
internal/adapter/internal/bunmodel/building_info.go
Normal file
91
internal/adapter/internal/bunmodel/building_info.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package bunmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Building struct {
|
||||||
|
MaxLevel int
|
||||||
|
MinLevel int
|
||||||
|
Wood int
|
||||||
|
Stone int
|
||||||
|
Iron int
|
||||||
|
Pop int
|
||||||
|
WoodFactor float64
|
||||||
|
StoneFactor float64
|
||||||
|
IronFactor float64
|
||||||
|
PopFactor float64
|
||||||
|
BuildTime float64
|
||||||
|
BuildTimeFactor float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuildingInfo struct {
|
||||||
|
Main Building
|
||||||
|
Barracks Building
|
||||||
|
Stable Building
|
||||||
|
Garage Building
|
||||||
|
Watchtower Building
|
||||||
|
Snob Building
|
||||||
|
Smith Building
|
||||||
|
Place Building
|
||||||
|
Statue Building
|
||||||
|
Market Building
|
||||||
|
Wood Building
|
||||||
|
Stone Building
|
||||||
|
Iron Building
|
||||||
|
Farm Building
|
||||||
|
Storage Building
|
||||||
|
Hide Building
|
||||||
|
Wall Building
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuildingInfo(info domain.BuildingInfo) BuildingInfo {
|
||||||
|
return BuildingInfo{
|
||||||
|
Main: Building(info.Main()),
|
||||||
|
Barracks: Building(info.Barracks()),
|
||||||
|
Stable: Building(info.Stable()),
|
||||||
|
Garage: Building(info.Garage()),
|
||||||
|
Watchtower: Building(info.Watchtower()),
|
||||||
|
Snob: Building(info.Snob()),
|
||||||
|
Smith: Building(info.Smith()),
|
||||||
|
Place: Building(info.Place()),
|
||||||
|
Statue: Building(info.Statue()),
|
||||||
|
Market: Building(info.Market()),
|
||||||
|
Wood: Building(info.Wood()),
|
||||||
|
Stone: Building(info.Stone()),
|
||||||
|
Iron: Building(info.Iron()),
|
||||||
|
Farm: Building(info.Farm()),
|
||||||
|
Storage: Building(info.Storage()),
|
||||||
|
Hide: Building(info.Hide()),
|
||||||
|
Wall: Building(info.Wall()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BuildingInfo) ToDomain() (domain.BuildingInfo, error) {
|
||||||
|
info, err := domain.NewBuildingInfo(
|
||||||
|
domain.Building(b.Main),
|
||||||
|
domain.Building(b.Barracks),
|
||||||
|
domain.Building(b.Stable),
|
||||||
|
domain.Building(b.Garage),
|
||||||
|
domain.Building(b.Watchtower),
|
||||||
|
domain.Building(b.Snob),
|
||||||
|
domain.Building(b.Smith),
|
||||||
|
domain.Building(b.Place),
|
||||||
|
domain.Building(b.Statue),
|
||||||
|
domain.Building(b.Market),
|
||||||
|
domain.Building(b.Wood),
|
||||||
|
domain.Building(b.Stone),
|
||||||
|
domain.Building(b.Iron),
|
||||||
|
domain.Building(b.Farm),
|
||||||
|
domain.Building(b.Storage),
|
||||||
|
domain.Building(b.Hide),
|
||||||
|
domain.Building(b.Wall),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.BuildingInfo{}, fmt.Errorf("couldn't construct domain.BuildingInfo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
97
internal/adapter/internal/bunmodel/server.go
Normal file
97
internal/adapter/internal/bunmodel/server.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package bunmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
bun.BaseModel `bun:"table:servers,alias:server"`
|
||||||
|
Key string `bun:"key,nullzero,pk"`
|
||||||
|
URL string `bun:"url,nullzero"`
|
||||||
|
Open bool `bun:"open"`
|
||||||
|
Special bool `bun:"special"`
|
||||||
|
NumPlayers int `bun:"num_players"`
|
||||||
|
NumTribes int `bun:"num_tribes"`
|
||||||
|
NumVillages int `bun:"num_villages"`
|
||||||
|
NumPlayerVillages int `bun:"num_player_villages"`
|
||||||
|
NumBarbarianVillages int `bun:"num_barbarian_villages"`
|
||||||
|
NumBonusVillages int `bun:"num_bonus_villages"`
|
||||||
|
Config ServerConfig `bun:"config"`
|
||||||
|
BuildingInfo BuildingInfo `bun:"building_info"`
|
||||||
|
UnitInfo UnitInfo `bun:"unit_info"`
|
||||||
|
CreatedAt time.Time `bun:"created_at,nullzero"`
|
||||||
|
PlayerDataUpdatedAt time.Time `bun:"player_data_updated_at,nullzero"`
|
||||||
|
PlayerSnapshotsCreatedAt time.Time `bun:"player_snapshots_created_at,nullzero"`
|
||||||
|
TribeDataUpdatedAt time.Time `bun:"tribe_data_updated_at,nullzero"`
|
||||||
|
TribeSnapshotsCreatedAt time.Time `bun:"tribe_snapshots_created_at,nullzero"`
|
||||||
|
VillageDataUpdatedAt time.Time `bun:"village_data_updated_at,nullzero"`
|
||||||
|
EnnoblementDataUpdatedAt time.Time `bun:"ennoblement_data_updated_at,nullzero"`
|
||||||
|
VersionCode string `bun:"version_code,nullzero"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) ToDomain() (domain.Server, error) {
|
||||||
|
serverCfg, err := s.Config.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return domain.Server{}, fmt.Errorf("couldn't construct domain.Server (key=%s): %w", s.Key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buildingInfo, err := s.BuildingInfo.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return domain.Server{}, fmt.Errorf("couldn't construct domain.Server (key=%s): %w", s.Key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
unitInfo, err := s.UnitInfo.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return domain.Server{}, fmt.Errorf("couldn't construct domain.Server (key=%s): %w", s.Key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
converted, err := domain.UnmarshalServerFromDatabase(
|
||||||
|
s.Key,
|
||||||
|
s.VersionCode,
|
||||||
|
s.URL,
|
||||||
|
s.Open,
|
||||||
|
s.Special,
|
||||||
|
s.NumPlayers,
|
||||||
|
s.NumTribes,
|
||||||
|
s.NumVillages,
|
||||||
|
s.NumPlayerVillages,
|
||||||
|
s.NumBarbarianVillages,
|
||||||
|
s.NumBonusVillages,
|
||||||
|
serverCfg,
|
||||||
|
buildingInfo,
|
||||||
|
unitInfo,
|
||||||
|
s.CreatedAt,
|
||||||
|
s.PlayerDataUpdatedAt,
|
||||||
|
s.PlayerSnapshotsCreatedAt,
|
||||||
|
s.TribeDataUpdatedAt,
|
||||||
|
s.TribeSnapshotsCreatedAt,
|
||||||
|
s.VillageDataUpdatedAt,
|
||||||
|
s.EnnoblementDataUpdatedAt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Server{}, fmt.Errorf("couldn't construct domain.Server (key=%s): %w", s.Key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Servers []Server
|
||||||
|
|
||||||
|
func (ss Servers) ToDomain() (domain.Servers, error) {
|
||||||
|
res := make(domain.Servers, 0, len(ss))
|
||||||
|
|
||||||
|
for _, s := range ss {
|
||||||
|
converted, err := s.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, converted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
207
internal/adapter/internal/bunmodel/server_config.go
Normal file
207
internal/adapter/internal/bunmodel/server_config.go
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
package bunmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerConfigBuild struct {
|
||||||
|
Destroy int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigMisc struct {
|
||||||
|
KillRanking int
|
||||||
|
Tutorial int
|
||||||
|
TradeCancelTime int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigCommands struct {
|
||||||
|
MillisArrival int
|
||||||
|
CommandCancelTime int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigNewbie struct {
|
||||||
|
Days int
|
||||||
|
RatioDays int
|
||||||
|
Ratio int
|
||||||
|
RemoveNewbieVillages int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigGame struct {
|
||||||
|
BuildtimeFormula int
|
||||||
|
Knight int
|
||||||
|
KnightNewItems int
|
||||||
|
Archer int
|
||||||
|
Tech int
|
||||||
|
FarmLimit int
|
||||||
|
Church int
|
||||||
|
Watchtower int
|
||||||
|
Stronghold int
|
||||||
|
FakeLimit float64
|
||||||
|
BarbarianRise float64
|
||||||
|
BarbarianShrink int
|
||||||
|
BarbarianMaxPoints int
|
||||||
|
Scavenging int
|
||||||
|
Hauls int
|
||||||
|
HaulsBase int
|
||||||
|
HaulsMax int
|
||||||
|
BaseProduction int
|
||||||
|
Event int
|
||||||
|
SuppressEvents int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigBuildings struct {
|
||||||
|
CustomMain int
|
||||||
|
CustomFarm int
|
||||||
|
CustomStorage int
|
||||||
|
CustomPlace int
|
||||||
|
CustomBarracks int
|
||||||
|
CustomChurch int
|
||||||
|
CustomSmith int
|
||||||
|
CustomWood int
|
||||||
|
CustomStone int
|
||||||
|
CustomIron int
|
||||||
|
CustomMarket int
|
||||||
|
CustomStable int
|
||||||
|
CustomWall int
|
||||||
|
CustomGarage int
|
||||||
|
CustomHide int
|
||||||
|
CustomSnob int
|
||||||
|
CustomStatue int
|
||||||
|
CustomWatchtower int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigSnob struct {
|
||||||
|
Gold int
|
||||||
|
CheapRebuild int
|
||||||
|
Rise int
|
||||||
|
MaxDist int
|
||||||
|
Factor float64
|
||||||
|
CoinWood int
|
||||||
|
CoinStone int
|
||||||
|
CoinIron int
|
||||||
|
NoBarbConquer int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigAlly struct {
|
||||||
|
NoHarm int
|
||||||
|
NoOtherSupport int
|
||||||
|
NoOtherSupportType int
|
||||||
|
AllytimeSupport int
|
||||||
|
NoLeave int
|
||||||
|
NoJoin int
|
||||||
|
Limit int
|
||||||
|
FixedAllies int
|
||||||
|
PointsMemberCount int
|
||||||
|
WarsMemberRequirement int
|
||||||
|
WarsPointsRequirement int
|
||||||
|
WarsAutoacceptDays int
|
||||||
|
Levels int
|
||||||
|
XpRequirements string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigCoord struct {
|
||||||
|
MapSize int
|
||||||
|
Func int
|
||||||
|
EmptyVillages int
|
||||||
|
BonusVillages int
|
||||||
|
BonusNew int
|
||||||
|
Inner int
|
||||||
|
SelectStart int
|
||||||
|
VillageMoveWait int
|
||||||
|
NobleRestart int
|
||||||
|
StartVillages int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigSitter struct {
|
||||||
|
Allow int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigSleep struct {
|
||||||
|
Active int
|
||||||
|
Delay int
|
||||||
|
Min int
|
||||||
|
Max int
|
||||||
|
MinAwake int
|
||||||
|
MaxAwake int
|
||||||
|
WarnTime int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigNight struct {
|
||||||
|
Active int
|
||||||
|
StartHour int
|
||||||
|
EndHour int
|
||||||
|
DefFactor float64
|
||||||
|
Duration int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigWin struct {
|
||||||
|
Check int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
Speed float64
|
||||||
|
UnitSpeed float64
|
||||||
|
Moral int
|
||||||
|
Build ServerConfigBuild
|
||||||
|
Misc ServerConfigMisc
|
||||||
|
Commands ServerConfigCommands
|
||||||
|
Newbie ServerConfigNewbie
|
||||||
|
Game ServerConfigGame
|
||||||
|
Buildings ServerConfigBuildings
|
||||||
|
Snob ServerConfigSnob
|
||||||
|
Ally ServerConfigAlly
|
||||||
|
Coord ServerConfigCoord
|
||||||
|
Sitter ServerConfigSitter
|
||||||
|
Sleep ServerConfigSleep
|
||||||
|
Night ServerConfigNight
|
||||||
|
Win ServerConfigWin
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerConfig(cfg domain.ServerConfig) ServerConfig {
|
||||||
|
return ServerConfig{
|
||||||
|
Speed: cfg.Speed(),
|
||||||
|
UnitSpeed: cfg.UnitSpeed(),
|
||||||
|
Moral: cfg.Moral(),
|
||||||
|
Build: ServerConfigBuild(cfg.Build()),
|
||||||
|
Misc: ServerConfigMisc(cfg.Misc()),
|
||||||
|
Commands: ServerConfigCommands(cfg.Commands()),
|
||||||
|
Newbie: ServerConfigNewbie(cfg.Newbie()),
|
||||||
|
Game: ServerConfigGame(cfg.Game()),
|
||||||
|
Buildings: ServerConfigBuildings(cfg.Buildings()),
|
||||||
|
Snob: ServerConfigSnob(cfg.Snob()),
|
||||||
|
Ally: ServerConfigAlly(cfg.Ally()),
|
||||||
|
Coord: ServerConfigCoord(cfg.Coord()),
|
||||||
|
Sitter: ServerConfigSitter(cfg.Sitter()),
|
||||||
|
Sleep: ServerConfigSleep(cfg.Sleep()),
|
||||||
|
Night: ServerConfigNight(cfg.Night()),
|
||||||
|
Win: ServerConfigWin(cfg.Win()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s ServerConfig) ToDomain() (domain.ServerConfig, error) {
|
||||||
|
cfg, err := domain.NewServerConfig(
|
||||||
|
s.Speed,
|
||||||
|
s.UnitSpeed,
|
||||||
|
s.Moral,
|
||||||
|
domain.ServerConfigBuild(s.Build),
|
||||||
|
domain.ServerConfigMisc(s.Misc),
|
||||||
|
domain.ServerConfigCommands(s.Commands),
|
||||||
|
domain.ServerConfigNewbie(s.Newbie),
|
||||||
|
domain.ServerConfigGame(s.Game),
|
||||||
|
domain.ServerConfigBuildings(s.Buildings),
|
||||||
|
domain.ServerConfigSnob(s.Snob),
|
||||||
|
domain.ServerConfigAlly(s.Ally),
|
||||||
|
domain.ServerConfigCoord(s.Coord),
|
||||||
|
domain.ServerConfigSitter(s.Sitter),
|
||||||
|
domain.ServerConfigSleep(s.Sleep),
|
||||||
|
domain.ServerConfigNight(s.Night),
|
||||||
|
domain.ServerConfigWin(s.Win),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.ServerConfig{}, fmt.Errorf("couldn't construct domain.ServerConfig: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
75
internal/adapter/internal/bunmodel/unit_info.go
Normal file
75
internal/adapter/internal/bunmodel/unit_info.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package bunmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Unit struct {
|
||||||
|
BuildTime float64
|
||||||
|
Pop int
|
||||||
|
Speed float64
|
||||||
|
Attack int
|
||||||
|
Defense int
|
||||||
|
DefenseCavalry int
|
||||||
|
DefenseArcher int
|
||||||
|
Carry int
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnitInfo struct {
|
||||||
|
Spear Unit
|
||||||
|
Sword Unit
|
||||||
|
Axe Unit
|
||||||
|
Archer Unit
|
||||||
|
Spy Unit
|
||||||
|
Light Unit
|
||||||
|
Marcher Unit
|
||||||
|
Heavy Unit
|
||||||
|
Ram Unit
|
||||||
|
Catapult Unit
|
||||||
|
Knight Unit
|
||||||
|
Snob Unit
|
||||||
|
Militia Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnitInfo(info domain.UnitInfo) UnitInfo {
|
||||||
|
return UnitInfo{
|
||||||
|
Spear: Unit(info.Spear()),
|
||||||
|
Sword: Unit(info.Sword()),
|
||||||
|
Axe: Unit(info.Axe()),
|
||||||
|
Archer: Unit(info.Archer()),
|
||||||
|
Spy: Unit(info.Spy()),
|
||||||
|
Light: Unit(info.Light()),
|
||||||
|
Marcher: Unit(info.Marcher()),
|
||||||
|
Heavy: Unit(info.Heavy()),
|
||||||
|
Ram: Unit(info.Ram()),
|
||||||
|
Catapult: Unit(info.Catapult()),
|
||||||
|
Knight: Unit(info.Knight()),
|
||||||
|
Snob: Unit(info.Snob()),
|
||||||
|
Militia: Unit(info.Militia()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UnitInfo) ToDomain() (domain.UnitInfo, error) {
|
||||||
|
info, err := domain.NewUnitInfo(
|
||||||
|
domain.Unit(u.Spear),
|
||||||
|
domain.Unit(u.Sword),
|
||||||
|
domain.Unit(u.Axe),
|
||||||
|
domain.Unit(u.Archer),
|
||||||
|
domain.Unit(u.Spy),
|
||||||
|
domain.Unit(u.Light),
|
||||||
|
domain.Unit(u.Marcher),
|
||||||
|
domain.Unit(u.Heavy),
|
||||||
|
domain.Unit(u.Ram),
|
||||||
|
domain.Unit(u.Catapult),
|
||||||
|
domain.Unit(u.Knight),
|
||||||
|
domain.Unit(u.Snob),
|
||||||
|
domain.Unit(u.Militia),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.UnitInfo{}, fmt.Errorf("couldn't construct domain.UnitInfo: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
119
internal/adapter/repository_bun_server.go
Normal file
119
internal/adapter/repository_bun_server.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package adapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter/internal/bunmodel"
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dialect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerBunRepository struct {
|
||||||
|
db bun.IDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerBunRepository(db bun.IDB) *ServerBunRepository {
|
||||||
|
return &ServerBunRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ServerBunRepository) CreateOrUpdate(ctx context.Context, params ...domain.CreateServerParams) error {
|
||||||
|
if len(params) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
servers := make(bunmodel.Servers, 0, len(params))
|
||||||
|
|
||||||
|
for _, p := range params {
|
||||||
|
base := p.Base()
|
||||||
|
servers = append(servers, bunmodel.Server{
|
||||||
|
Key: base.Key(),
|
||||||
|
URL: base.URL().String(),
|
||||||
|
Open: base.Open(),
|
||||||
|
VersionCode: p.VersionCode(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
q := repo.db.NewInsert().
|
||||||
|
Model(&servers)
|
||||||
|
|
||||||
|
//nolint:exhaustive
|
||||||
|
switch q.Dialect().Name() {
|
||||||
|
case dialect.PG:
|
||||||
|
q = q.On("CONFLICT ON CONSTRAINT servers_pkey DO UPDATE")
|
||||||
|
case dialect.SQLite:
|
||||||
|
q = q.On("CONFLICT(key) DO UPDATE")
|
||||||
|
default:
|
||||||
|
q = q.Err(errors.New("unsupported dialect"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := q.
|
||||||
|
Set("url = EXCLUDED.url").
|
||||||
|
Set("open = EXCLUDED.open").
|
||||||
|
Returning("").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return fmt.Errorf("something went wrong while inserting servers into the db: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ServerBunRepository) List(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) {
|
||||||
|
var servers bunmodel.Servers
|
||||||
|
|
||||||
|
if err := repo.baseListQuery(params).Model(&servers).Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, fmt.Errorf("couldn't select servers from the db: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers.ToDomain()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *ServerBunRepository) baseListQuery(params domain.ListServersParams) *bun.SelectQuery {
|
||||||
|
return repo.db.NewSelect().Apply(listServersParamsApplier{params: params}.apply)
|
||||||
|
}
|
||||||
|
|
||||||
|
type listServersParamsApplier struct {
|
||||||
|
params domain.ListServersParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
if keys := a.params.Keys(); len(keys) > 0 {
|
||||||
|
q = q.Where("server.key IN (?)", bun.In(keys))
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyGT := a.params.KeyGT(); keyGT.Valid {
|
||||||
|
q = q.Where("server.key > ?", keyGT.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if open := a.params.Open(); open.Valid {
|
||||||
|
q = q.Where("server.open = ?", open.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if special := a.params.Special(); special.Valid {
|
||||||
|
q = q.Where("server.special = ?", special.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
q = q.Limit(a.params.Limit()).Offset(a.params.Offset())
|
||||||
|
|
||||||
|
for _, s := range a.params.Sort() {
|
||||||
|
switch s {
|
||||||
|
case domain.ServerSortKeyASC:
|
||||||
|
q = q.Order("server.key ASC")
|
||||||
|
case domain.ServerSortKeyDESC:
|
||||||
|
q = q.Order("server.key DESC")
|
||||||
|
case domain.ServerSortOpenASC:
|
||||||
|
q = q.Order("server.open ASC")
|
||||||
|
case domain.ServerSortOpenDESC:
|
||||||
|
q = q.Order("server.open DESC")
|
||||||
|
default:
|
||||||
|
return q.Err(errors.New("unsupported sort value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
29
internal/adapter/repository_bun_server_test.go
Normal file
29
internal/adapter/repository_bun_server_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package adapter_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter/adaptertest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerBunRepository_Postgres(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping long-running test")
|
||||||
|
}
|
||||||
|
|
||||||
|
testServerRepository(t, func(t *testing.T) repositories {
|
||||||
|
t.Helper()
|
||||||
|
return newBunDBRepositories(t, postgres.NewBunDB(t))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerBunRepository_SQLite(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testServerRepository(t, func(t *testing.T) repositories {
|
||||||
|
t.Helper()
|
||||||
|
return newBunDBRepositories(t, adaptertest.NewBunDBSQLite(t))
|
||||||
|
})
|
||||||
|
}
|
|
@ -3,28 +3,27 @@ package adapter_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter"
|
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter/adaptertest"
|
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter/adaptertest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVersionBunRepo_Postgres(t *testing.T) {
|
func TestVersionBunRepository_Postgres(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping long-running test")
|
t.Skip("skipping long-running test")
|
||||||
}
|
}
|
||||||
|
|
||||||
testVersionRepo(t, func(t *testing.T) versionRepository {
|
testVersionRepository(t, func(t *testing.T) repositories {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return adapter.NewVersionBunRepository(postgres.NewBunDB(t))
|
return newBunDBRepositories(t, postgres.NewBunDB(t))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVersionBunRepo_SQLite(t *testing.T) {
|
func TestVersionBunRepository_SQLite(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
testVersionRepo(t, func(t *testing.T) versionRepository {
|
testVersionRepository(t, func(t *testing.T) repositories {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return adapter.NewVersionBunRepository(adaptertest.NewBunDBSQLite(t))
|
return newBunDBRepositories(t, adaptertest.NewBunDBSQLite(t))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
354
internal/adapter/repository_server_test.go
Normal file
354
internal/adapter/repository_server_test.go
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
package adapter_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
||||||
|
"github.com/brianvoe/gofakeit/v6"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
t.Run("CreateOrUpdate", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
versions, err := repos.version.List(ctx, domain.NewListVersionsParams())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, versions)
|
||||||
|
version := versions[0]
|
||||||
|
|
||||||
|
serversToCreate := domain.BaseServers{
|
||||||
|
domaintest.NewBaseServer(t, domaintest.BaseServerConfig{Open: true}),
|
||||||
|
domaintest.NewBaseServer(t, domaintest.BaseServerConfig{Open: true}),
|
||||||
|
}
|
||||||
|
|
||||||
|
createParams, err := domain.NewCreateServerParams(serversToCreate, version.Code())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, repos.server.CreateOrUpdate(ctx, createParams...))
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(serversToCreate))
|
||||||
|
for _, s := range serversToCreate {
|
||||||
|
keys = append(keys, s.Key())
|
||||||
|
}
|
||||||
|
|
||||||
|
listCreatedServersParams := domain.NewListServersParams()
|
||||||
|
require.NoError(t, listCreatedServersParams.SetKeys(keys))
|
||||||
|
|
||||||
|
createdServers, err := repos.server.List(ctx, listCreatedServersParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, createdServers, len(serversToCreate))
|
||||||
|
for _, base := range serversToCreate {
|
||||||
|
assert.True(t, slices.ContainsFunc(createdServers, func(server domain.Server) bool {
|
||||||
|
return server.Key() == base.Key() &&
|
||||||
|
server.Open() == base.Open() &&
|
||||||
|
server.URL().String() == base.URL().String() &&
|
||||||
|
server.VersionCode() == version.Code()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
serversToUpdate := domain.BaseServers{
|
||||||
|
domaintest.NewBaseServer(t, domaintest.BaseServerConfig{
|
||||||
|
Key: serversToCreate[0].Key(),
|
||||||
|
URL: randURL(t),
|
||||||
|
Open: !serversToCreate[0].Open(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParams, err := domain.NewCreateServerParams(serversToUpdate, version.Code())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, repos.server.CreateOrUpdate(ctx, updateParams...))
|
||||||
|
|
||||||
|
keys = make([]string, 0, len(serversToUpdate))
|
||||||
|
for _, s := range serversToUpdate {
|
||||||
|
keys = append(keys, s.Key())
|
||||||
|
}
|
||||||
|
|
||||||
|
listUpdatedServersParams := domain.NewListServersParams()
|
||||||
|
require.NoError(t, listUpdatedServersParams.SetKeys(keys))
|
||||||
|
|
||||||
|
updatedServers, err := repos.server.List(ctx, listUpdatedServersParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, updatedServers, len(serversToUpdate))
|
||||||
|
for _, base := range serversToUpdate {
|
||||||
|
assert.True(t, slices.ContainsFunc(updatedServers, func(server domain.Server) bool {
|
||||||
|
return server.Key() == base.Key() &&
|
||||||
|
server.Open() == base.Open() &&
|
||||||
|
server.URL().String() == base.URL().String() &&
|
||||||
|
server.VersionCode() == version.Code()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("List & ListCount", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params func(t *testing.T) domain.ListServersParams
|
||||||
|
assertServers func(t *testing.T, params domain.ListServersParams, servers domain.Servers)
|
||||||
|
assertError func(t *testing.T, err error)
|
||||||
|
assertTotal func(t *testing.T, params domain.ListServersParams, total int)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: default params",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
return domain.NewListServersParams()
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(servers))
|
||||||
|
assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int {
|
||||||
|
return cmp.Compare(a.Key(), b.Key())
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: sort=[open ASC, key ASC]",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSort{domain.ServerSortOpenASC, domain.ServerSortKeyASC}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(servers))
|
||||||
|
assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int {
|
||||||
|
if a.Open() && !b.Open() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.Open() && b.Open() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmp.Compare(a.Key(), b.Key())
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: sort=[open DESC, key DESC]",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.ServerSort{domain.ServerSortOpenDESC, domain.ServerSortKeyDESC}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(servers))
|
||||||
|
assert.True(t, slices.IsSortedFunc(servers, func(a, b domain.Server) int {
|
||||||
|
if a.Open() && !b.Open() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.Open() && b.Open() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmp.Compare(a.Key(), b.Key()) * -1
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: keys=[de188, en113]",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetKeys([]string{"de188", "en113"}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
keys := params.Keys()
|
||||||
|
|
||||||
|
assert.Len(t, servers, len(keys))
|
||||||
|
for _, k := range keys {
|
||||||
|
assert.True(t, slices.ContainsFunc(servers, func(server domain.Server) bool {
|
||||||
|
return server.Key() == k
|
||||||
|
}), k)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.Equal(t, len(params.Keys()), total) //nolint:testifylint
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: keyGT=de188",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetKeyGT(domain.NullString{
|
||||||
|
Value: "de188",
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(servers))
|
||||||
|
for _, s := range servers {
|
||||||
|
assert.Greater(t, s.Key(), params.KeyGT().Value, s.Key())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: special=true",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetSpecial(domain.NullBool{
|
||||||
|
Value: true,
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(servers))
|
||||||
|
for _, s := range servers {
|
||||||
|
assert.True(t, s.Special(), s.Key())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: open=false",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetOpen(domain.NullBool{
|
||||||
|
Value: false,
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(servers))
|
||||||
|
for _, s := range servers {
|
||||||
|
assert.False(t, s.Open(), s.Key())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: offset=1 limit=2",
|
||||||
|
params: func(t *testing.T) domain.ListServersParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
require.NoError(t, params.SetOffset(1))
|
||||||
|
require.NoError(t, params.SetLimit(2))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertServers: func(t *testing.T, params domain.ListServersParams, servers domain.Servers) {
|
||||||
|
t.Helper()
|
||||||
|
assert.Len(t, servers, params.Limit())
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListServersParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
params := tt.params(t)
|
||||||
|
|
||||||
|
res, err := repos.server.List(ctx, params)
|
||||||
|
tt.assertError(t, err)
|
||||||
|
tt.assertServers(t, params, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func randURL(tb testing.TB) *url.URL {
|
||||||
|
tb.Helper()
|
||||||
|
u, err := url.Parse("https://" + gofakeit.DomainName())
|
||||||
|
require.NoError(tb, err)
|
||||||
|
return u
|
||||||
|
}
|
41
internal/adapter/repository_test.go
Normal file
41
internal/adapter/repository_test.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package adapter_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter"
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter/adaptertest"
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type versionRepository interface {
|
||||||
|
List(ctx context.Context, params domain.ListVersionsParams) (domain.Versions, error)
|
||||||
|
ListCount(
|
||||||
|
ctx context.Context,
|
||||||
|
params domain.ListVersionsParams,
|
||||||
|
) (domain.Versions, int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverRepository interface {
|
||||||
|
CreateOrUpdate(ctx context.Context, params ...domain.CreateServerParams) error
|
||||||
|
List(ctx context.Context, params domain.ListServersParams) (domain.Servers, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type repositories struct {
|
||||||
|
version versionRepository
|
||||||
|
server serverRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBunDBRepositories(tb testing.TB, bunDB *bun.DB) repositories {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
adaptertest.NewFixture(bunDB).Load(tb, context.Background(), os.DirFS("testdata"), "fixture.yml")
|
||||||
|
|
||||||
|
return repositories{
|
||||||
|
version: adapter.NewVersionBunRepository(bunDB),
|
||||||
|
server: adapter.NewServerBunRepository(bunDB),
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,21 +11,13 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type versionRepository interface {
|
func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositories) {
|
||||||
List(ctx context.Context, params domain.ListVersionsParams) (domain.Versions, error)
|
|
||||||
ListCount(
|
|
||||||
ctx context.Context,
|
|
||||||
params domain.ListVersionsParams,
|
|
||||||
) (domain.Versions, int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testVersionRepo(t *testing.T, newRepo func(t *testing.T) versionRepository) {
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
t.Run("List & ListCount", func(t *testing.T) {
|
t.Run("List & ListCount", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
repo := newRepo(t)
|
repos := newRepos(t)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -53,11 +45,11 @@ func testVersionRepo(t *testing.T, newRepo func(t *testing.T) versionRepository)
|
||||||
},
|
},
|
||||||
assertTotal: func(t *testing.T, total int) {
|
assertTotal: func(t *testing.T, total int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
assert.NotEmpty(t, total, 0)
|
assert.NotEmpty(t, total)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "OK: Sort=[Code DESC]",
|
name: "OK: sort=[code DESC]",
|
||||||
params: func(t *testing.T) domain.ListVersionsParams {
|
params: func(t *testing.T) domain.ListVersionsParams {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
params := domain.NewListVersionsParams()
|
params := domain.NewListVersionsParams()
|
||||||
|
@ -91,11 +83,11 @@ func testVersionRepo(t *testing.T, newRepo func(t *testing.T) versionRepository)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
params := tt.params(t)
|
params := tt.params(t)
|
||||||
|
|
||||||
res, err := repo.List(ctx, params)
|
res, err := repos.version.List(ctx, params)
|
||||||
tt.assertError(t, err)
|
tt.assertError(t, err)
|
||||||
tt.assertVersions(t, res)
|
tt.assertVersions(t, res)
|
||||||
|
|
||||||
res, count, err := repo.ListCount(ctx, params)
|
res, count, err := repos.version.ListCount(ctx, params)
|
||||||
tt.assertError(t, err)
|
tt.assertError(t, err)
|
||||||
tt.assertVersions(t, res)
|
tt.assertVersions(t, res)
|
||||||
tt.assertTotal(t, count)
|
tt.assertTotal(t, count)
|
||||||
|
|
78
internal/adapter/testdata/fixture.yml
vendored
Normal file
78
internal/adapter/testdata/fixture.yml
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
- model: Server
|
||||||
|
rows:
|
||||||
|
- _id: de188
|
||||||
|
key: de188
|
||||||
|
url: https://de188.die-staemme.de
|
||||||
|
open: true
|
||||||
|
special: false
|
||||||
|
num_players: 180
|
||||||
|
num_tribes: 76
|
||||||
|
num_villages: 16180
|
||||||
|
num_player_villages: 15000
|
||||||
|
num_barbarian_villages: 1180
|
||||||
|
num_bonus_villages: 512
|
||||||
|
created_at: 2022-03-19T12:00:54.000Z
|
||||||
|
player_data_updated_at: 2022-03-19T12:00:54.000Z
|
||||||
|
player_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
||||||
|
tribe_data_updated_at: 2022-03-19T12:00:54.000Z
|
||||||
|
tribe_snapshots_created_at: 2022-03-19T12:00:54.000Z
|
||||||
|
village_data_updated_at: 2022-03-19T12:00:54.000Z
|
||||||
|
ennoblement_data_updated_at: 2022-03-19T12:00:54.000Z
|
||||||
|
version_code: de
|
||||||
|
- _id: en113
|
||||||
|
key: en113
|
||||||
|
url: https://en113.tribalwars.net
|
||||||
|
open: false
|
||||||
|
special: false
|
||||||
|
num_players: 251
|
||||||
|
num_tribes: 57
|
||||||
|
num_villages: 41700
|
||||||
|
num_player_villages: 41000
|
||||||
|
num_barbarian_villages: 700
|
||||||
|
num_bonus_villages: 1024
|
||||||
|
created_at: 2021-04-02T16:01:25.000Z
|
||||||
|
player_data_updated_at: 2021-04-02T16:01:25.000Z
|
||||||
|
player_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
||||||
|
tribe_data_updated_at: 2021-04-02T16:01:25.000Z
|
||||||
|
tribe_snapshots_created_at: 2021-04-02T16:01:25.000Z
|
||||||
|
village_data_updated_at: 2021-04-02T16:01:25.000Z
|
||||||
|
ennoblement_data_updated_at: 2021-04-02T16:01:25.000Z
|
||||||
|
version_code: en
|
||||||
|
- _id: it70
|
||||||
|
key: it70
|
||||||
|
url: https://it70.tribals.it
|
||||||
|
open: true
|
||||||
|
special: false
|
||||||
|
num_players: 1883
|
||||||
|
num_tribes: 101
|
||||||
|
num_villages: 4882
|
||||||
|
num_player_villages: 3200
|
||||||
|
num_barbarian_villages: 1682
|
||||||
|
num_bonus_villages: 256
|
||||||
|
created_at: 2022-03-19T12:00:04.000Z
|
||||||
|
player_data_updated_at: 2022-03-19T12:00:04.000Z
|
||||||
|
player_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
||||||
|
tribe_data_updated_at: 2022-03-19T12:00:04.000Z
|
||||||
|
tribe_snapshots_created_at: 2022-03-19T12:00:04.000Z
|
||||||
|
village_data_updated_at: 2022-03-19T12:00:04.000Z
|
||||||
|
ennoblement_data_updated_at: 2022-03-19T12:00:04.000Z
|
||||||
|
version_code: it
|
||||||
|
- _id: pl169
|
||||||
|
key: pl169
|
||||||
|
url: https://pl169.plemiona.pl
|
||||||
|
open: true
|
||||||
|
special: false
|
||||||
|
num_players: 2001
|
||||||
|
num_tribes: 214
|
||||||
|
num_villages: 49074
|
||||||
|
num_player_villages: 48500
|
||||||
|
num_barbarian_villages: 1574
|
||||||
|
num_bonus_villages: 2048
|
||||||
|
created_at: 2022-03-19T12:01:39.000Z
|
||||||
|
player_data_updated_at: 2022-03-19T12:01:39.000Z
|
||||||
|
player_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
|
tribe_data_updated_at: 2022-03-19T12:01:39.000Z
|
||||||
|
tribe_snapshots_created_at: 2022-03-19T12:01:39.000Z
|
||||||
|
village_data_updated_at: 2022-03-19T12:01:39.000Z
|
||||||
|
ennoblement_data_updated_at: 2022-03-19T12:01:39.000Z
|
||||||
|
version_code: pl
|
|
@ -9,26 +9,27 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseServerConfig struct {
|
type BaseServerConfig struct {
|
||||||
Key string
|
Key string
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
|
Open bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseServerConfig) init() {
|
func (cfg *BaseServerConfig) init() {
|
||||||
if cfg.Key == "" {
|
if cfg.Key == "" {
|
||||||
cfg.Key = gofakeit.LetterN(5)
|
cfg.Key = RandServerKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.URL == nil {
|
if cfg.URL == nil {
|
||||||
cfg.URL = &url.URL{
|
cfg.URL = &url.URL{
|
||||||
Scheme: "https",
|
Scheme: "https",
|
||||||
Host: cfg.Key + ".plemiona.pl",
|
Host: cfg.Key + "." + gofakeit.DomainName(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBaseServer(tb TestingTB, cfg BaseServerConfig) domain.BaseServer {
|
func NewBaseServer(tb TestingTB, cfg BaseServerConfig) domain.BaseServer {
|
||||||
cfg.init()
|
cfg.init()
|
||||||
s, err := domain.NewBaseServer(cfg.Key, cfg.URL.String(), true)
|
s, err := domain.NewBaseServer(cfg.Key, cfg.URL.String(), cfg.Open)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
7
internal/domain/domaintest/server.go
Normal file
7
internal/domain/domaintest/server.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package domaintest
|
||||||
|
|
||||||
|
import "github.com/brianvoe/gofakeit/v6"
|
||||||
|
|
||||||
|
func RandServerKey() string {
|
||||||
|
return gofakeit.LetterN(5)
|
||||||
|
}
|
33
internal/domain/domaintest/version.go
Normal file
33
internal/domain/domaintest/version.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package domaintest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/brianvoe/gofakeit/v6"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VersionConfig struct {
|
||||||
|
Code string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *VersionConfig) init() {
|
||||||
|
if cfg.Code == "" {
|
||||||
|
cfg.Code = RandVersionCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVersion(tb TestingTB, cfg VersionConfig) domain.Version {
|
||||||
|
cfg.init()
|
||||||
|
s, err := domain.UnmarshalVersionFromDatabase(
|
||||||
|
cfg.Code,
|
||||||
|
gofakeit.LetterN(10),
|
||||||
|
gofakeit.DomainName(),
|
||||||
|
gofakeit.TimeZoneRegion(),
|
||||||
|
)
|
||||||
|
require.NoError(tb, err)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandVersionCode() string {
|
||||||
|
return gofakeit.LetterN(2)
|
||||||
|
}
|
|
@ -1,12 +1,5 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/gosimple/slug"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ErrorCode uint8
|
type ErrorCode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -38,40 +31,3 @@ type ErrorWithParams interface {
|
||||||
Error
|
Error
|
||||||
Params() map[string]any
|
Params() map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidationError struct {
|
|
||||||
Field string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ErrorWithParams = ValidationError{}
|
|
||||||
|
|
||||||
func (e ValidationError) Error() string {
|
|
||||||
return fmt.Sprintf("%s: %s", e.Field, e.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ValidationError) Code() ErrorCode {
|
|
||||||
var domainErr Error
|
|
||||||
if errors.As(e.Err, &domainErr) {
|
|
||||||
return domainErr.Code()
|
|
||||||
}
|
|
||||||
return ErrorCodeIncorrectInput
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ValidationError) Slug() string {
|
|
||||||
s := "validation"
|
|
||||||
var domainErr Error
|
|
||||||
if errors.As(e.Err, &domainErr) {
|
|
||||||
s = domainErr.Slug()
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s-%s", slug.Make(e.Field), s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ValidationError) Params() map[string]any {
|
|
||||||
var withParams ErrorWithParams
|
|
||||||
ok := errors.As(e.Err, &withParams)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return withParams.Params()
|
|
||||||
}
|
|
||||||
|
|
12
internal/domain/null.go
Normal file
12
internal/domain/null.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
type NullValue[T any] struct {
|
||||||
|
Value T
|
||||||
|
Valid bool // Valid is true if Value is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullInt = NullValue[int]
|
||||||
|
|
||||||
|
type NullString = NullValue[string]
|
||||||
|
|
||||||
|
type NullBool = NullValue[bool]
|
|
@ -2,39 +2,359 @@ package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SyncServersCmdPayload struct {
|
type Server struct {
|
||||||
versionCode string
|
key string
|
||||||
url *url.URL
|
versionCode string
|
||||||
|
url *url.URL
|
||||||
|
open bool
|
||||||
|
special bool
|
||||||
|
numPlayers int
|
||||||
|
numTribes int
|
||||||
|
numVillages int
|
||||||
|
numPlayerVillages int
|
||||||
|
numBarbarianVillages int
|
||||||
|
numBonusVillages int
|
||||||
|
config ServerConfig
|
||||||
|
buildingInfo BuildingInfo
|
||||||
|
unitInfo UnitInfo
|
||||||
|
createdAt time.Time
|
||||||
|
playerDataSyncedAt time.Time
|
||||||
|
playerSnapshotsCreatedAt time.Time
|
||||||
|
tribeDataSyncedAt time.Time
|
||||||
|
tribeSnapshotsCreatedAt time.Time
|
||||||
|
villageDataSyncedAt time.Time
|
||||||
|
ennoblementDataSyncedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSyncServersCmdPayload(versionCode string, u *url.URL) (SyncServersCmdPayload, error) {
|
// UnmarshalServerFromDatabase unmarshals Server from the database.
|
||||||
|
//
|
||||||
|
// It should be used only for unmarshalling from the database!
|
||||||
|
// You can't use UnmarshalServerFromDatabase as constructor - It may put domain into the invalid state!
|
||||||
|
func UnmarshalServerFromDatabase(
|
||||||
|
key string,
|
||||||
|
versionCode string,
|
||||||
|
rawURL string,
|
||||||
|
open bool,
|
||||||
|
special bool,
|
||||||
|
numPlayers int,
|
||||||
|
numTribes int,
|
||||||
|
numVillages int,
|
||||||
|
numPlayerVillages int,
|
||||||
|
numBarbarianVillages int,
|
||||||
|
numBonusVillages int,
|
||||||
|
config ServerConfig,
|
||||||
|
buildingInfo BuildingInfo,
|
||||||
|
unitInfo UnitInfo,
|
||||||
|
createdAt time.Time,
|
||||||
|
playerDataSyncedAt time.Time,
|
||||||
|
playerSnapshotsCreatedAt time.Time,
|
||||||
|
tribeDataSyncedAt time.Time,
|
||||||
|
tribeSnapshotsCreatedAt time.Time,
|
||||||
|
villageDataSyncedAt time.Time,
|
||||||
|
ennoblementDataSyncedAt time.Time,
|
||||||
|
) (Server, error) {
|
||||||
|
if key == "" {
|
||||||
|
return Server{}, errors.New("key can't be blank")
|
||||||
|
}
|
||||||
|
|
||||||
if versionCode == "" {
|
if versionCode == "" {
|
||||||
return SyncServersCmdPayload{}, errors.New("version code can't be blank")
|
return Server{}, errors.New("version code can't be blank")
|
||||||
}
|
}
|
||||||
|
|
||||||
if u == nil {
|
|
||||||
return SyncServersCmdPayload{}, errors.New("url can't be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
return SyncServersCmdPayload{versionCode: versionCode, url: u}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSyncServersCmdPayloadWithStringURL(versionCode string, rawURL string) (SyncServersCmdPayload, error) {
|
|
||||||
u, err := parseURL(rawURL)
|
u, err := parseURL(rawURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SyncServersCmdPayload{}, err
|
return Server{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewSyncServersCmdPayload(versionCode, u)
|
return Server{
|
||||||
|
key: key,
|
||||||
|
url: u,
|
||||||
|
open: open,
|
||||||
|
special: special,
|
||||||
|
numPlayers: numPlayers,
|
||||||
|
numTribes: numTribes,
|
||||||
|
numVillages: numVillages,
|
||||||
|
numPlayerVillages: numPlayerVillages,
|
||||||
|
numBarbarianVillages: numBarbarianVillages,
|
||||||
|
numBonusVillages: numBonusVillages,
|
||||||
|
config: config,
|
||||||
|
buildingInfo: buildingInfo,
|
||||||
|
unitInfo: unitInfo,
|
||||||
|
createdAt: createdAt,
|
||||||
|
playerDataSyncedAt: playerDataSyncedAt,
|
||||||
|
playerSnapshotsCreatedAt: playerSnapshotsCreatedAt,
|
||||||
|
tribeDataSyncedAt: tribeDataSyncedAt,
|
||||||
|
tribeSnapshotsCreatedAt: tribeSnapshotsCreatedAt,
|
||||||
|
villageDataSyncedAt: villageDataSyncedAt,
|
||||||
|
ennoblementDataSyncedAt: ennoblementDataSyncedAt,
|
||||||
|
versionCode: versionCode,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p SyncServersCmdPayload) VersionCode() string {
|
func (s Server) Key() string {
|
||||||
return p.versionCode
|
return s.key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p SyncServersCmdPayload) URL() *url.URL {
|
func (s Server) VersionCode() string {
|
||||||
return p.url
|
return s.versionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) URL() *url.URL {
|
||||||
|
return s.url
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) Open() bool {
|
||||||
|
return s.open
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) Special() bool {
|
||||||
|
return s.special
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumPlayers() int {
|
||||||
|
return s.numPlayers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumTribes() int {
|
||||||
|
return s.numTribes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumVillages() int {
|
||||||
|
return s.numVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumPlayerVillages() int {
|
||||||
|
return s.numPlayerVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumBarbarianVillages() int {
|
||||||
|
return s.numBarbarianVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) NumBonusVillages() int {
|
||||||
|
return s.numBonusVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) Config() ServerConfig {
|
||||||
|
return s.config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) BuildingInfo() BuildingInfo {
|
||||||
|
return s.buildingInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) UnitInfo() UnitInfo {
|
||||||
|
return s.unitInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) CreatedAt() time.Time {
|
||||||
|
return s.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) PlayerDataSyncedAt() time.Time {
|
||||||
|
return s.playerDataSyncedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) PlayerSnapshotsCreatedAt() time.Time {
|
||||||
|
return s.playerSnapshotsCreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) TribeDataSyncedAt() time.Time {
|
||||||
|
return s.tribeDataSyncedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) TribeSnapshotsCreatedAt() time.Time {
|
||||||
|
return s.tribeSnapshotsCreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) VillageDataSyncedAt() time.Time {
|
||||||
|
return s.villageDataSyncedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) EnnoblementDataSyncedAt() time.Time {
|
||||||
|
return s.ennoblementDataSyncedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
type Servers []Server
|
||||||
|
|
||||||
|
type CreateServerParams struct {
|
||||||
|
base BaseServer
|
||||||
|
versionCode string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCreateServerParams(servers BaseServers, versionCode string) ([]CreateServerParams, error) {
|
||||||
|
if err := validateVersionCode(versionCode); err != nil {
|
||||||
|
return nil, ValidationError{
|
||||||
|
Err: err,
|
||||||
|
Field: "versionCode",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make([]CreateServerParams, 0, len(servers))
|
||||||
|
|
||||||
|
for i, s := range servers {
|
||||||
|
if s.IsZero() {
|
||||||
|
return nil, fmt.Errorf("servers[%d] is an empty struct", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, CreateServerParams{
|
||||||
|
base: s,
|
||||||
|
versionCode: versionCode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerParams) Base() BaseServer {
|
||||||
|
return params.base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateServerParams) VersionCode() string {
|
||||||
|
return params.versionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSort uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ServerSortKeyASC ServerSort = iota + 1
|
||||||
|
ServerSortKeyDESC
|
||||||
|
ServerSortOpenASC
|
||||||
|
ServerSortOpenDESC
|
||||||
|
)
|
||||||
|
|
||||||
|
const ServerListMaxLimit = 500
|
||||||
|
|
||||||
|
type ListServersParams struct {
|
||||||
|
keys []string
|
||||||
|
keyGT NullString
|
||||||
|
open NullBool
|
||||||
|
special NullBool
|
||||||
|
sort []ServerSort
|
||||||
|
limit int
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListServersParams() ListServersParams {
|
||||||
|
return ListServersParams{
|
||||||
|
sort: []ServerSort{ServerSortKeyASC},
|
||||||
|
limit: ServerListMaxLimit,
|
||||||
|
special: NullBool{
|
||||||
|
Value: false,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) Keys() []string {
|
||||||
|
return params.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetKeys(keys []string) error {
|
||||||
|
params.keys = keys
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) KeyGT() NullString {
|
||||||
|
return params.keyGT
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetKeyGT(keyGT NullString) error {
|
||||||
|
params.keyGT = keyGT
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) Open() NullBool {
|
||||||
|
return params.open
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetOpen(open NullBool) error {
|
||||||
|
params.open = open
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) Special() NullBool {
|
||||||
|
return params.special
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetSpecial(special NullBool) error {
|
||||||
|
params.special = special
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) Sort() []ServerSort {
|
||||||
|
return params.sort
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
serverSortMinLength = 1
|
||||||
|
serverSortMaxLength = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetSort(sort []ServerSort) error {
|
||||||
|
if err := validateSliceLen(sort, serverSortMinLength, serverSortMaxLength); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Field: "sort",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.sort = sort
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) Limit() int {
|
||||||
|
return params.limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetLimit(limit int) error {
|
||||||
|
if limit <= 0 {
|
||||||
|
return ValidationError{
|
||||||
|
Field: "limit",
|
||||||
|
Err: MinGreaterEqualError{
|
||||||
|
Min: 1,
|
||||||
|
Current: limit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit > ServerListMaxLimit {
|
||||||
|
return ValidationError{
|
||||||
|
Field: "limit",
|
||||||
|
Err: MaxLessEqualError{
|
||||||
|
Max: ServerListMaxLimit,
|
||||||
|
Current: limit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.limit = limit
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) Offset() int {
|
||||||
|
return params.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListServersParams) SetOffset(offset int) error {
|
||||||
|
if offset < 0 {
|
||||||
|
return ValidationError{
|
||||||
|
Field: "offset",
|
||||||
|
Err: MinGreaterEqualError{
|
||||||
|
Min: 0,
|
||||||
|
Current: offset,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.offset = offset
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
40
internal/domain/server_message_payloads.go
Normal file
40
internal/domain/server_message_payloads.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyncServersCmdPayload struct {
|
||||||
|
versionCode string
|
||||||
|
url *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSyncServersCmdPayload(versionCode string, u *url.URL) (SyncServersCmdPayload, error) {
|
||||||
|
if versionCode == "" {
|
||||||
|
return SyncServersCmdPayload{}, errors.New("version code can't be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
if u == nil {
|
||||||
|
return SyncServersCmdPayload{}, errors.New("url can't be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return SyncServersCmdPayload{versionCode: versionCode, url: u}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSyncServersCmdPayloadWithStringURL(versionCode string, rawURL string) (SyncServersCmdPayload, error) {
|
||||||
|
u, err := parseURL(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
return SyncServersCmdPayload{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewSyncServersCmdPayload(versionCode, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p SyncServersCmdPayload) VersionCode() string {
|
||||||
|
return p.versionCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p SyncServersCmdPayload) URL() *url.URL {
|
||||||
|
return p.url
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
package domain_test
|
package domain_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
@ -100,6 +102,255 @@ func TestNewSyncServersCmdPayloadWithStringURL(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewCreateServerParams(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
validVersion := domaintest.NewVersion(t, domaintest.VersionConfig{})
|
||||||
|
validBaseServer := domaintest.NewBaseServer(t, domaintest.BaseServerConfig{})
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
servers domain.BaseServers
|
||||||
|
versionCode string
|
||||||
|
}
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []test{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
versionCode: validVersion.Code(),
|
||||||
|
servers: domain.BaseServers{
|
||||||
|
validBaseServer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, versionCodeTest := range newVersionCodeValidationTests() {
|
||||||
|
tests = append(tests, test{
|
||||||
|
name: versionCodeTest.name,
|
||||||
|
args: args{
|
||||||
|
versionCode: versionCodeTest.code,
|
||||||
|
servers: domain.BaseServers{
|
||||||
|
validBaseServer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Err: versionCodeTest.expectedErr,
|
||||||
|
Field: "versionCode",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
res, err := domain.NewCreateServerParams(tt.args.servers, tt.args.versionCode)
|
||||||
|
require.ErrorIs(t, err, tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Len(t, res, len(tt.args.servers))
|
||||||
|
for i, b := range tt.args.servers {
|
||||||
|
assert.Equal(t, b, res[i].Base())
|
||||||
|
assert.Equal(t, tt.args.versionCode, res[i].VersionCode())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServersParams_SetSort(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sort []domain.ServerSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSort{
|
||||||
|
domain.ServerSortKeyASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) < 1",
|
||||||
|
args: args{
|
||||||
|
sort: nil,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 2,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) > 2",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.ServerSort{
|
||||||
|
domain.ServerSortKeyASC,
|
||||||
|
domain.ServerSortKeyDESC,
|
||||||
|
domain.ServerSortOpenASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 2,
|
||||||
|
Current: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetSort(tt.args.sort), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.sort, params.Sort())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServersParams_SetLimit(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
limit: domain.ServerListMaxLimit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: limit < 1",
|
||||||
|
args: args{
|
||||||
|
limit: 0,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Field: "limit",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 1,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fmt.Sprintf("ERR: limit > %d", domain.ServerListMaxLimit),
|
||||||
|
args: args{
|
||||||
|
limit: domain.ServerListMaxLimit + 1,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Field: "limit",
|
||||||
|
Err: domain.MaxLessEqualError{
|
||||||
|
Max: domain.ServerListMaxLimit,
|
||||||
|
Current: domain.ServerListMaxLimit + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetLimit(tt.args.limit), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.limit, params.Limit())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListServersParams_SetOffset(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
offset: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: offset < 0",
|
||||||
|
args: args{
|
||||||
|
offset: -1,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Field: "offset",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 0,
|
||||||
|
Current: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListServersParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetOffset(tt.args.offset), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.offset, params.Offset())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type serverKeyValidationTest struct {
|
type serverKeyValidationTest struct {
|
||||||
name string
|
name string
|
||||||
key string
|
key string
|
||||||
|
@ -110,7 +361,7 @@ func newServerKeyValidationTests() []serverKeyValidationTest {
|
||||||
return []serverKeyValidationTest{
|
return []serverKeyValidationTest{
|
||||||
{
|
{
|
||||||
name: "ERR: server key length < 1",
|
name: "ERR: server key length < 1",
|
||||||
expectedErr: domain.LenError{
|
expectedErr: domain.LenOutOfRangeError{
|
||||||
Min: 1,
|
Min: 1,
|
||||||
Max: 10,
|
Max: 10,
|
||||||
},
|
},
|
||||||
|
@ -118,7 +369,7 @@ func newServerKeyValidationTests() []serverKeyValidationTest {
|
||||||
{
|
{
|
||||||
name: "ERR: server key length > 10",
|
name: "ERR: server key length > 10",
|
||||||
key: "keykeykeyke",
|
key: "keykeykeyke",
|
||||||
expectedErr: domain.LenError{
|
expectedErr: domain.LenOutOfRangeError{
|
||||||
Min: 1,
|
Min: 1,
|
||||||
Max: 10,
|
Max: 10,
|
||||||
Current: len("keykeykeyke"),
|
Current: len("keykeykeyke"),
|
||||||
|
|
171
internal/domain/validation.go
Normal file
171
internal/domain/validation.go
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gosimple/slug"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValidationError struct {
|
||||||
|
Field string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorWithParams = ValidationError{}
|
||||||
|
|
||||||
|
func (e ValidationError) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %s", e.Field, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ValidationError) Code() ErrorCode {
|
||||||
|
var domainErr Error
|
||||||
|
if errors.As(e.Err, &domainErr) {
|
||||||
|
return domainErr.Code()
|
||||||
|
}
|
||||||
|
return ErrorCodeIncorrectInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ValidationError) Slug() string {
|
||||||
|
s := "validation"
|
||||||
|
var domainErr Error
|
||||||
|
if errors.As(e.Err, &domainErr) {
|
||||||
|
s = domainErr.Slug()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s-%s", slug.Make(e.Field), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ValidationError) Params() map[string]any {
|
||||||
|
var withParams ErrorWithParams
|
||||||
|
ok := errors.As(e.Err, &withParams)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return withParams.Params()
|
||||||
|
}
|
||||||
|
|
||||||
|
type MinGreaterEqualError struct {
|
||||||
|
Min int
|
||||||
|
Current int
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorWithParams = MinGreaterEqualError{}
|
||||||
|
|
||||||
|
func (e MinGreaterEqualError) Error() string {
|
||||||
|
return fmt.Sprintf("must be no less than %d (current: %d)", e.Min, e.Current)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MinGreaterEqualError) Code() ErrorCode {
|
||||||
|
return ErrorCodeIncorrectInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MinGreaterEqualError) Slug() string {
|
||||||
|
return "min-greater-equal"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MinGreaterEqualError) Params() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"Min": e.Min,
|
||||||
|
"Current": e.Current,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MaxLessEqualError struct {
|
||||||
|
Max int
|
||||||
|
Current int
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorWithParams = MaxLessEqualError{}
|
||||||
|
|
||||||
|
func (e MaxLessEqualError) Error() string {
|
||||||
|
return fmt.Sprintf("must be no greater than %d (current: %d)", e.Max, e.Current)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MaxLessEqualError) Code() ErrorCode {
|
||||||
|
return ErrorCodeIncorrectInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MaxLessEqualError) Slug() string {
|
||||||
|
return "max-less-equal"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e MaxLessEqualError) Params() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"Max": e.Max,
|
||||||
|
"Current": e.Current,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LenOutOfRangeError struct {
|
||||||
|
Min int
|
||||||
|
Max int
|
||||||
|
Current int
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorWithParams = LenOutOfRangeError{}
|
||||||
|
|
||||||
|
func (e LenOutOfRangeError) Error() string {
|
||||||
|
return fmt.Sprintf("length must be between %d and %d (current length: %d)", e.Min, e.Max, e.Current)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e LenOutOfRangeError) Code() ErrorCode {
|
||||||
|
return ErrorCodeIncorrectInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e LenOutOfRangeError) Slug() string {
|
||||||
|
return "length-out-of-range"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e LenOutOfRangeError) Params() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"Min": e.Min,
|
||||||
|
"Max": e.Max,
|
||||||
|
"Current": e.Current,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSliceLen[S ~[]E, E any](s S, min, max int) error {
|
||||||
|
if l := len(s); l > max || l < min {
|
||||||
|
return LenOutOfRangeError{
|
||||||
|
Min: min,
|
||||||
|
Max: max,
|
||||||
|
Current: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
serverKeyMinLength = 1
|
||||||
|
serverKeyMaxLength = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateServerKey(key string) error {
|
||||||
|
if l := len(key); l < serverKeyMinLength || l > serverKeyMaxLength {
|
||||||
|
return LenOutOfRangeError{
|
||||||
|
Min: serverKeyMinLength,
|
||||||
|
Max: serverKeyMaxLength,
|
||||||
|
Current: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
versionCodeMinLength = 2
|
||||||
|
versionCodeMaxLength = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateVersionCode(code string) error {
|
||||||
|
if l := len(code); l < versionCodeMinLength || l > versionCodeMaxLength {
|
||||||
|
return LenOutOfRangeError{
|
||||||
|
Min: versionCodeMinLength,
|
||||||
|
Max: versionCodeMaxLength,
|
||||||
|
Current: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
package domain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LenError struct {
|
|
||||||
Min int
|
|
||||||
Max int
|
|
||||||
Current int
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ErrorWithParams = LenError{}
|
|
||||||
|
|
||||||
func (e LenError) Error() string {
|
|
||||||
return fmt.Sprintf("length must be between %d and %d (current length: %d)", e.Min, e.Max, e.Current)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e LenError) Code() ErrorCode {
|
|
||||||
return ErrorCodeIncorrectInput
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e LenError) Slug() string {
|
|
||||||
return "length"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e LenError) Params() map[string]any {
|
|
||||||
return map[string]any{
|
|
||||||
"Min": e.Min,
|
|
||||||
"Max": e.Max,
|
|
||||||
"Current": e.Current,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateSliceLen[S ~[]E, E any](s S, min, max int) error {
|
|
||||||
if l := len(s); l > max || l < min {
|
|
||||||
return LenError{
|
|
||||||
Min: min,
|
|
||||||
Max: max,
|
|
||||||
Current: l,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
serverKeyMinLength = 1
|
|
||||||
serverKeyMaxLength = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
func validateServerKey(key string) error {
|
|
||||||
if l := len(key); l < serverKeyMinLength || l > serverKeyMaxLength {
|
|
||||||
return LenError{
|
|
||||||
Min: serverKeyMinLength,
|
|
||||||
Max: serverKeyMaxLength,
|
|
||||||
Current: l,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -91,6 +91,10 @@ const (
|
||||||
versionSortMaxLength = 1
|
versionSortMaxLength = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (params *ListVersionsParams) Sort() []VersionSort {
|
||||||
|
return params.sort
|
||||||
|
}
|
||||||
|
|
||||||
func (params *ListVersionsParams) SetSort(sort []VersionSort) error {
|
func (params *ListVersionsParams) SetSort(sort []VersionSort) error {
|
||||||
if err := validateSliceLen(sort, versionSortMinLength, versionSortMaxLength); err != nil {
|
if err := validateSliceLen(sort, versionSortMinLength, versionSortMaxLength); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
|
@ -103,7 +107,3 @@ func (params *ListVersionsParams) SetSort(sort []VersionSort) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListVersionsParams) Sort() []VersionSort {
|
|
||||||
return params.sort
|
|
||||||
}
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ func TestListVersionsParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedErr: domain.ValidationError{
|
expectedErr: domain.ValidationError{
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
Err: domain.LenError{
|
Err: domain.LenOutOfRangeError{
|
||||||
Min: 1,
|
Min: 1,
|
||||||
Max: 1,
|
Max: 1,
|
||||||
Current: 0,
|
Current: 0,
|
||||||
|
@ -52,7 +52,7 @@ func TestListVersionsParams_SetSort(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedErr: domain.ValidationError{
|
expectedErr: domain.ValidationError{
|
||||||
Field: "sort",
|
Field: "sort",
|
||||||
Err: domain.LenError{
|
Err: domain.LenOutOfRangeError{
|
||||||
Min: 1,
|
Min: 1,
|
||||||
Max: 1,
|
Max: 1,
|
||||||
Current: 2,
|
Current: 2,
|
||||||
|
@ -77,3 +77,32 @@ func TestListVersionsParams_SetSort(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type versionCodeValidationTest struct {
|
||||||
|
name string
|
||||||
|
code string
|
||||||
|
expectedErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVersionCodeValidationTests() []versionCodeValidationTest {
|
||||||
|
return []versionCodeValidationTest{
|
||||||
|
{
|
||||||
|
name: "ERR: version code length < 2",
|
||||||
|
code: "p",
|
||||||
|
expectedErr: domain.LenOutOfRangeError{
|
||||||
|
Min: 2,
|
||||||
|
Max: 2,
|
||||||
|
Current: len("p"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: version code length > 2",
|
||||||
|
code: "pll",
|
||||||
|
expectedErr: domain.LenOutOfRangeError{
|
||||||
|
Min: 2,
|
||||||
|
Max: 2,
|
||||||
|
Current: len("pll"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func testObserver(t *testing.T, newObserver func(t *testing.T) *healthfile.Obser
|
||||||
case <-observerStopped:
|
case <-observerStopped:
|
||||||
// OK
|
// OK
|
||||||
default:
|
default:
|
||||||
t.Error("Run goroutine not stopped")
|
assert.Fail(t, "Run goroutine not stopped")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user