parent
b5b699ca49
commit
9668c23cc8
|
@ -92,7 +92,7 @@ var cmdConsumer = &cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
consumer := port.NewTribeWatermillConsumer(
|
consumer := port.NewTribeWatermillConsumer(
|
||||||
app.NewTribeService(twSvc),
|
app.NewTribeService(adapter.NewTribeBunRepository(db), twSvc),
|
||||||
subscriber,
|
subscriber,
|
||||||
logger,
|
logger,
|
||||||
marshaler,
|
marshaler,
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -11,7 +11,6 @@ require (
|
||||||
github.com/go-chi/chi/v5 v5.0.10
|
github.com/go-chi/chi/v5 v5.0.10
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.3.1
|
||||||
github.com/gosimple/slug v1.13.1
|
|
||||||
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
|
||||||
|
@ -20,6 +19,7 @@ require (
|
||||||
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
|
||||||
github.com/uptrace/bun/driver/sqliteshim v1.1.16
|
github.com/uptrace/bun/driver/sqliteshim v1.1.16
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.1.16
|
||||||
github.com/urfave/cli/v2 v2.26.0
|
github.com/urfave/cli/v2 v2.26.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,15 +36,16 @@ require (
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/fatih/color v1.15.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/gosimple/unidecode v1.0.1 // indirect
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7 // indirect
|
github.com/lithammer/shortuuid/v3 v3.0.7 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
|
@ -68,9 +69,9 @@ require (
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
golang.org/x/crypto v0.13.0 // indirect
|
golang.org/x/crypto v0.13.0 // indirect
|
||||||
golang.org/x/mod v0.12.0 // indirect
|
golang.org/x/mod v0.14.0 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.12.0 // indirect
|
||||||
golang.org/x/tools v0.13.0 // indirect
|
golang.org/x/tools v0.16.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/uint128 v1.3.0 // indirect
|
lukechampine.com/uint128 v1.3.0 // indirect
|
||||||
|
|
24
go.sum
24
go.sum
|
@ -42,6 +42,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/elliotchance/phpserialize v1.3.3 h1:hV4QVmGdCiYgoBbw+ADt6fNgyZ2mYX0OgpnON1adTCM=
|
github.com/elliotchance/phpserialize v1.3.3 h1:hV4QVmGdCiYgoBbw+ADt6fNgyZ2mYX0OgpnON1adTCM=
|
||||||
github.com/elliotchance/phpserialize v1.3.3/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
|
github.com/elliotchance/phpserialize v1.3.3/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
|
||||||
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
|
@ -65,10 +67,6 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
|
|
||||||
github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
|
||||||
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
|
||||||
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
@ -92,6 +90,9 @@ github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2 h1:hRGSmZu7j271trc9sneMrpOW
|
||||||
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8=
|
github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8=
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
|
github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||||
|
@ -159,6 +160,8 @@ github.com/uptrace/bun/driver/pgdriver v1.1.16 h1:b/NiSXk6Ldw7KLfMLbOqIkm4odHd7Q
|
||||||
github.com/uptrace/bun/driver/pgdriver v1.1.16/go.mod h1:Rmfbc+7lx1z/umjMyAxkOHK81LgnGj71XC5YpA6k1vU=
|
github.com/uptrace/bun/driver/pgdriver v1.1.16/go.mod h1:Rmfbc+7lx1z/umjMyAxkOHK81LgnGj71XC5YpA6k1vU=
|
||||||
github.com/uptrace/bun/driver/sqliteshim v1.1.16 h1:dhXrdXQegGSM+Jk07s9p1Y575DMhCKl7wBkss8kgM+8=
|
github.com/uptrace/bun/driver/sqliteshim v1.1.16 h1:dhXrdXQegGSM+Jk07s9p1Y575DMhCKl7wBkss8kgM+8=
|
||||||
github.com/uptrace/bun/driver/sqliteshim v1.1.16/go.mod h1:7fJaPqaiSvnWytQcMwVv1ZcjUJh7tGDonO/zq3O6RRI=
|
github.com/uptrace/bun/driver/sqliteshim v1.1.16/go.mod h1:7fJaPqaiSvnWytQcMwVv1ZcjUJh7tGDonO/zq3O6RRI=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.1.16 h1:SgicRQGtnjhrIhlYOxdkOm1Em4s6HykmT3JblHnoTBM=
|
||||||
|
github.com/uptrace/bun/extra/bundebug v1.1.16/go.mod h1:SkiOkfUirBiO1Htc4s5bQKEq+JSeU1TkBVpMsPz2ePM=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
||||||
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
|
@ -187,8 +190,8 @@ golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
@ -198,8 +201,8 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -212,6 +215,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
@ -223,8 +227,8 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -19,6 +19,7 @@ func NewFixture(bunDB *bun.DB) *Fixture {
|
||||||
bunDB.RegisterModel(
|
bunDB.RegisterModel(
|
||||||
(*bunmodel.Version)(nil),
|
(*bunmodel.Version)(nil),
|
||||||
(*bunmodel.Server)(nil),
|
(*bunmodel.Server)(nil),
|
||||||
|
(*bunmodel.Tribe)(nil),
|
||||||
)
|
)
|
||||||
return &Fixture{
|
return &Fixture{
|
||||||
f: dbfixture.New(bunDB),
|
f: dbfixture.New(bunDB),
|
||||||
|
|
|
@ -141,7 +141,7 @@ func (p *Postgres) NewBunDB(tb TestingTB) *bun.DB {
|
||||||
|
|
||||||
schema := generatePostgresSchema()
|
schema := generatePostgresSchema()
|
||||||
|
|
||||||
sqldb := sql.OpenDB(
|
sqlDB := sql.OpenDB(
|
||||||
pgdriver.NewConnector(
|
pgdriver.NewConnector(
|
||||||
pgdriver.WithDSN(p.connectionString.String()),
|
pgdriver.WithDSN(p.connectionString.String()),
|
||||||
pgdriver.WithConnParams(map[string]any{
|
pgdriver.WithConnParams(map[string]any{
|
||||||
|
@ -150,11 +150,13 @@ func (p *Postgres) NewBunDB(tb TestingTB) *bun.DB {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
bunDB := bun.NewDB(sqldb, pgdialect.New())
|
bunDB := bun.NewDB(sqlDB, pgdialect.New())
|
||||||
tb.Cleanup(func() {
|
tb.Cleanup(func() {
|
||||||
_ = bunDB.Close()
|
_ = bunDB.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
bunDB.AddQueryHook(newBunDebugHook())
|
||||||
|
|
||||||
bo := backoff.NewExponentialBackOff()
|
bo := backoff.NewExponentialBackOff()
|
||||||
bo.MaxInterval = postgresPingBackOffMaxInterval
|
bo.MaxInterval = postgresPingBackOffMaxInterval
|
||||||
bo.MaxElapsedTime = postgresPingBackOffMaxElapsedTime
|
bo.MaxElapsedTime = postgresPingBackOffMaxElapsedTime
|
||||||
|
|
|
@ -12,15 +12,17 @@ import (
|
||||||
// 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:")
|
sqlDB, err := sql.Open(sqliteshim.ShimName, "file::memory:")
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
sqldb.SetMaxOpenConns(1)
|
sqlDB.SetMaxOpenConns(1)
|
||||||
sqldb.SetMaxIdleConns(1)
|
sqlDB.SetMaxIdleConns(1)
|
||||||
sqldb.SetConnMaxLifetime(0)
|
sqlDB.SetConnMaxLifetime(0)
|
||||||
|
|
||||||
db := bun.NewDB(sqldb, sqlitedialect.New())
|
bunDB := bun.NewDB(sqlDB, sqlitedialect.New())
|
||||||
|
|
||||||
runMigrations(tb, db)
|
bunDB.AddQueryHook(newBunDebugHook())
|
||||||
|
|
||||||
return db
|
runMigrations(tb, bunDB)
|
||||||
|
|
||||||
|
return bunDB
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/ory/dockertest/v3"
|
"github.com/ory/dockertest/v3"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/extra/bundebug"
|
||||||
)
|
)
|
||||||
|
|
||||||
const migrationTimeout = 10 * time.Second
|
const migrationTimeout = 10 * time.Second
|
||||||
|
@ -55,3 +56,7 @@ func retry(bo backoff.BackOff, op backoff.Operation) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newBunDebugHook() *bundebug.QueryHook {
|
||||||
|
return bundebug.NewQueryHook(bundebug.FromEnv("BUNDEBUG"))
|
||||||
|
}
|
||||||
|
|
14
internal/adapter/bun_utils.go
Normal file
14
internal/adapter/bun_utils.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package adapter
|
||||||
|
|
||||||
|
import "github.com/uptrace/bun"
|
||||||
|
|
||||||
|
func appendODSetClauses(q *bun.InsertQuery) *bun.InsertQuery {
|
||||||
|
return q.Set("rank_att = EXCLUDED.rank_att").
|
||||||
|
Set("score_att = EXCLUDED.score_att").
|
||||||
|
Set("rank_def = EXCLUDED.rank_def").
|
||||||
|
Set("score_def = EXCLUDED.score_def").
|
||||||
|
Set("rank_sup = EXCLUDED.rank_sup").
|
||||||
|
Set("score_sup = EXCLUDED.score_sup").
|
||||||
|
Set("rank_total = EXCLUDED.rank_total").
|
||||||
|
Set("score_total = EXCLUDED.score_total")
|
||||||
|
}
|
48
internal/adapter/internal/bunmodel/opponents_defeated.go
Normal file
48
internal/adapter/internal/bunmodel/opponents_defeated.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package bunmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OpponentsDefeated struct {
|
||||||
|
RankAtt int `bun:"rank_att"`
|
||||||
|
ScoreAtt int `bun:"score_att"`
|
||||||
|
RankDef int `bun:"rank_def"`
|
||||||
|
ScoreDef int `bun:"score_def"`
|
||||||
|
RankSup int `bun:"rank_sup"`
|
||||||
|
ScoreSup int `bun:"score_sup"`
|
||||||
|
RankTotal int `bun:"rank_total"`
|
||||||
|
ScoreTotal int `bun:"score_total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOpponentsDefeated(od domain.OpponentsDefeated) OpponentsDefeated {
|
||||||
|
return OpponentsDefeated{
|
||||||
|
RankAtt: od.RankAtt(),
|
||||||
|
ScoreAtt: od.ScoreAtt(),
|
||||||
|
RankDef: od.RankDef(),
|
||||||
|
ScoreDef: od.ScoreDef(),
|
||||||
|
RankSup: od.RankSup(),
|
||||||
|
ScoreSup: od.ScoreSup(),
|
||||||
|
RankTotal: od.RankTotal(),
|
||||||
|
ScoreTotal: od.ScoreTotal(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (od OpponentsDefeated) ToDomain() (domain.OpponentsDefeated, error) {
|
||||||
|
converted, err := domain.NewOpponentsDefeated(
|
||||||
|
od.RankAtt,
|
||||||
|
od.ScoreAtt,
|
||||||
|
od.RankDef,
|
||||||
|
od.ScoreDef,
|
||||||
|
od.RankSup,
|
||||||
|
od.ScoreSup,
|
||||||
|
od.RankTotal,
|
||||||
|
od.ScoreTotal,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OpponentsDefeated{}, fmt.Errorf("couldn't construct domain.OpponentsDefeated: %w", err)
|
||||||
|
}
|
||||||
|
return converted, nil
|
||||||
|
}
|
87
internal/adapter/internal/bunmodel/tribe.go
Normal file
87
internal/adapter/internal/bunmodel/tribe.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package bunmodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tribe struct {
|
||||||
|
bun.BaseModel `bun:"table:tribes,alias:tribe"`
|
||||||
|
|
||||||
|
ID int `bun:"id,nullzero,pk"`
|
||||||
|
ServerKey string `bun:"server_key,nullzero,pk"`
|
||||||
|
Name string `bun:"name,nullzero"`
|
||||||
|
Tag string `bun:"tag,nullzero"`
|
||||||
|
NumMembers int `bun:"num_members"`
|
||||||
|
NumVillages int `bun:"num_villages"`
|
||||||
|
Points int `bun:"points"`
|
||||||
|
AllPoints int `bun:"all_points"`
|
||||||
|
Rank int `bun:"rank"`
|
||||||
|
Dominance float64 `bun:"dominance"`
|
||||||
|
ProfileURL string `bun:"profile_url,nullzero"`
|
||||||
|
BestRank int `bun:"best_rank,nullzero,default:999999"`
|
||||||
|
BestRankAt time.Time `bun:"best_rank_at,nullzero,default:CURRENT_TIMESTAMP"`
|
||||||
|
MostPoints int `bun:"most_points"`
|
||||||
|
MostPointsAt time.Time `bun:"most_points_at,nullzero,default:CURRENT_TIMESTAMP"`
|
||||||
|
MostVillages int `bun:"most_villages"`
|
||||||
|
MostVillagesAt time.Time `bun:"most_villages_at,nullzero,default:CURRENT_TIMESTAMP"`
|
||||||
|
CreatedAt time.Time `bun:"created_at,nullzero,default:CURRENT_TIMESTAMP"`
|
||||||
|
DeletedAt time.Time `bun:"deleted_at,nullzero"`
|
||||||
|
|
||||||
|
OpponentsDefeated
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tribe) ToDomain() (domain.Tribe, error) {
|
||||||
|
od, err := t.OpponentsDefeated.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return domain.Tribe{}, fmt.Errorf("couldn't construct domain.Tribe (id=%d,serverKey=%s): %w", t.ID, t.ServerKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
converted, err := domain.UnmarshalTribeFromDatabase(
|
||||||
|
t.ID,
|
||||||
|
t.ServerKey,
|
||||||
|
t.Name,
|
||||||
|
t.Tag,
|
||||||
|
t.NumMembers,
|
||||||
|
t.NumVillages,
|
||||||
|
t.Points,
|
||||||
|
t.AllPoints,
|
||||||
|
t.Rank,
|
||||||
|
od,
|
||||||
|
t.ProfileURL,
|
||||||
|
t.Dominance,
|
||||||
|
t.BestRank,
|
||||||
|
t.BestRankAt,
|
||||||
|
t.MostPoints,
|
||||||
|
t.MostPointsAt,
|
||||||
|
t.MostVillages,
|
||||||
|
t.MostVillagesAt,
|
||||||
|
t.CreatedAt,
|
||||||
|
t.DeletedAt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return domain.Tribe{}, fmt.Errorf("couldn't construct domain.Tribe (id=%d,serverKey=%s): %w", t.ID, t.ServerKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tribes []Tribe
|
||||||
|
|
||||||
|
func (ts Tribes) ToDomain() (domain.Tribes, error) {
|
||||||
|
res := make(domain.Tribes, 0, len(ts))
|
||||||
|
|
||||||
|
for _, t := range ts {
|
||||||
|
converted, err := t.ToDomain()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, converted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
|
@ -146,8 +146,6 @@ func (a listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
q = q.Where("server.special = ?", special.Value)
|
q = q.Where("server.special = ?", special.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
q = q.Limit(a.params.Limit()).Offset(a.params.Offset())
|
|
||||||
|
|
||||||
for _, s := range a.params.Sort() {
|
for _, s := range a.params.Sort() {
|
||||||
switch s {
|
switch s {
|
||||||
case domain.ServerSortKeyASC:
|
case domain.ServerSortKeyASC:
|
||||||
|
@ -163,5 +161,5 @@ func (a listServersParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return q
|
return q.Limit(a.params.Limit()).Offset(a.params.Offset())
|
||||||
}
|
}
|
||||||
|
|
167
internal/adapter/repository_bun_tribe.go
Normal file
167
internal/adapter/repository_bun_tribe.go
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
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 TribeBunRepository struct {
|
||||||
|
db bun.IDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTribeBunRepository(db bun.IDB) *TribeBunRepository {
|
||||||
|
return &TribeBunRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *TribeBunRepository) CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error {
|
||||||
|
if len(params) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tribes := make(bunmodel.Tribes, 0, len(params))
|
||||||
|
|
||||||
|
for _, p := range params {
|
||||||
|
base := p.Base()
|
||||||
|
tribes = append(tribes, bunmodel.Tribe{
|
||||||
|
ID: base.ID(),
|
||||||
|
ServerKey: p.ServerKey(),
|
||||||
|
Name: base.Name(),
|
||||||
|
Tag: base.Tag(),
|
||||||
|
NumMembers: base.NumMembers(),
|
||||||
|
NumVillages: base.NumVillages(),
|
||||||
|
Points: base.Points(),
|
||||||
|
AllPoints: base.AllPoints(),
|
||||||
|
Rank: base.Rank(),
|
||||||
|
ProfileURL: base.ProfileURL().String(),
|
||||||
|
BestRank: p.BestRank(),
|
||||||
|
BestRankAt: p.BestRankAt(),
|
||||||
|
MostPoints: p.MostPoints(),
|
||||||
|
MostPointsAt: p.MostPointsAt(),
|
||||||
|
MostVillages: p.MostVillages(),
|
||||||
|
MostVillagesAt: p.MostVillagesAt(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
OpponentsDefeated: bunmodel.NewOpponentsDefeated(base.OD()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
q := repo.db.NewInsert().
|
||||||
|
Model(&tribes)
|
||||||
|
|
||||||
|
//nolint:exhaustive
|
||||||
|
switch q.Dialect().Name() {
|
||||||
|
case dialect.PG:
|
||||||
|
q = q.On("CONFLICT ON CONSTRAINT tribes_pkey DO UPDATE")
|
||||||
|
case dialect.SQLite:
|
||||||
|
q = q.On("CONFLICT(id, server_key) DO UPDATE")
|
||||||
|
default:
|
||||||
|
q = q.Err(errors.New("unsupported dialect"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := q.
|
||||||
|
Set("name = EXCLUDED.name").
|
||||||
|
Set("tag = EXCLUDED.tag").
|
||||||
|
Set("num_members = EXCLUDED.num_members").
|
||||||
|
Set("num_villages = EXCLUDED.num_villages").
|
||||||
|
Set("points = EXCLUDED.points").
|
||||||
|
Set("all_points = EXCLUDED.all_points").
|
||||||
|
Set("rank = EXCLUDED.rank").
|
||||||
|
Set("profile_url = EXCLUDED.profile_url").
|
||||||
|
Set("best_rank = EXCLUDED.best_rank").
|
||||||
|
Set("best_rank_at = EXCLUDED.best_rank_at").
|
||||||
|
Set("most_villages = EXCLUDED.most_villages").
|
||||||
|
Set("most_villages_at = EXCLUDED.most_villages_at").
|
||||||
|
Set("most_points = EXCLUDED.most_points").
|
||||||
|
Set("most_points_at = EXCLUDED.most_points_at").
|
||||||
|
Set("deleted_at = EXCLUDED.deleted_at").
|
||||||
|
Apply(appendODSetClauses).
|
||||||
|
Returning("").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return fmt.Errorf("something went wrong while inserting tribes into the db: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *TribeBunRepository) List(ctx context.Context, params domain.ListTribesParams) (domain.Tribes, error) {
|
||||||
|
var servers bunmodel.Tribes
|
||||||
|
|
||||||
|
if err := repo.db.NewSelect().
|
||||||
|
Model(&servers).
|
||||||
|
Apply(listTribesParamsApplier{params: params}.apply).
|
||||||
|
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, fmt.Errorf("couldn't select tribes from the db: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers.ToDomain()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *TribeBunRepository) Delete(ctx context.Context, serverKey string, ids ...int) error {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := repo.db.NewUpdate().
|
||||||
|
Model((*bunmodel.Tribe)(nil)).
|
||||||
|
Where("deleted_at IS NULL").
|
||||||
|
Where("id IN (?)", bun.In(ids)).
|
||||||
|
Where("server_key = ?", serverKey).
|
||||||
|
Set("deleted_at = ?", time.Now()).
|
||||||
|
Returning("").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return fmt.Errorf("couldn't delete tribes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type listTribesParamsApplier struct {
|
||||||
|
params domain.ListTribesParams
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (a listTribesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
if ids := a.params.IDs(); len(ids) > 0 {
|
||||||
|
q = q.Where("tribe.id IN (?)", bun.In(ids))
|
||||||
|
}
|
||||||
|
|
||||||
|
if idGT := a.params.IDGT(); idGT.Valid {
|
||||||
|
q = q.Where("tribe.id > ?", idGT.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serverKeys := a.params.ServerKeys(); len(serverKeys) > 0 {
|
||||||
|
q = q.Where("tribe.server_key IN (?)", bun.In(serverKeys))
|
||||||
|
}
|
||||||
|
|
||||||
|
if deleted := a.params.Deleted(); deleted.Valid {
|
||||||
|
if deleted.Value {
|
||||||
|
q = q.Where("tribe.deleted_at IS NOT NULL")
|
||||||
|
} else {
|
||||||
|
q = q.Where("tribe.deleted_at IS NULL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range a.params.Sort() {
|
||||||
|
switch s {
|
||||||
|
case domain.TribeSortIDASC:
|
||||||
|
q = q.Order("tribe.id ASC")
|
||||||
|
case domain.TribeSortIDDESC:
|
||||||
|
q = q.Order("tribe.id DESC")
|
||||||
|
case domain.TribeSortServerKeyASC:
|
||||||
|
q = q.Order("tribe.server_key ASC")
|
||||||
|
case domain.TribeSortServerKeyDESC:
|
||||||
|
q = q.Order("tribe.server_key DESC")
|
||||||
|
default:
|
||||||
|
return q.Err(errors.New("unsupported sort value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.Limit(a.params.Limit()).Offset(a.params.Offset())
|
||||||
|
}
|
29
internal/adapter/repository_bun_tribe_test.go
Normal file
29
internal/adapter/repository_bun_tribe_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package adapter_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/adapter/adaptertest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTribeBunRepository_Postgres(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping long-running test")
|
||||||
|
}
|
||||||
|
|
||||||
|
testTribeRepository(t, func(t *testing.T) repositories {
|
||||||
|
t.Helper()
|
||||||
|
return newBunDBRepositories(t, postgres.NewBunDB(t))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTribeBunRepository_SQLite(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testTribeRepository(t, func(t *testing.T) repositories {
|
||||||
|
t.Helper()
|
||||||
|
return newBunDBRepositories(t, adaptertest.NewBunDBSQLite(t))
|
||||||
|
})
|
||||||
|
}
|
|
@ -4,13 +4,11 @@ import (
|
||||||
"cmp"
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
||||||
"github.com/brianvoe/gofakeit/v6"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -23,11 +21,39 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
t.Run("CreateOrUpdate", func(t *testing.T) {
|
t.Run("CreateOrUpdate", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
assertCreatedUpdated := func(t *testing.T, params []domain.CreateServerParams) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
require.NotEmpty(t, params)
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(params))
|
||||||
|
for _, p := range params {
|
||||||
|
keys = append(keys, p.Base().Key())
|
||||||
|
}
|
||||||
|
|
||||||
|
listParams := domain.NewListServersParams()
|
||||||
|
require.NoError(t, listParams.SetKeys(keys))
|
||||||
|
|
||||||
|
servers, err := repos.server.List(ctx, listParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, servers, len(params))
|
||||||
|
for i, p := range params {
|
||||||
|
idx := slices.IndexFunc(servers, func(server domain.Server) bool {
|
||||||
|
return server.Key() == p.Base().Key()
|
||||||
|
})
|
||||||
|
require.GreaterOrEqualf(t, idx, 0, "params[%d]", i)
|
||||||
|
server := servers[idx]
|
||||||
|
|
||||||
|
assert.Equalf(t, p.Base(), server.Base(), "params[%d]", i)
|
||||||
|
assert.Equalf(t, p.VersionCode(), server.VersionCode(), "params[%d]", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
t.Run("OK", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
repos := newRepos(t)
|
|
||||||
|
|
||||||
versions, err := repos.version.List(ctx, domain.NewListVersionsParams())
|
versions, err := repos.version.List(ctx, domain.NewListVersionsParams())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEmpty(t, versions)
|
require.NotEmpty(t, versions)
|
||||||
|
@ -42,31 +68,11 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.NoError(t, repos.server.CreateOrUpdate(ctx, createParams...))
|
require.NoError(t, repos.server.CreateOrUpdate(ctx, createParams...))
|
||||||
|
assertCreatedUpdated(t, 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{
|
serversToUpdate := domain.BaseServers{
|
||||||
domaintest.NewBaseServer(t, func(cfg *domaintest.BaseServerConfig) {
|
domaintest.NewBaseServer(t, func(cfg *domaintest.BaseServerConfig) {
|
||||||
cfg.Key = serversToCreate[0].Key()
|
cfg.Key = serversToCreate[0].Key()
|
||||||
cfg.URL = randURL(t)
|
|
||||||
cfg.Open = !serversToCreate[0].Open()
|
cfg.Open = !serversToCreate[0].Open()
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -75,26 +81,13 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.NoError(t, repos.server.CreateOrUpdate(ctx, updateParams...))
|
require.NoError(t, repos.server.CreateOrUpdate(ctx, updateParams...))
|
||||||
|
assertCreatedUpdated(t, updateParams)
|
||||||
|
})
|
||||||
|
|
||||||
keys = make([]string, 0, len(serversToUpdate))
|
t.Run("OK: len(params) == 0", func(t *testing.T) {
|
||||||
for _, s := range serversToUpdate {
|
t.Parallel()
|
||||||
keys = append(keys, s.Key())
|
|
||||||
}
|
|
||||||
|
|
||||||
listUpdatedServersParams := domain.NewListServersParams()
|
require.NoError(t, repos.server.CreateOrUpdate(ctx))
|
||||||
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()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -447,10 +440,3 @@ func testServerRepository(t *testing.T, newRepos func(t *testing.T) repositories
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func randURL(tb testing.TB) *url.URL {
|
|
||||||
tb.Helper()
|
|
||||||
u, err := url.Parse("https://" + gofakeit.DomainName())
|
|
||||||
require.NoError(tb, err)
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,9 +25,16 @@ type serverRepository interface {
|
||||||
Update(ctx context.Context, key string, params domain.UpdateServerParams) error
|
Update(ctx context.Context, key string, params domain.UpdateServerParams) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type tribeRepository interface {
|
||||||
|
CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error
|
||||||
|
List(ctx context.Context, params domain.ListTribesParams) (domain.Tribes, error)
|
||||||
|
Delete(ctx context.Context, serverKey string, ids ...int) error
|
||||||
|
}
|
||||||
|
|
||||||
type repositories struct {
|
type repositories struct {
|
||||||
version versionRepository
|
version versionRepository
|
||||||
server serverRepository
|
server serverRepository
|
||||||
|
tribe tribeRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBunDBRepositories(tb testing.TB, bunDB *bun.DB) repositories {
|
func newBunDBRepositories(tb testing.TB, bunDB *bun.DB) repositories {
|
||||||
|
@ -38,5 +45,6 @@ func newBunDBRepositories(tb testing.TB, bunDB *bun.DB) repositories {
|
||||||
return repositories{
|
return repositories{
|
||||||
version: adapter.NewVersionBunRepository(bunDB),
|
version: adapter.NewVersionBunRepository(bunDB),
|
||||||
server: adapter.NewServerBunRepository(bunDB),
|
server: adapter.NewServerBunRepository(bunDB),
|
||||||
|
tribe: adapter.NewTribeBunRepository(bunDB),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
390
internal/adapter/repository_tribe_test.go
Normal file
390
internal/adapter/repository_tribe_test.go
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
package adapter_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain/domaintest"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testTribeRepository(t *testing.T, newRepos func(t *testing.T) repositories) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
t.Run("CreateOrUpdate", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
assertCreatedUpdated := func(t *testing.T, params []domain.CreateTribeParams) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
require.NotEmpty(t, params)
|
||||||
|
|
||||||
|
ids := make([]int, 0, len(params))
|
||||||
|
for _, p := range params {
|
||||||
|
ids = append(ids, p.Base().ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
listParams := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, listParams.SetIDs(ids))
|
||||||
|
require.NoError(t, listParams.SetServerKeys([]string{params[0].ServerKey()}))
|
||||||
|
|
||||||
|
tribes, err := repos.tribe.List(ctx, listParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, tribes, len(params))
|
||||||
|
for i, p := range params {
|
||||||
|
idx := slices.IndexFunc(tribes, func(tribe domain.Tribe) bool {
|
||||||
|
return tribe.ID() == p.Base().ID() && tribe.ServerKey() == p.ServerKey()
|
||||||
|
})
|
||||||
|
require.GreaterOrEqualf(t, idx, 0, "params[%d]", i)
|
||||||
|
tribe := tribes[idx]
|
||||||
|
|
||||||
|
assert.Equalf(t, p.Base(), tribe.Base(), "params[%d]", i)
|
||||||
|
assert.Equalf(t, p.ServerKey(), tribe.ServerKey(), "params[%d]", i)
|
||||||
|
assert.Equalf(t, p.BestRank(), tribe.BestRank(), "params[%d]", i)
|
||||||
|
assert.WithinDurationf(t, p.BestRankAt(), tribe.BestRankAt(), time.Minute, "params[%d]", i)
|
||||||
|
assert.Equalf(t, p.MostVillages(), tribe.MostVillages(), "params[%d]", i)
|
||||||
|
assert.WithinDurationf(t, p.MostVillagesAt(), tribe.MostVillagesAt(), time.Minute, "params[%d]", i)
|
||||||
|
assert.Equalf(t, p.MostPoints(), tribe.MostPoints(), "params[%d]", i)
|
||||||
|
assert.WithinDurationf(t, p.MostPointsAt(), tribe.MostPointsAt(), time.Minute, "params[%d]", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
servers, err := repos.server.List(ctx, domain.NewListServersParams())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, servers)
|
||||||
|
server := servers[0]
|
||||||
|
|
||||||
|
tribesToCreate := domain.BaseTribes{
|
||||||
|
domaintest.NewBaseTribe(t),
|
||||||
|
domaintest.NewBaseTribe(t),
|
||||||
|
}
|
||||||
|
|
||||||
|
createParams, err := domain.NewCreateTribeParams(server.Key(), tribesToCreate, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, repos.tribe.CreateOrUpdate(ctx, createParams...))
|
||||||
|
assertCreatedUpdated(t, createParams)
|
||||||
|
|
||||||
|
tribesToUpdate := domain.BaseTribes{
|
||||||
|
domaintest.NewBaseTribe(t, func(cfg *domaintest.BaseTribeConfig) {
|
||||||
|
cfg.ID = tribesToCreate[0].ID()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParams, err := domain.NewCreateTribeParams(server.Key(), tribesToUpdate, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, repos.tribe.CreateOrUpdate(ctx, updateParams...))
|
||||||
|
assertCreatedUpdated(t, updateParams)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: len(params) == 0", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
require.NoError(t, repos.tribe.CreateOrUpdate(ctx))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("List & ListCount", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
tribes, listTribesErr := repos.tribe.List(ctx, domain.NewListTribesParams())
|
||||||
|
require.NoError(t, listTribesErr)
|
||||||
|
require.NotEmpty(t, tribes)
|
||||||
|
randTribe := tribes[0]
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params func(t *testing.T) domain.ListTribesParams
|
||||||
|
assertTribes func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes)
|
||||||
|
assertError func(t *testing.T, err error)
|
||||||
|
assertTotal func(t *testing.T, params domain.ListTribesParams, total int)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK: default params",
|
||||||
|
params: func(t *testing.T) domain.ListTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
return domain.NewListTribesParams()
|
||||||
|
},
|
||||||
|
assertTribes: func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(tribes))
|
||||||
|
assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int {
|
||||||
|
if x := cmp.Compare(a.ServerKey(), b.ServerKey()); x != 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return cmp.Compare(a.ID(), b.ID())
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListTribesParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: sort=[serverKey DESC, id DESC]",
|
||||||
|
params: func(t *testing.T) domain.ListTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, params.SetSort([]domain.TribeSort{domain.TribeSortServerKeyDESC, domain.TribeSortIDDESC}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertTribes: func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, len(tribes))
|
||||||
|
assert.True(t, slices.IsSortedFunc(tribes, func(a, b domain.Tribe) int {
|
||||||
|
if x := cmp.Compare(a.ServerKey(), b.ServerKey()) * -1; x != 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return cmp.Compare(a.ID(), b.ID()) * -1
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListTribesParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fmt.Sprintf("OK: ids=[%d] serverKeys=[%s]", randTribe.ID(), randTribe.ServerKey()),
|
||||||
|
params: func(t *testing.T) domain.ListTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, params.SetIDs([]int{randTribe.ID()}))
|
||||||
|
require.NoError(t, params.SetServerKeys([]string{randTribe.ServerKey()}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertTribes: func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
ids := params.IDs()
|
||||||
|
serverKeys := params.ServerKeys()
|
||||||
|
|
||||||
|
for _, tr := range tribes {
|
||||||
|
assert.True(t, slices.Contains(ids, tr.ID()))
|
||||||
|
assert.True(t, slices.Contains(serverKeys, tr.ServerKey()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListTribesParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fmt.Sprintf("OK: idGT=%d", randTribe.ID()),
|
||||||
|
params: func(t *testing.T) domain.ListTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, params.SetIDGT(domain.NullInt{
|
||||||
|
Value: randTribe.ID(),
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertTribes: func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, tribes)
|
||||||
|
for _, tr := range tribes {
|
||||||
|
assert.Greater(t, tr.ID(), params.IDGT().Value, tr.ID())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListTribesParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: deleted=true",
|
||||||
|
params: func(t *testing.T) domain.ListTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, params.SetDeleted(domain.NullBool{
|
||||||
|
Value: true,
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertTribes: func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, tribes)
|
||||||
|
for _, s := range tribes {
|
||||||
|
assert.True(t, s.IsDeleted())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListTribesParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: deleted=false",
|
||||||
|
params: func(t *testing.T) domain.ListTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, params.SetDeleted(domain.NullBool{
|
||||||
|
Value: false,
|
||||||
|
Valid: true,
|
||||||
|
}))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertTribes: func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, tribes)
|
||||||
|
for _, s := range tribes {
|
||||||
|
assert.False(t, s.IsDeleted())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListTribesParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OK: offset=1 limit=2",
|
||||||
|
params: func(t *testing.T) domain.ListTribesParams {
|
||||||
|
t.Helper()
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, params.SetOffset(1))
|
||||||
|
require.NoError(t, params.SetLimit(2))
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
assertTribes: func(t *testing.T, params domain.ListTribesParams, tribes domain.Tribes) {
|
||||||
|
t.Helper()
|
||||||
|
assert.Len(t, tribes, params.Limit())
|
||||||
|
},
|
||||||
|
assertError: func(t *testing.T, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
assertTotal: func(t *testing.T, params domain.ListTribesParams, total int) {
|
||||||
|
t.Helper()
|
||||||
|
assert.NotEmpty(t, total)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := tt.params(t)
|
||||||
|
|
||||||
|
res, err := repos.tribe.List(ctx, params)
|
||||||
|
tt.assertError(t, err)
|
||||||
|
tt.assertTribes(t, params, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
repos := newRepos(t)
|
||||||
|
|
||||||
|
listServersParams := domain.NewListServersParams()
|
||||||
|
require.NoError(t, listServersParams.SetSpecial(domain.NullBool{Value: false, Valid: true}))
|
||||||
|
servers, listServersErr := repos.server.List(ctx, listServersParams)
|
||||||
|
require.NoError(t, listServersErr)
|
||||||
|
require.NotEmpty(t, servers)
|
||||||
|
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
serverKeys := make([]string, 0, len(servers))
|
||||||
|
for _, s := range servers {
|
||||||
|
serverKeys = append(serverKeys, s.Key())
|
||||||
|
}
|
||||||
|
|
||||||
|
listTribesParams := domain.NewListTribesParams()
|
||||||
|
require.NoError(t, listTribesParams.SetDeleted(domain.NullBool{Value: false, Valid: true}))
|
||||||
|
require.NoError(t, listTribesParams.SetServerKeys(serverKeys))
|
||||||
|
|
||||||
|
tribes, err := repos.tribe.List(ctx, listTribesParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var serverKey string
|
||||||
|
var ids []int
|
||||||
|
|
||||||
|
for _, tr := range tribes {
|
||||||
|
if serverKey == "" {
|
||||||
|
serverKey = tr.ServerKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.ServerKey() == serverKey {
|
||||||
|
ids = append(ids, tr.ID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idsToDelete := ids[:int(math.Ceil(float64(len(ids))/2))]
|
||||||
|
|
||||||
|
require.NoError(t, repos.tribe.Delete(ctx, serverKey, idsToDelete...))
|
||||||
|
|
||||||
|
listTribesParams = domain.NewListTribesParams()
|
||||||
|
require.NoError(t, listTribesParams.SetDeleted(domain.NullBool{Valid: false}))
|
||||||
|
require.NoError(t, listTribesParams.SetServerKeys(serverKeys))
|
||||||
|
|
||||||
|
tribes, err = repos.tribe.List(ctx, listTribesParams)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, tr := range tribes {
|
||||||
|
if tr.ServerKey() == serverKey && slices.Contains(ids, tr.ID()) {
|
||||||
|
if slices.Contains(idsToDelete, tr.ID()) {
|
||||||
|
assert.WithinDuration(t, time.Now(), tr.DeletedAt(), time.Minute)
|
||||||
|
} else {
|
||||||
|
assert.Zero(t, tr.DeletedAt())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that no other tribe is removed
|
||||||
|
assert.WithinRange(t, tr.DeletedAt(), time.Time{}, time.Now().Add(-time.Minute))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("OK: len(ids) == 0", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
require.NoError(t, repos.tribe.Delete(ctx, servers[0].Key()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -14,6 +14,8 @@ import (
|
||||||
func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositories) {
|
func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositories) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
t.Run("List & ListCount", func(t *testing.T) {
|
t.Run("List & ListCount", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -80,7 +82,6 @@ func testVersionRepository(t *testing.T, newRepos func(t *testing.T) repositorie
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
params := tt.params(t)
|
params := tt.params(t)
|
||||||
|
|
||||||
res, err := repos.version.List(ctx, params)
|
res, err := repos.version.List(ctx, params)
|
||||||
|
|
424
internal/adapter/testdata/fixture.yml
vendored
424
internal/adapter/testdata/fixture.yml
vendored
|
@ -76,3 +76,427 @@
|
||||||
village_data_updated_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
|
ennoblement_data_updated_at: 2022-03-19T12:01:39.000Z
|
||||||
version_code: pl
|
version_code: pl
|
||||||
|
- model: Tribe
|
||||||
|
rows:
|
||||||
|
- rank_att: 1
|
||||||
|
score_att: 730488339
|
||||||
|
rank_def: 1
|
||||||
|
score_def: 303300128
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 1
|
||||||
|
score_total: 1033788467
|
||||||
|
_id: de188-obott
|
||||||
|
id: 772
|
||||||
|
server_key: de188
|
||||||
|
name: Obdachlose Otter
|
||||||
|
tag: ObOtt
|
||||||
|
num_members: 20
|
||||||
|
num_villages: 8419
|
||||||
|
points: 86151161
|
||||||
|
all_points: 86151161
|
||||||
|
rank: 1
|
||||||
|
dominance: 58.367997781475324
|
||||||
|
created_at: 2021-03-07T11:00:51.000Z
|
||||||
|
profile_url: https://de188.die-staemme.de/game.php?screen=info_ally&id=772
|
||||||
|
- rank_att: 4
|
||||||
|
score_att: 84537872
|
||||||
|
rank_def: 2
|
||||||
|
score_def: 233565453
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 2
|
||||||
|
score_total: 318103325
|
||||||
|
_id: de188-clap
|
||||||
|
id: 950
|
||||||
|
server_key: de188
|
||||||
|
name: Klatschn muss et
|
||||||
|
tag: Clap!
|
||||||
|
num_members: 4
|
||||||
|
num_villages: 333
|
||||||
|
points: 3274015
|
||||||
|
all_points: 3274015
|
||||||
|
rank: 5
|
||||||
|
dominance: 2.3086522462562398
|
||||||
|
created_at: 2021-03-16T01:00:54.000Z
|
||||||
|
profile_url: https://de188.die-staemme.de/game.php?screen=info_ally&id=950
|
||||||
|
- rank_att: 2
|
||||||
|
score_att: 91976316
|
||||||
|
rank_def: 21
|
||||||
|
score_def: 15324882
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 6
|
||||||
|
score_total: 107301198
|
||||||
|
_id: de188-rb
|
||||||
|
id: 1307
|
||||||
|
server_key: de188
|
||||||
|
name: Rasselbande
|
||||||
|
tag: -RB-
|
||||||
|
num_members: 6
|
||||||
|
num_villages: 1509
|
||||||
|
points: 14729501
|
||||||
|
all_points: 14729501
|
||||||
|
rank: 2
|
||||||
|
dominance: 10.461730449251249
|
||||||
|
created_at: 2021-04-17T09:01:01.000Z
|
||||||
|
profile_url: https://de188.die-staemme.de/game.php?screen=info_ally&id=1307
|
||||||
|
- rank_att: 3
|
||||||
|
score_att: 87034216
|
||||||
|
rank_def: 3
|
||||||
|
score_def: 228160403
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 3
|
||||||
|
score_total: 315194619
|
||||||
|
_id: de188-lost
|
||||||
|
id: 97
|
||||||
|
server_key: de188
|
||||||
|
name: Fühl ich nicht.
|
||||||
|
tag: LOST
|
||||||
|
num_members: 2
|
||||||
|
num_villages: 274
|
||||||
|
points: 2541853
|
||||||
|
all_points: 2541853
|
||||||
|
rank: 7
|
||||||
|
dominance: 1.8996117581808099
|
||||||
|
created_at: 2021-02-26T15:00:43.000Z
|
||||||
|
profile_url: https://de188.die-staemme.de/game.php?screen=info_ally&id=97
|
||||||
|
- rank_att: 5
|
||||||
|
score_att: 81487046
|
||||||
|
rank_def: 5
|
||||||
|
score_def: 67353196
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 4
|
||||||
|
score_total: 148840242
|
||||||
|
_id: de188-shnauz
|
||||||
|
id: 64
|
||||||
|
server_key: de188
|
||||||
|
name: aufshnauz
|
||||||
|
tag: shnauz
|
||||||
|
num_members: 2
|
||||||
|
num_villages: 351
|
||||||
|
points: 3431912
|
||||||
|
all_points: 3431912
|
||||||
|
rank: 4
|
||||||
|
dominance: 2.4334442595673877
|
||||||
|
created_at: 2021-02-24T22:00:55.000Z
|
||||||
|
profile_url: https://de188.die-staemme.de/game.php?screen=info_ally&id=64
|
||||||
|
- rank_att: 1
|
||||||
|
score_att: 1449031872
|
||||||
|
rank_def: 1
|
||||||
|
score_def: 993299748
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 1
|
||||||
|
score_total: 2442331620
|
||||||
|
_id: en113-kekw
|
||||||
|
id: 122
|
||||||
|
server_key: en113
|
||||||
|
name: Identity Crisis
|
||||||
|
tag: KEKW
|
||||||
|
num_members: 20
|
||||||
|
num_villages: 22568
|
||||||
|
points: 266237628
|
||||||
|
all_points: 266237628
|
||||||
|
rank: 1
|
||||||
|
dominance: 54.27478896611433
|
||||||
|
created_at: 2020-06-22T13:45:46.000Z
|
||||||
|
profile_url: https://en113.tribalwars.net/game.php?screen=info_ally&id=122
|
||||||
|
- rank_att: 2
|
||||||
|
score_att: 508057735
|
||||||
|
rank_def: 2
|
||||||
|
score_def: 742858106
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 2
|
||||||
|
score_total: 1250915841
|
||||||
|
_id: en113-pear
|
||||||
|
id: 112
|
||||||
|
server_key: en113
|
||||||
|
name: The Pear Bears
|
||||||
|
tag: Pear
|
||||||
|
num_members: 6
|
||||||
|
num_villages: 804
|
||||||
|
points: 7407640
|
||||||
|
all_points: 7407640
|
||||||
|
rank: 7
|
||||||
|
dominance: 1.9335754310863134
|
||||||
|
created_at: 2020-06-22T13:45:46.000Z
|
||||||
|
profile_url: https://en113.tribalwars.net/game.php?screen=info_ally&id=112
|
||||||
|
- rank_att: 3
|
||||||
|
score_att: 321486919
|
||||||
|
rank_def: 2
|
||||||
|
score_def: 531735322
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 3
|
||||||
|
score_total: 853222241
|
||||||
|
_id: en113-virus
|
||||||
|
id: 1337
|
||||||
|
server_key: en113
|
||||||
|
name: RIM VIRUS
|
||||||
|
tag: VIRUS
|
||||||
|
num_members: 4
|
||||||
|
num_villages: 34
|
||||||
|
points: 268858
|
||||||
|
all_points: 268858
|
||||||
|
rank: 30
|
||||||
|
dominance: 0
|
||||||
|
created_at: 2020-06-22T13:45:46.000Z
|
||||||
|
deleted_at: 2020-12-03T18:01:07Z
|
||||||
|
profile_url: https://en113.tribalwars.net/game.php?screen=info_ally&id=1337
|
||||||
|
- rank_att: 3
|
||||||
|
score_att: 231146512
|
||||||
|
rank_def: 5
|
||||||
|
score_def: 107383183
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 3
|
||||||
|
score_total: 338529695
|
||||||
|
_id: en113-rule
|
||||||
|
id: 2544
|
||||||
|
server_key: en113
|
||||||
|
name: One Rule to Rule Them All
|
||||||
|
tag: RULE
|
||||||
|
num_members: 11
|
||||||
|
num_villages: 4529
|
||||||
|
points: 47701113
|
||||||
|
all_points: 47701113
|
||||||
|
rank: 2
|
||||||
|
dominance: 10.891993939539693
|
||||||
|
created_at: 2020-06-22T13:45:46.000Z
|
||||||
|
profile_url: https://en113.tribalwars.net/game.php?screen=info_ally&id=2544
|
||||||
|
- rank_att: 5
|
||||||
|
score_att: 63878040
|
||||||
|
rank_def: 3
|
||||||
|
score_def: 132316341
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 5
|
||||||
|
score_total: 196194381
|
||||||
|
_id: en113-pq
|
||||||
|
id: 1115
|
||||||
|
server_key: en113
|
||||||
|
name: Promising Quest in your area
|
||||||
|
tag: ~PQ~
|
||||||
|
num_members: 1
|
||||||
|
num_villages: 23
|
||||||
|
points: 72446
|
||||||
|
all_points: 72446
|
||||||
|
rank: 40
|
||||||
|
dominance: 0
|
||||||
|
created_at: 2020-06-22T13:45:46.000Z
|
||||||
|
deleted_at: 2020-12-07T04:00:56Z
|
||||||
|
profile_url: https://en113.tribalwars.net/game.php?screen=info_ally&id=1115
|
||||||
|
- rank_att: 1
|
||||||
|
score_att: 132897
|
||||||
|
rank_def: 3
|
||||||
|
score_def: 60776
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 1
|
||||||
|
score_total: 193673
|
||||||
|
_id: it70-tww
|
||||||
|
id: 5
|
||||||
|
server_key: it70
|
||||||
|
name: The White Wolves
|
||||||
|
tag: TWW
|
||||||
|
num_members: 25
|
||||||
|
num_villages: 65
|
||||||
|
points: 182130
|
||||||
|
all_points: 182130
|
||||||
|
rank: 1
|
||||||
|
dominance: 3.026070763500931
|
||||||
|
created_at: 2022-02-21T20:00:07.000Z
|
||||||
|
profile_url: https://it70.tribals.it/game.php?screen=info_ally&id=5
|
||||||
|
- rank_att: 2
|
||||||
|
score_att: 73109
|
||||||
|
rank_def: 6
|
||||||
|
score_def: 29374
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 4
|
||||||
|
score_total: 102483
|
||||||
|
_id: it70-tbw
|
||||||
|
id: 30
|
||||||
|
server_key: it70
|
||||||
|
name: The Black Wolves
|
||||||
|
tag: TBW
|
||||||
|
num_members: 25
|
||||||
|
num_villages: 57
|
||||||
|
points: 155593
|
||||||
|
all_points: 155593
|
||||||
|
rank: 2
|
||||||
|
dominance: 2.653631284916201
|
||||||
|
created_at: 2022-02-24T12:00:05.000Z
|
||||||
|
profile_url: https://it70.tribals.it/game.php?screen=info_ally&id=30
|
||||||
|
- rank_att: 4
|
||||||
|
score_att: 48518
|
||||||
|
rank_def: 16
|
||||||
|
score_def: 7674
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 7
|
||||||
|
score_total: 56192
|
||||||
|
_id: it70-tprm
|
||||||
|
id: 1
|
||||||
|
server_key: it70
|
||||||
|
name: The Pokémon Red Machine
|
||||||
|
tag: TPRM
|
||||||
|
num_members: 23
|
||||||
|
num_villages: 72
|
||||||
|
points: 147717
|
||||||
|
all_points: 147717
|
||||||
|
rank: 3
|
||||||
|
dominance: 3.35195530726257
|
||||||
|
created_at: 2022-02-21T19:00:08.000Z
|
||||||
|
profile_url: https://it70.tribals.it/game.php?screen=info_ally&id=1
|
||||||
|
- rank_att: 5
|
||||||
|
score_att: 47159
|
||||||
|
rank_def: 1
|
||||||
|
score_def: 88401
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 2
|
||||||
|
score_total: 135560
|
||||||
|
_id: it70-tbrm
|
||||||
|
id: 3
|
||||||
|
server_key: it70
|
||||||
|
name: The Big Red Machine
|
||||||
|
tag: TBRM
|
||||||
|
num_members: 23
|
||||||
|
num_villages: 56
|
||||||
|
points: 142377
|
||||||
|
all_points: 142377
|
||||||
|
rank: 4
|
||||||
|
dominance: 2.60707635009311
|
||||||
|
created_at: 2022-02-21T20:00:07.000Z
|
||||||
|
profile_url: https://it70.tribals.it/game.php?screen=info_ally&id=3
|
||||||
|
- rank_att: 6
|
||||||
|
score_att: 46008
|
||||||
|
rank_def: 2
|
||||||
|
score_def: 74025
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 3
|
||||||
|
score_total: 120033
|
||||||
|
_id: it70-tsrm
|
||||||
|
id: 31
|
||||||
|
server_key: it70
|
||||||
|
name: The Small Red Machine
|
||||||
|
tag: TSRM
|
||||||
|
num_members: 25
|
||||||
|
num_villages: 46
|
||||||
|
points: 105512
|
||||||
|
all_points: 105512
|
||||||
|
rank: 5
|
||||||
|
dominance: 2.1415270018621975
|
||||||
|
created_at: 2022-02-24T12:00:05.000Z
|
||||||
|
profile_url: https://it70.tribals.it/game.php?screen=info_ally&id=31
|
||||||
|
- rank_att: 1
|
||||||
|
score_att: 669292167
|
||||||
|
rank_def: 5
|
||||||
|
score_def: 199124128
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 2
|
||||||
|
score_total: 868416295
|
||||||
|
_id: pl169-csa
|
||||||
|
id: 28
|
||||||
|
server_key: pl169
|
||||||
|
name: Konfederacja
|
||||||
|
tag: CSA.
|
||||||
|
num_members: 66
|
||||||
|
num_villages: 14480
|
||||||
|
points: 114277979
|
||||||
|
all_points: 143350058
|
||||||
|
rank: 1
|
||||||
|
dominance: 29.76912481240106
|
||||||
|
created_at: 2021-09-02T23:01:13.000Z
|
||||||
|
profile_url: https://pl169.plemiona.pl/game.php?screen=info_ally&id=28
|
||||||
|
- rank_att: 3
|
||||||
|
score_att: 358150686
|
||||||
|
rank_def: 3
|
||||||
|
score_def: 331975367
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 4
|
||||||
|
score_total: 690126053
|
||||||
|
_id: pl169-csa-jr
|
||||||
|
id: 2
|
||||||
|
server_key: pl169
|
||||||
|
name: KONFEDERACJA JUNIOR
|
||||||
|
tag: CSA.JR
|
||||||
|
num_members: 53
|
||||||
|
num_villages: 10752
|
||||||
|
points: 97684589
|
||||||
|
all_points: 105576587
|
||||||
|
rank: 2
|
||||||
|
dominance: 22.10480870047902
|
||||||
|
created_at: 2021-09-02T18:01:08.000Z
|
||||||
|
profile_url: https://pl169.plemiona.pl/game.php?screen=info_ally&id=2
|
||||||
|
- rank_att: 2
|
||||||
|
score_att: 513902873
|
||||||
|
rank_def: 6
|
||||||
|
score_def: 177660231
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 3
|
||||||
|
score_total: 691563104
|
||||||
|
_id: pl169-kuzyni
|
||||||
|
id: 27
|
||||||
|
server_key: pl169
|
||||||
|
name: Spoceni Kuzyni
|
||||||
|
tag: KUZYNI
|
||||||
|
num_members: 52
|
||||||
|
num_villages: 10152
|
||||||
|
points: 91951132
|
||||||
|
all_points: 97812820
|
||||||
|
rank: 3
|
||||||
|
dominance: 20.87128142924693
|
||||||
|
created_at: 2021-09-02T22:00:37.000Z
|
||||||
|
profile_url: https://pl169.plemiona.pl/game.php?screen=info_ally&id=27
|
||||||
|
- rank_att: 4
|
||||||
|
score_att: 228636414
|
||||||
|
rank_def: 1
|
||||||
|
score_def: 865423891
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 1
|
||||||
|
score_total: 1094060305
|
||||||
|
_id: pl169-lpk
|
||||||
|
id: 1
|
||||||
|
server_key: pl169
|
||||||
|
name: Łapanka
|
||||||
|
tag: ŁPK
|
||||||
|
num_members: 61
|
||||||
|
num_villages: 3471
|
||||||
|
points: 28417793
|
||||||
|
all_points: 30608034
|
||||||
|
rank: 4
|
||||||
|
dominance: 7.13595526407763
|
||||||
|
created_at: 2021-09-02T18:01:08.000Z
|
||||||
|
profile_url: https://pl169.plemiona.pl/game.php?screen=info_ally&id=1
|
||||||
|
- rank_att: 5
|
||||||
|
score_att: 163168564
|
||||||
|
rank_def: 13
|
||||||
|
score_def: 60229489
|
||||||
|
rank_sup: 0
|
||||||
|
score_sup: 0
|
||||||
|
rank_total: 7
|
||||||
|
score_total: 223398053
|
||||||
|
_id: pl169-tt
|
||||||
|
id: 122
|
||||||
|
server_key: pl169
|
||||||
|
name: TIP TOP
|
||||||
|
tag: TT.
|
||||||
|
num_members: 23
|
||||||
|
num_villages: 908
|
||||||
|
points: 8188040
|
||||||
|
all_points: 8188040
|
||||||
|
rank: 6
|
||||||
|
dominance: 1.8667379371312267
|
||||||
|
created_at: 2021-09-08T02:00:58.000Z
|
||||||
|
profile_url: https://pl169.plemiona.pl/game.php?screen=info_ally&id=122
|
||||||
|
|
|
@ -7,12 +7,19 @@ import (
|
||||||
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TribeRepository interface {
|
||||||
|
CreateOrUpdate(ctx context.Context, params ...domain.CreateTribeParams) error
|
||||||
|
List(ctx context.Context, params domain.ListTribesParams) (domain.Tribes, error)
|
||||||
|
Delete(ctx context.Context, serverKey string, ids ...int) error
|
||||||
|
}
|
||||||
|
|
||||||
type TribeService struct {
|
type TribeService struct {
|
||||||
|
repo TribeRepository
|
||||||
twSvc TWService
|
twSvc TWService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTribeService(twSvc TWService) *TribeService {
|
func NewTribeService(repo TribeRepository, twSvc TWService) *TribeService {
|
||||||
return &TribeService{twSvc: twSvc}
|
return &TribeService{repo: repo, twSvc: twSvc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc *TribeService) Sync(ctx context.Context, payload domain.ServerSyncedEventPayload) error {
|
func (svc *TribeService) Sync(ctx context.Context, payload domain.ServerSyncedEventPayload) error {
|
||||||
|
|
|
@ -26,7 +26,7 @@ func NewBaseServer(key string, u *url.URL, open bool) (BaseServer, error) {
|
||||||
return BaseServer{}, ValidationError{
|
return BaseServer{}, ValidationError{
|
||||||
Model: baseServerModelName,
|
Model: baseServerModelName,
|
||||||
Field: "url",
|
Field: "url",
|
||||||
Err: ErrNotNil,
|
Err: ErrNil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,20 +37,20 @@ func NewBaseServer(key string, u *url.URL, open bool) (BaseServer, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseServer) Key() string {
|
func (s BaseServer) Key() string {
|
||||||
return b.key
|
return s.key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseServer) URL() *url.URL {
|
func (s BaseServer) URL() *url.URL {
|
||||||
return b.url
|
return s.url
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseServer) Open() bool {
|
func (s BaseServer) Open() bool {
|
||||||
return b.open
|
return s.open
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseServer) IsZero() bool {
|
func (s BaseServer) IsZero() bool {
|
||||||
return b == BaseServer{}
|
return s == BaseServer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseServers []BaseServer
|
type BaseServers []BaseServer
|
||||||
|
|
|
@ -47,7 +47,7 @@ func TestNewBaseServer(t *testing.T) {
|
||||||
expectedErr: domain.ValidationError{
|
expectedErr: domain.ValidationError{
|
||||||
Model: "BaseServer",
|
Model: "BaseServer",
|
||||||
Field: "url",
|
Field: "url",
|
||||||
Err: domain.ErrNotNil,
|
Err: domain.ErrNil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -108,12 +108,12 @@ func TestBaseServers_FilterOutSpecial(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res := servers.FilterOutSpecial(special)
|
res := servers.FilterOutSpecial(special)
|
||||||
assert.Len(t, res, len(servers)-len(special))
|
|
||||||
for _, s := range servers {
|
for _, s := range servers {
|
||||||
if slices.ContainsFunc(special, func(server domain.Server) bool {
|
if slices.ContainsFunc(special, func(server domain.Server) bool {
|
||||||
return server.Key() == s.Key()
|
return server.Key() == s.Key()
|
||||||
}) {
|
}) {
|
||||||
return
|
assert.NotContains(t, res, s)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Contains(t, res, s)
|
assert.Contains(t, res, s)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import "net/url"
|
import (
|
||||||
|
"math"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
type BaseTribe struct {
|
type BaseTribe struct {
|
||||||
id int
|
id int
|
||||||
|
@ -30,96 +33,70 @@ func NewBaseTribe(
|
||||||
od OpponentsDefeated,
|
od OpponentsDefeated,
|
||||||
profileURL *url.URL,
|
profileURL *url.URL,
|
||||||
) (BaseTribe, error) {
|
) (BaseTribe, error) {
|
||||||
if id < 1 {
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "id",
|
Field: "id",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 1,
|
|
||||||
Current: id,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if l := len(name); l < tribeNameMinLength || l > tribeNameMaxLength {
|
if err := validateStringLen(name, tribeNameMinLength, tribeNameMaxLength); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "name",
|
Field: "name",
|
||||||
Err: LenOutOfRangeError{
|
Err: err,
|
||||||
Min: tribeNameMinLength,
|
|
||||||
Max: tribeNameMaxLength,
|
|
||||||
Current: l,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we convert tag to []rune to get the correct length
|
// we convert tag to []rune to get the correct length
|
||||||
// e.g. for tags such as Орловы, which takes 12 bytes rather than 6
|
// e.g. for tags such as Орловы, which takes 12 bytes rather than 6
|
||||||
// explanation: https://golangbyexample.com/number-characters-string-golang/
|
// explanation: https://golangbyexample.com/number-characters-string-golang/
|
||||||
if l := len([]rune(tag)); l < tribeTagMinLength || l > tribeTagMaxLength {
|
if err := validateSliceLen([]rune(tag), tribeTagMinLength, tribeTagMaxLength); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "tag",
|
Field: "tag",
|
||||||
Err: LenOutOfRangeError{
|
Err: err,
|
||||||
Min: tribeTagMinLength,
|
|
||||||
Max: tribeTagMaxLength,
|
|
||||||
Current: l,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if numMembers < 0 {
|
if err := validateIntInRange(numMembers, 0, math.MaxInt); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "numMembers",
|
Field: "numMembers",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: numMembers,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if numVillages < 0 {
|
if err := validateIntInRange(numVillages, 0, math.MaxInt); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "numVillages",
|
Field: "numVillages",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: numVillages,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if points < 0 {
|
if err := validateIntInRange(points, 0, math.MaxInt); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "points",
|
Field: "points",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: points,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if allPoints < 0 {
|
if err := validateIntInRange(allPoints, 0, math.MaxInt); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "allPoints",
|
Field: "allPoints",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: allPoints,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rank < 0 {
|
if err := validateIntInRange(rank, 0, math.MaxInt); err != nil {
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "rank",
|
Field: "rank",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: rank,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +104,7 @@ func NewBaseTribe(
|
||||||
return BaseTribe{}, ValidationError{
|
return BaseTribe{}, ValidationError{
|
||||||
Model: baseTribeModelName,
|
Model: baseTribeModelName,
|
||||||
Field: "profileURL",
|
Field: "profileURL",
|
||||||
Err: ErrNotNil,
|
Err: ErrNil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,44 +122,48 @@ func NewBaseTribe(
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) ID() int {
|
func (t BaseTribe) ID() int {
|
||||||
return b.id
|
return t.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) Name() string {
|
func (t BaseTribe) Name() string {
|
||||||
return b.name
|
return t.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) Tag() string {
|
func (t BaseTribe) Tag() string {
|
||||||
return b.tag
|
return t.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) NumMembers() int {
|
func (t BaseTribe) NumMembers() int {
|
||||||
return b.numMembers
|
return t.numMembers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) NumVillages() int {
|
func (t BaseTribe) NumVillages() int {
|
||||||
return b.numVillages
|
return t.numVillages
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) Points() int {
|
func (t BaseTribe) Points() int {
|
||||||
return b.points
|
return t.points
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) AllPoints() int {
|
func (t BaseTribe) AllPoints() int {
|
||||||
return b.allPoints
|
return t.allPoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) Rank() int {
|
func (t BaseTribe) Rank() int {
|
||||||
return b.rank
|
return t.rank
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) OD() OpponentsDefeated {
|
func (t BaseTribe) OD() OpponentsDefeated {
|
||||||
return b.od
|
return t.od
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BaseTribe) ProfileURL() *url.URL {
|
func (t BaseTribe) ProfileURL() *url.URL {
|
||||||
return b.profileURL
|
return t.profileURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t BaseTribe) IsZero() bool {
|
||||||
|
return t == BaseTribe{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseTribes []BaseTribe
|
type BaseTribes []BaseTribe
|
||||||
|
|
|
@ -315,7 +315,7 @@ func TestNewBaseTribe(t *testing.T) {
|
||||||
expectedErr: domain.ValidationError{
|
expectedErr: domain.ValidationError{
|
||||||
Model: "BaseTribe",
|
Model: "BaseTribe",
|
||||||
Field: "profileURL",
|
Field: "profileURL",
|
||||||
Err: domain.ErrNotNil,
|
Err: domain.ErrNil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,24 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseTribeConfig struct {
|
type BaseTribeConfig struct {
|
||||||
ID int
|
ID int
|
||||||
Tag string
|
Tag string
|
||||||
OD domain.OpponentsDefeated
|
OD domain.OpponentsDefeated
|
||||||
|
NumVillages int
|
||||||
|
AllPoints int
|
||||||
|
Rank int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBaseTribe(tb TestingTB, opts ...func(cfg *BaseTribeConfig)) domain.BaseTribe {
|
func NewBaseTribe(tb TestingTB, opts ...func(cfg *BaseTribeConfig)) domain.BaseTribe {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
cfg := &BaseTribeConfig{
|
cfg := &BaseTribeConfig{
|
||||||
ID: RandID(),
|
ID: RandID(),
|
||||||
Tag: RandTribeTag(),
|
Tag: RandTribeTag(),
|
||||||
OD: NewOpponentsDefeated(tb),
|
OD: NewOpponentsDefeated(tb),
|
||||||
|
NumVillages: gofakeit.IntRange(1, 10000),
|
||||||
|
AllPoints: gofakeit.IntRange(1, 10000),
|
||||||
|
Rank: gofakeit.IntRange(1, 10000),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
@ -35,10 +41,10 @@ func NewBaseTribe(tb TestingTB, opts ...func(cfg *BaseTribeConfig)) domain.BaseT
|
||||||
gofakeit.LetterN(50),
|
gofakeit.LetterN(50),
|
||||||
cfg.Tag,
|
cfg.Tag,
|
||||||
gofakeit.IntRange(1, 10000),
|
gofakeit.IntRange(1, 10000),
|
||||||
|
cfg.NumVillages,
|
||||||
gofakeit.IntRange(1, 10000),
|
gofakeit.IntRange(1, 10000),
|
||||||
gofakeit.IntRange(1, 10000),
|
cfg.AllPoints,
|
||||||
gofakeit.IntRange(1, 10000),
|
cfg.Rank,
|
||||||
gofakeit.IntRange(1, 10000),
|
|
||||||
cfg.OD,
|
cfg.OD,
|
||||||
u,
|
u,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,73 @@
|
||||||
package domaintest
|
package domaintest
|
||||||
|
|
||||||
import "github.com/brianvoe/gofakeit/v6"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
||||||
|
"github.com/brianvoe/gofakeit/v6"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
func RandTribeTag() string {
|
func RandTribeTag() string {
|
||||||
return gofakeit.LetterN(5)
|
return gofakeit.LetterN(5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TribeConfig struct {
|
||||||
|
ID int
|
||||||
|
ServerKey string
|
||||||
|
Tag string
|
||||||
|
OD domain.OpponentsDefeated
|
||||||
|
BestRank int
|
||||||
|
BestRankAt time.Time
|
||||||
|
MostPoints int
|
||||||
|
MostPointsAt time.Time
|
||||||
|
MostVillages int
|
||||||
|
MostVillagesAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTribe(tb TestingTB, opts ...func(cfg *TribeConfig)) domain.Tribe {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
cfg := &TribeConfig{
|
||||||
|
ID: RandID(),
|
||||||
|
ServerKey: RandServerKey(),
|
||||||
|
Tag: RandTribeTag(),
|
||||||
|
OD: NewOpponentsDefeated(tb),
|
||||||
|
BestRank: gofakeit.IntRange(1, 10000),
|
||||||
|
BestRankAt: time.Now(),
|
||||||
|
MostPoints: gofakeit.IntRange(1, 10000),
|
||||||
|
MostPointsAt: time.Now(),
|
||||||
|
MostVillages: gofakeit.IntRange(1, 10000),
|
||||||
|
MostVillagesAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := domain.UnmarshalTribeFromDatabase(
|
||||||
|
cfg.ID,
|
||||||
|
cfg.ServerKey,
|
||||||
|
gofakeit.LetterN(50),
|
||||||
|
cfg.Tag,
|
||||||
|
gofakeit.IntRange(1, 10000),
|
||||||
|
gofakeit.IntRange(1, 10000),
|
||||||
|
gofakeit.IntRange(1, 10000),
|
||||||
|
gofakeit.IntRange(1, 10000),
|
||||||
|
gofakeit.IntRange(1, 10000),
|
||||||
|
cfg.OD,
|
||||||
|
gofakeit.URL(),
|
||||||
|
gofakeit.Float64Range(0.1, 99.9),
|
||||||
|
cfg.BestRank,
|
||||||
|
cfg.BestRankAt,
|
||||||
|
cfg.MostPoints,
|
||||||
|
cfg.MostPointsAt,
|
||||||
|
cfg.MostVillages,
|
||||||
|
cfg.MostVillagesAt,
|
||||||
|
time.Now(),
|
||||||
|
time.Time{},
|
||||||
|
)
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
type OpponentsDefeated struct {
|
type OpponentsDefeated struct {
|
||||||
rankAtt int
|
rankAtt int
|
||||||
scoreAtt int
|
scoreAtt int
|
||||||
|
@ -23,91 +25,67 @@ func NewOpponentsDefeated(
|
||||||
rankTotal int,
|
rankTotal int,
|
||||||
scoreTotal int,
|
scoreTotal int,
|
||||||
) (OpponentsDefeated, error) {
|
) (OpponentsDefeated, error) {
|
||||||
if rankAtt < 0 {
|
if err := validateIntInRange(rankAtt, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "rankAtt",
|
Field: "rankAtt",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: rankAtt,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scoreAtt < 0 {
|
if err := validateIntInRange(scoreAtt, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "scoreAtt",
|
Field: "scoreAtt",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: scoreAtt,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rankDef < 0 {
|
if err := validateIntInRange(rankDef, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "rankDef",
|
Field: "rankDef",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: rankDef,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scoreDef < 0 {
|
if err := validateIntInRange(scoreDef, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "scoreDef",
|
Field: "scoreDef",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: scoreDef,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rankSup < 0 {
|
if err := validateIntInRange(rankSup, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "rankSup",
|
Field: "rankSup",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: rankSup,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scoreSup < 0 {
|
if err := validateIntInRange(scoreSup, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "scoreSup",
|
Field: "scoreSup",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: scoreSup,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rankTotal < 0 {
|
if err := validateIntInRange(rankTotal, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "rankTotal",
|
Field: "rankTotal",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: rankTotal,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scoreTotal < 0 {
|
if err := validateIntInRange(scoreTotal, 0, math.MaxInt); err != nil {
|
||||||
return OpponentsDefeated{}, ValidationError{
|
return OpponentsDefeated{}, ValidationError{
|
||||||
Model: opponentsDefeatedModelName,
|
Model: opponentsDefeatedModelName,
|
||||||
Field: "scoreTotal",
|
Field: "scoreTotal",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: scoreTotal,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
@ -37,6 +37,8 @@ type Server struct {
|
||||||
ennoblementDataSyncedAt time.Time
|
ennoblementDataSyncedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serverModelName = "Server"
|
||||||
|
|
||||||
// UnmarshalServerFromDatabase unmarshals Server from the database.
|
// UnmarshalServerFromDatabase unmarshals Server from the database.
|
||||||
//
|
//
|
||||||
// It should be used only for unmarshalling from the database!
|
// It should be used only for unmarshalling from the database!
|
||||||
|
@ -65,16 +67,28 @@ func UnmarshalServerFromDatabase(
|
||||||
ennoblementDataSyncedAt time.Time,
|
ennoblementDataSyncedAt time.Time,
|
||||||
) (Server, error) {
|
) (Server, error) {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return Server{}, errors.New("key can't be blank")
|
return Server{}, ValidationError{
|
||||||
|
Model: serverModelName,
|
||||||
|
Field: "key",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if versionCode == "" {
|
if versionCode == "" {
|
||||||
return Server{}, errors.New("version code can't be blank")
|
return Server{}, ValidationError{
|
||||||
|
Model: serverModelName,
|
||||||
|
Field: "versionCode",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := parseURL(rawURL)
|
u, err := parseURL(rawURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Server{}, err
|
return Server{}, ValidationError{
|
||||||
|
Model: serverModelName,
|
||||||
|
Field: "url",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Server{
|
return Server{
|
||||||
|
@ -186,6 +200,14 @@ func (s Server) EnnoblementDataSyncedAt() time.Time {
|
||||||
return s.ennoblementDataSyncedAt
|
return s.ennoblementDataSyncedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Server) Base() BaseServer {
|
||||||
|
return BaseServer{
|
||||||
|
key: s.key,
|
||||||
|
url: s.url,
|
||||||
|
open: s.open,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Servers []Server
|
type Servers []Server
|
||||||
|
|
||||||
// Close finds all servers with Server.Open returning true that are not in the given slice with open servers
|
// Close finds all servers with Server.Open returning true that are not in the given slice with open servers
|
||||||
|
@ -398,25 +420,11 @@ func (params *ListServersParams) Limit() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListServersParams) SetLimit(limit int) error {
|
func (params *ListServersParams) SetLimit(limit int) error {
|
||||||
if limit <= 0 {
|
if err := validateIntInRange(limit, 1, ServerListMaxLimit); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listServersParamsModelName,
|
Model: listServersParamsModelName,
|
||||||
Field: "limit",
|
Field: "limit",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 1,
|
|
||||||
Current: limit,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if limit > ServerListMaxLimit {
|
|
||||||
return ValidationError{
|
|
||||||
Model: listServersParamsModelName,
|
|
||||||
Field: "limit",
|
|
||||||
Err: MaxLessEqualError{
|
|
||||||
Max: ServerListMaxLimit,
|
|
||||||
Current: limit,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,14 +438,11 @@ func (params *ListServersParams) Offset() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (params *ListServersParams) SetOffset(offset int) error {
|
func (params *ListServersParams) SetOffset(offset int) error {
|
||||||
if offset < 0 {
|
if err := validateIntInRange(offset, 0, math.MaxInt); err != nil {
|
||||||
return ValidationError{
|
return ValidationError{
|
||||||
Model: listServersParamsModelName,
|
Model: listServersParamsModelName,
|
||||||
Field: "offset",
|
Field: "offset",
|
||||||
Err: MinGreaterEqualError{
|
Err: err,
|
||||||
Min: 0,
|
|
||||||
Current: offset,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,13 +9,23 @@ type SyncServersCmdPayload struct {
|
||||||
url *url.URL
|
url *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const syncServersCmdPayloadModelName = "SyncServersCmdPayload"
|
||||||
|
|
||||||
func NewSyncServersCmdPayload(versionCode string, u *url.URL) (SyncServersCmdPayload, error) {
|
func NewSyncServersCmdPayload(versionCode string, u *url.URL) (SyncServersCmdPayload, error) {
|
||||||
if versionCode == "" {
|
if versionCode == "" {
|
||||||
return SyncServersCmdPayload{}, errors.New("version code can't be blank")
|
return SyncServersCmdPayload{}, ValidationError{
|
||||||
|
Model: syncServersCmdPayloadModelName,
|
||||||
|
Field: "versionCode",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return SyncServersCmdPayload{}, errors.New("url can't be nil")
|
return SyncServersCmdPayload{}, ValidationError{
|
||||||
|
Model: syncServersCmdPayloadModelName,
|
||||||
|
Field: "url",
|
||||||
|
Err: ErrNil,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SyncServersCmdPayload{versionCode: versionCode, url: u}, nil
|
return SyncServersCmdPayload{versionCode: versionCode, url: u}, nil
|
||||||
|
@ -37,17 +45,31 @@ type ServerSyncedEventPayload struct {
|
||||||
versionCode string
|
versionCode string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serverSyncedEventPayloadModelName = "ServerSyncedEventPayload"
|
||||||
|
|
||||||
func NewServerSyncedEventPayload(key string, u *url.URL, versionCode string) (ServerSyncedEventPayload, error) {
|
func NewServerSyncedEventPayload(key string, u *url.URL, versionCode string) (ServerSyncedEventPayload, error) {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return ServerSyncedEventPayload{}, errors.New("key can't be blank")
|
return ServerSyncedEventPayload{}, ValidationError{
|
||||||
|
Model: serverSyncedEventPayloadModelName,
|
||||||
|
Field: "key",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if versionCode == "" {
|
if versionCode == "" {
|
||||||
return ServerSyncedEventPayload{}, errors.New("version code can't be blank")
|
return ServerSyncedEventPayload{}, ValidationError{
|
||||||
|
Model: serverSyncedEventPayloadModelName,
|
||||||
|
Field: "versionCode",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return ServerSyncedEventPayload{}, errors.New("url can't be nil")
|
return ServerSyncedEventPayload{}, ValidationError{
|
||||||
|
Model: serverSyncedEventPayloadModelName,
|
||||||
|
Field: "url",
|
||||||
|
Err: ErrNil,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ServerSyncedEventPayload{
|
return ServerSyncedEventPayload{
|
||||||
|
@ -58,22 +80,15 @@ func NewServerSyncedEventPayload(key string, u *url.URL, versionCode string) (Se
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerSyncedEventPayloads(servers BaseServers, versionCode string) ([]ServerSyncedEventPayload, error) {
|
func NewServerSyncedEventPayloads(servers BaseServers, versionCode string) ([]ServerSyncedEventPayload, error) {
|
||||||
if versionCode == "" {
|
|
||||||
return nil, errors.New("version code can't be blank")
|
|
||||||
}
|
|
||||||
|
|
||||||
res := make([]ServerSyncedEventPayload, 0, len(servers))
|
res := make([]ServerSyncedEventPayload, 0, len(servers))
|
||||||
|
|
||||||
for i, s := range servers {
|
for _, s := range servers {
|
||||||
if s.IsZero() {
|
payload, err := NewServerSyncedEventPayload(s.Key(), s.URL(), versionCode)
|
||||||
return nil, fmt.Errorf("servers[%d] is an empty struct", i)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, ServerSyncedEventPayload{
|
res = append(res, payload)
|
||||||
key: s.Key(),
|
|
||||||
url: s.URL(),
|
|
||||||
versionCode: versionCode,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
|
@ -37,17 +37,20 @@ func TestServers_Close(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotEmpty(t, res)
|
assert.NotEmpty(t, res)
|
||||||
for _, s := range servers {
|
for _, s := range servers {
|
||||||
if !s.Open() || slices.ContainsFunc(open, func(server domain.BaseServer) bool {
|
expected := domaintest.NewBaseServer(t, func(cfg *domaintest.BaseServerConfig) {
|
||||||
return server.Key() == s.Key()
|
|
||||||
}) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Contains(t, res, domaintest.NewBaseServer(t, func(cfg *domaintest.BaseServerConfig) {
|
|
||||||
cfg.Key = s.Key()
|
cfg.Key = s.Key()
|
||||||
cfg.URL = s.URL()
|
cfg.URL = s.URL()
|
||||||
cfg.Open = false
|
cfg.Open = false
|
||||||
}))
|
})
|
||||||
|
|
||||||
|
if !s.Open() || slices.ContainsFunc(open, func(server domain.BaseServer) bool {
|
||||||
|
return server.Key() == s.Key()
|
||||||
|
}) {
|
||||||
|
assert.NotContains(t, res, expected)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Contains(t, res, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,6 +38,75 @@ type Tribe struct {
|
||||||
deletedAt time.Time
|
deletedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tribeModelName = "Tribe"
|
||||||
|
|
||||||
|
// UnmarshalTribeFromDatabase unmarshals Tribe from the database.
|
||||||
|
//
|
||||||
|
// It should be used only for unmarshalling from the database!
|
||||||
|
// You can't use UnmarshalTribeFromDatabase as constructor - It may put domain into the invalid state!
|
||||||
|
func UnmarshalTribeFromDatabase(
|
||||||
|
id int,
|
||||||
|
serverKey string,
|
||||||
|
name string,
|
||||||
|
tag string,
|
||||||
|
numMembers int,
|
||||||
|
numVillages int,
|
||||||
|
points int,
|
||||||
|
allPoints int,
|
||||||
|
rank int,
|
||||||
|
od OpponentsDefeated,
|
||||||
|
rawProfileURL string,
|
||||||
|
dominance float64,
|
||||||
|
bestRank int,
|
||||||
|
bestRankAt time.Time,
|
||||||
|
mostPoints int,
|
||||||
|
mostPointsAt time.Time,
|
||||||
|
mostVillages int,
|
||||||
|
mostVillagesAt time.Time,
|
||||||
|
createdAt time.Time,
|
||||||
|
deletedAt time.Time,
|
||||||
|
) (Tribe, error) {
|
||||||
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
||||||
|
return Tribe{}, ValidationError{
|
||||||
|
Model: tribeModelName,
|
||||||
|
Field: "id",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profileURL, err := parseURL(rawProfileURL)
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ValidationError{
|
||||||
|
Model: tribeModelName,
|
||||||
|
Field: "profileURL",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tribe{
|
||||||
|
id: id,
|
||||||
|
serverKey: serverKey,
|
||||||
|
name: name,
|
||||||
|
tag: tag,
|
||||||
|
numMembers: numMembers,
|
||||||
|
numVillages: numVillages,
|
||||||
|
points: points,
|
||||||
|
allPoints: allPoints,
|
||||||
|
rank: rank,
|
||||||
|
od: od,
|
||||||
|
profileURL: profileURL,
|
||||||
|
dominance: dominance,
|
||||||
|
bestRank: bestRank,
|
||||||
|
bestRankAt: bestRankAt,
|
||||||
|
mostPoints: mostPoints,
|
||||||
|
mostPointsAt: mostPointsAt,
|
||||||
|
mostVillages: mostVillages,
|
||||||
|
mostVillagesAt: mostVillagesAt,
|
||||||
|
createdAt: createdAt,
|
||||||
|
deletedAt: deletedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t Tribe) ID() int {
|
func (t Tribe) ID() int {
|
||||||
return t.id
|
return t.id
|
||||||
}
|
}
|
||||||
|
@ -114,3 +186,276 @@ func (t Tribe) CreatedAt() time.Time {
|
||||||
func (t Tribe) DeletedAt() time.Time {
|
func (t Tribe) DeletedAt() time.Time {
|
||||||
return t.deletedAt
|
return t.deletedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Tribe) IsDeleted() bool {
|
||||||
|
return !t.deletedAt.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Tribe) Base() BaseTribe {
|
||||||
|
return BaseTribe{
|
||||||
|
id: t.id,
|
||||||
|
name: t.name,
|
||||||
|
tag: t.tag,
|
||||||
|
numMembers: t.numMembers,
|
||||||
|
numVillages: t.numVillages,
|
||||||
|
points: t.points,
|
||||||
|
allPoints: t.allPoints,
|
||||||
|
rank: t.rank,
|
||||||
|
od: t.od,
|
||||||
|
profileURL: t.profileURL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tribes []Tribe
|
||||||
|
|
||||||
|
type CreateTribeParams struct {
|
||||||
|
base BaseTribe
|
||||||
|
serverKey string
|
||||||
|
bestRank int
|
||||||
|
bestRankAt time.Time
|
||||||
|
mostPoints int
|
||||||
|
mostPointsAt time.Time
|
||||||
|
mostVillages int
|
||||||
|
mostVillagesAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTribeParamsModelName = "CreateTribeParams"
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func NewCreateTribeParams(serverKey string, tribes BaseTribes, storedTribes Tribes) ([]CreateTribeParams, error) {
|
||||||
|
if err := validateServerKey(serverKey); err != nil {
|
||||||
|
return nil, ValidationError{
|
||||||
|
Model: createTribeParamsModelName,
|
||||||
|
Field: "serverKey",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := make([]CreateTribeParams, 0, len(tribes))
|
||||||
|
|
||||||
|
for i, t := range tribes {
|
||||||
|
if t.IsZero() {
|
||||||
|
return nil, fmt.Errorf("tribes[%d] is an empty struct", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
var old Tribe
|
||||||
|
if oldIdx := slices.IndexFunc(storedTribes, func(old Tribe) bool {
|
||||||
|
return old.ID() == t.ID() && old.ServerKey() == serverKey
|
||||||
|
}); oldIdx >= 0 {
|
||||||
|
old = storedTribes[oldIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
p := CreateTribeParams{
|
||||||
|
base: t,
|
||||||
|
serverKey: serverKey,
|
||||||
|
bestRank: old.bestRank,
|
||||||
|
bestRankAt: old.bestRankAt,
|
||||||
|
mostPoints: old.mostPoints,
|
||||||
|
mostPointsAt: old.mostPointsAt,
|
||||||
|
mostVillages: old.mostVillages,
|
||||||
|
mostVillagesAt: old.mostVillagesAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.AllPoints() > p.MostPoints() || old.ID() <= 0 {
|
||||||
|
p.mostPoints = t.AllPoints()
|
||||||
|
p.mostPointsAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.NumVillages() > p.MostVillages() || old.ID() <= 0 {
|
||||||
|
p.mostVillages = t.NumVillages()
|
||||||
|
p.mostVillagesAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Rank() < p.BestRank() || old.ID() <= 0 {
|
||||||
|
p.bestRank = t.Rank()
|
||||||
|
p.bestRankAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
params = append(params, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) Base() BaseTribe {
|
||||||
|
return params.base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) ServerKey() string {
|
||||||
|
return params.serverKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) BestRank() int {
|
||||||
|
return params.bestRank
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) BestRankAt() time.Time {
|
||||||
|
return params.bestRankAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) MostPoints() int {
|
||||||
|
return params.mostPoints
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) MostPointsAt() time.Time {
|
||||||
|
return params.mostPointsAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) MostVillages() int {
|
||||||
|
return params.mostVillages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params CreateTribeParams) MostVillagesAt() time.Time {
|
||||||
|
return params.mostVillagesAt
|
||||||
|
}
|
||||||
|
|
||||||
|
type TribeSort uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TribeSortIDASC TribeSort = iota + 1
|
||||||
|
TribeSortIDDESC
|
||||||
|
TribeSortServerKeyASC
|
||||||
|
TribeSortServerKeyDESC
|
||||||
|
)
|
||||||
|
|
||||||
|
const TribeListMaxLimit = 200
|
||||||
|
|
||||||
|
type ListTribesParams struct {
|
||||||
|
ids []int
|
||||||
|
idGT NullInt
|
||||||
|
serverKeys []string
|
||||||
|
deleted NullBool
|
||||||
|
sort []TribeSort
|
||||||
|
limit int
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
const listTribesParamsModelName = "ListTribesParams"
|
||||||
|
|
||||||
|
func NewListTribesParams() ListTribesParams {
|
||||||
|
return ListTribesParams{
|
||||||
|
sort: []TribeSort{
|
||||||
|
TribeSortServerKeyASC,
|
||||||
|
TribeSortIDASC,
|
||||||
|
},
|
||||||
|
limit: TribeListMaxLimit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) IDs() []int {
|
||||||
|
return params.ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) SetIDs(ids []int) error {
|
||||||
|
for i, id := range ids {
|
||||||
|
if err := validateIntInRange(id, 0, math.MaxInt); err != nil {
|
||||||
|
return SliceElementValidationError{
|
||||||
|
Model: listTribesParamsModelName,
|
||||||
|
Field: "ids",
|
||||||
|
Index: i,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.ids = ids
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) IDGT() NullInt {
|
||||||
|
return params.idGT
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) SetIDGT(idGT NullInt) error {
|
||||||
|
if idGT.Valid {
|
||||||
|
if err := validateIntInRange(idGT.Value, 0, math.MaxInt); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listTribesParamsModelName,
|
||||||
|
Field: "idGT",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.idGT = idGT
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) ServerKeys() []string {
|
||||||
|
return params.serverKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) SetServerKeys(serverKeys []string) error {
|
||||||
|
params.serverKeys = serverKeys
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) Deleted() NullBool {
|
||||||
|
return params.deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) SetDeleted(deleted NullBool) error {
|
||||||
|
params.deleted = deleted
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) Sort() []TribeSort {
|
||||||
|
return params.sort
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tribeSortMinLength = 1
|
||||||
|
tribeSortMaxLength = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (params *ListTribesParams) SetSort(sort []TribeSort) error {
|
||||||
|
if err := validateSliceLen(sort, tribeSortMinLength, tribeSortMaxLength); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listTribesParamsModelName,
|
||||||
|
Field: "sort",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.sort = sort
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) Limit() int {
|
||||||
|
return params.limit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) SetLimit(limit int) error {
|
||||||
|
if err := validateIntInRange(limit, 1, TribeListMaxLimit); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listTribesParamsModelName,
|
||||||
|
Field: "limit",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.limit = limit
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) Offset() int {
|
||||||
|
return params.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params *ListTribesParams) SetOffset(offset int) error {
|
||||||
|
if err := validateIntInRange(offset, 0, math.MaxInt); err != nil {
|
||||||
|
return ValidationError{
|
||||||
|
Model: listTribesParamsModelName,
|
||||||
|
Field: "offset",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.offset = offset
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
419
internal/domain/tribe_test.go
Normal file
419
internal/domain/tribe_test.go
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
package domain_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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 TestNewCreateTribeParams(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
server := domaintest.NewServer(t)
|
||||||
|
|
||||||
|
tribes := domain.BaseTribes{
|
||||||
|
domaintest.NewBaseTribe(t),
|
||||||
|
domaintest.NewBaseTribe(t),
|
||||||
|
domaintest.NewBaseTribe(t),
|
||||||
|
}
|
||||||
|
|
||||||
|
storedTribes := domain.Tribes{
|
||||||
|
domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) {
|
||||||
|
cfg.ID = tribes[0].ID()
|
||||||
|
cfg.ServerKey = server.Key()
|
||||||
|
cfg.BestRank = tribes[0].Rank() + 1
|
||||||
|
cfg.BestRankAt = now.Add(-time.Hour)
|
||||||
|
cfg.MostPoints = tribes[0].AllPoints() - 1
|
||||||
|
cfg.MostPointsAt = now.Add(-time.Hour)
|
||||||
|
cfg.MostVillages = tribes[0].NumVillages() - 1
|
||||||
|
cfg.MostVillagesAt = now.Add(-time.Hour)
|
||||||
|
}),
|
||||||
|
domaintest.NewTribe(t, func(cfg *domaintest.TribeConfig) {
|
||||||
|
cfg.ID = tribes[1].ID()
|
||||||
|
cfg.ServerKey = server.Key()
|
||||||
|
cfg.BestRank = tribes[1].Rank() - 1
|
||||||
|
cfg.BestRankAt = now.Add(-time.Hour)
|
||||||
|
cfg.MostPoints = tribes[1].AllPoints() + 1
|
||||||
|
cfg.MostPointsAt = now.Add(-time.Hour)
|
||||||
|
cfg.MostVillages = tribes[1].NumVillages() + 1
|
||||||
|
cfg.MostVillagesAt = now.Add(-time.Hour)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedParams := []struct {
|
||||||
|
base domain.BaseTribe
|
||||||
|
bestRank int
|
||||||
|
bestRankAt time.Time
|
||||||
|
mostPoints int
|
||||||
|
mostPointsAt time.Time
|
||||||
|
mostVillages int
|
||||||
|
mostVillagesAt time.Time
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
base: tribes[0],
|
||||||
|
bestRank: tribes[0].Rank(),
|
||||||
|
bestRankAt: now,
|
||||||
|
mostPoints: tribes[0].AllPoints(),
|
||||||
|
mostPointsAt: now,
|
||||||
|
mostVillages: tribes[0].NumVillages(),
|
||||||
|
mostVillagesAt: now,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
base: tribes[1],
|
||||||
|
bestRank: storedTribes[1].BestRank(),
|
||||||
|
bestRankAt: storedTribes[1].BestRankAt(),
|
||||||
|
mostPoints: storedTribes[1].MostPoints(),
|
||||||
|
mostPointsAt: storedTribes[1].MostPointsAt(),
|
||||||
|
mostVillages: storedTribes[1].MostVillages(),
|
||||||
|
mostVillagesAt: storedTribes[1].MostVillagesAt(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
base: tribes[2],
|
||||||
|
bestRank: tribes[2].Rank(),
|
||||||
|
bestRankAt: now,
|
||||||
|
mostPoints: tribes[2].AllPoints(),
|
||||||
|
mostPointsAt: now,
|
||||||
|
mostVillages: tribes[2].NumVillages(),
|
||||||
|
mostVillagesAt: now,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := domain.NewCreateTribeParams(server.Key(), tribes, storedTribes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, res, len(expectedParams))
|
||||||
|
for i, expected := range expectedParams {
|
||||||
|
idx := slices.IndexFunc(res, func(params domain.CreateTribeParams) bool {
|
||||||
|
return params.Base().ID() == expected.base.ID()
|
||||||
|
})
|
||||||
|
require.GreaterOrEqualf(t, idx, 0, "expectedParams[%d] not found", i)
|
||||||
|
|
||||||
|
params := res[idx]
|
||||||
|
|
||||||
|
assert.Equalf(t, expected.base, params.Base(), "expectedParams[%d]", i)
|
||||||
|
assert.Equalf(t, expected.bestRank, params.BestRank(), "expectedParams[%d]", i)
|
||||||
|
assert.WithinDurationf(t, expected.bestRankAt, params.BestRankAt(), time.Minute, "expectedParams[%d]", i)
|
||||||
|
assert.Equalf(t, expected.mostPoints, params.MostPoints(), "expectedParams[%d]", i)
|
||||||
|
assert.WithinDurationf(t, expected.mostPointsAt, params.MostPointsAt(), time.Minute, "expectedParams[%d]", i)
|
||||||
|
assert.Equalf(t, expected.mostVillages, params.MostVillages(), "expectedParams[%d]", i)
|
||||||
|
assert.WithinDurationf(t, expected.mostVillagesAt, params.MostVillagesAt(), time.Minute, "expectedParams[%d]", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListTribesParams_SetIDs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
ids []int
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
ids: []int{
|
||||||
|
gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: value < 0",
|
||||||
|
args: args{
|
||||||
|
ids: []int{
|
||||||
|
gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
-1,
|
||||||
|
gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.SliceElementValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "ids",
|
||||||
|
Index: 3,
|
||||||
|
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.NewListTribesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetIDs(tt.args.ids), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.ids, params.IDs())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListTribesParams_SetIDGT(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
idGT domain.NullInt
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
idGT: domain.NullInt{
|
||||||
|
Value: gofakeit.IntRange(0, math.MaxInt),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: value < 0",
|
||||||
|
args: args{
|
||||||
|
idGT: domain.NullInt{
|
||||||
|
Value: -1,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "idGT",
|
||||||
|
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.NewListTribesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetIDGT(tt.args.idGT), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.idGT, params.IDGT())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListTribesParams_SetSort(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
sort []domain.TribeSort
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OK",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.TribeSort{
|
||||||
|
domain.TribeSortIDASC,
|
||||||
|
domain.TribeSortServerKeyASC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) < 1",
|
||||||
|
args: args{
|
||||||
|
sort: nil,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "sort",
|
||||||
|
Err: domain.LenOutOfRangeError{
|
||||||
|
Min: 1,
|
||||||
|
Max: 2,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: len(sort) > 2",
|
||||||
|
args: args{
|
||||||
|
sort: []domain.TribeSort{
|
||||||
|
domain.TribeSortIDASC,
|
||||||
|
domain.TribeSortServerKeyASC,
|
||||||
|
domain.TribeSortServerKeyDESC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
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.NewListTribesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetSort(tt.args.sort), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.sort, params.Sort())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListTribesParams_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.TribeListMaxLimit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ERR: limit < 1",
|
||||||
|
args: args{
|
||||||
|
limit: 0,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "limit",
|
||||||
|
Err: domain.MinGreaterEqualError{
|
||||||
|
Min: 1,
|
||||||
|
Current: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fmt.Sprintf("ERR: limit > %d", domain.TribeListMaxLimit),
|
||||||
|
args: args{
|
||||||
|
limit: domain.TribeListMaxLimit + 1,
|
||||||
|
},
|
||||||
|
expectedErr: domain.ValidationError{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
Field: "limit",
|
||||||
|
Err: domain.MaxLessEqualError{
|
||||||
|
Max: domain.TribeListMaxLimit,
|
||||||
|
Current: domain.TribeListMaxLimit + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
params := domain.NewListTribesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetLimit(tt.args.limit), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.limit, params.Limit())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListTribesParams_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{
|
||||||
|
Model: "ListTribesParams",
|
||||||
|
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.NewListTribesParams()
|
||||||
|
|
||||||
|
require.ErrorIs(t, params.SetOffset(tt.args.offset), tt.expectedErr)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.args.offset, params.Offset())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,7 @@ package domain
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"github.com/gosimple/slug"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ValidationError struct {
|
type ValidationError struct {
|
||||||
|
@ -41,22 +40,11 @@ func (e ValidationError) Code() ErrorCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ValidationError) Slug() string {
|
func (e ValidationError) Slug() string {
|
||||||
s := "validation"
|
|
||||||
|
|
||||||
var domainErr Error
|
var domainErr Error
|
||||||
if errors.As(e.Err, &domainErr) {
|
if errors.As(e.Err, &domainErr) {
|
||||||
s = domainErr.Slug()
|
return domainErr.Slug()
|
||||||
}
|
}
|
||||||
|
return "validation-failed"
|
||||||
if e.Field != "" {
|
|
||||||
s = slug.Make(e.Field) + "-" + s
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.Model != "" {
|
|
||||||
s = slug.Make(e.Model) + "-" + s
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ValidationError) Params() map[string]any {
|
func (e ValidationError) Params() map[string]any {
|
||||||
|
@ -71,6 +59,60 @@ func (e ValidationError) Unwrap() error {
|
||||||
return e.Err
|
return e.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SliceElementValidationError struct {
|
||||||
|
Model string
|
||||||
|
Field string
|
||||||
|
Index int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorWithParams = SliceElementValidationError{}
|
||||||
|
|
||||||
|
func (e SliceElementValidationError) Error() string {
|
||||||
|
prefix := e.Model
|
||||||
|
|
||||||
|
if e.Field != "" {
|
||||||
|
if len(prefix) > 0 {
|
||||||
|
prefix += "."
|
||||||
|
}
|
||||||
|
prefix += e.Field + "[" + strconv.Itoa(e.Index) + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefix != "" {
|
||||||
|
prefix += ": "
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SliceElementValidationError) Code() ErrorCode {
|
||||||
|
var domainErr Error
|
||||||
|
if errors.As(e.Err, &domainErr) {
|
||||||
|
return domainErr.Code()
|
||||||
|
}
|
||||||
|
return ErrorCodeIncorrectInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SliceElementValidationError) Slug() string {
|
||||||
|
var domainErr Error
|
||||||
|
if errors.As(e.Err, &domainErr) {
|
||||||
|
return domainErr.Slug()
|
||||||
|
}
|
||||||
|
return "slice-element-validation-failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SliceElementValidationError) Params() map[string]any {
|
||||||
|
var withParams ErrorWithParams
|
||||||
|
if ok := errors.As(e.Err, &withParams); !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return withParams.Params()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e SliceElementValidationError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
type MinGreaterEqualError struct {
|
type MinGreaterEqualError struct {
|
||||||
Min int
|
Min int
|
||||||
Current int
|
Current int
|
||||||
|
@ -151,10 +193,16 @@ func (e LenOutOfRangeError) Params() map[string]any {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNotNil error = simpleError{
|
var ErrRequired error = simpleError{
|
||||||
|
msg: "can't be blank",
|
||||||
|
code: ErrorCodeIncorrectInput,
|
||||||
|
slug: "required",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrNil error = simpleError{
|
||||||
msg: "must not be nil",
|
msg: "must not be nil",
|
||||||
code: ErrorCodeIncorrectInput,
|
code: ErrorCodeIncorrectInput,
|
||||||
slug: "not-nil",
|
slug: "nil",
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSliceLen[S ~[]E, E any](s S, min, max int) error {
|
func validateSliceLen[S ~[]E, E any](s S, min, max int) error {
|
||||||
|
@ -169,6 +217,18 @@ func validateSliceLen[S ~[]E, E any](s S, min, max int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateStringLen(s string, min, max int) error {
|
||||||
|
if l := len(s); l > max || l < min {
|
||||||
|
return LenOutOfRangeError{
|
||||||
|
Min: min,
|
||||||
|
Max: max,
|
||||||
|
Current: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func validateServerKey(key string) error {
|
func validateServerKey(key string) error {
|
||||||
if l := len(key); l < serverKeyMinLength || l > serverKeyMaxLength {
|
if l := len(key); l < serverKeyMinLength || l > serverKeyMaxLength {
|
||||||
return LenOutOfRangeError{
|
return LenOutOfRangeError{
|
||||||
|
@ -192,3 +252,21 @@ func validateVersionCode(code string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateIntInRange(current, min, max int) error {
|
||||||
|
if current < min {
|
||||||
|
return MinGreaterEqualError{
|
||||||
|
Min: min,
|
||||||
|
Current: current,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current > max {
|
||||||
|
return MaxLessEqualError{
|
||||||
|
Max: max,
|
||||||
|
Current: current,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +16,8 @@ type Version struct {
|
||||||
timezone string
|
timezone string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var versionModelName = "Version"
|
||||||
|
|
||||||
// UnmarshalVersionFromDatabase unmarshals Version from the database.
|
// UnmarshalVersionFromDatabase unmarshals Version from the database.
|
||||||
//
|
//
|
||||||
// It should be used only for unmarshalling from the database!
|
// It should be used only for unmarshalling from the database!
|
||||||
|
@ -28,19 +29,35 @@ func UnmarshalVersionFromDatabase(
|
||||||
timezone string,
|
timezone string,
|
||||||
) (Version, error) {
|
) (Version, error) {
|
||||||
if code == "" {
|
if code == "" {
|
||||||
return Version{}, errors.New("code can't be blank")
|
return Version{}, ValidationError{
|
||||||
|
Model: versionModelName,
|
||||||
|
Field: "code",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return Version{}, errors.New("name can't be blank")
|
return Version{}, ValidationError{
|
||||||
|
Model: versionModelName,
|
||||||
|
Field: "name",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if host == "" {
|
if host == "" {
|
||||||
return Version{}, errors.New("host can't be blank")
|
return Version{}, ValidationError{
|
||||||
|
Model: versionModelName,
|
||||||
|
Field: "host",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if timezone == "" {
|
if timezone == "" {
|
||||||
return Version{}, errors.New("timezone can't be blank")
|
return Version{}, ValidationError{
|
||||||
|
Model: versionModelName,
|
||||||
|
Field: "timezone",
|
||||||
|
Err: ErrRequired,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Version{
|
return Version{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user