diff --git a/go.mod b/go.mod index 9ba0f64..4e1cffa 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/Kichiyaki/gopgutil/v10 v10.0.0-20210521204542-cc672e361b3d github.com/go-pg/pg/v10 v10.10.2 github.com/pkg/errors v0.9.1 - github.com/vmihailenco/msgpack/v5 v5.3.1 // indirect - go.opentelemetry.io/otel v0.20.0 // indirect - golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect + github.com/stretchr/testify v1.7.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index c263c6d..7ff38f3 100644 --- a/go.sum +++ b/go.sum @@ -16,10 +16,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-pg/pg/v10 v10.9.1 h1:kU4t84zWGGaU0Qsu49FbNtToUVrlSTkNOngW8aQmwvk= github.com/go-pg/pg/v10 v10.9.1/go.mod h1:rgmTPgHgl5EN2CNKKoMwC7QT62t8BqsdpEkUQuiZMQs= -github.com/go-pg/pg/v10 v10.10.1 h1:82lLX4KGs2wOFOvVVIICoU0Si1fLu6Aitniu73HaDuM= -github.com/go-pg/pg/v10 v10.10.1/go.mod h1:EmoJGYErc+stNN/1Jf+o4csXuprjxcRztBnn6cHe38E= github.com/go-pg/pg/v10 v10.10.2 h1:8G2DdKrB3/0nRIlpur0HySEWBJnHYUByiC0ko4XzE8w= github.com/go-pg/pg/v10 v10.10.2/go.mod h1:EmoJGYErc+stNN/1Jf+o4csXuprjxcRztBnn6cHe38E= github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU= @@ -41,7 +38,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -84,17 +80,9 @@ github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= -go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= -go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= -go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -131,8 +119,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -175,8 +161,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w= diff --git a/tw/twdataloader/endpoints.go b/tw/twdataloader/endpoints.go index d06f771..7e5705b 100644 --- a/tw/twdataloader/endpoints.go +++ b/tw/twdataloader/endpoints.go @@ -26,6 +26,7 @@ const ( EndpointKillAllTribeNotGzipped = "/map/kill_all_tribe.txt" EndpointConquer = "/map/conquer.txt.gz" EndpointConquerNotGzipped = "/map/conquer.txt" - EndpointGetConquer = "/interface.php?func=get_conquer&since=%d" + endpointInterface = "/interface.php" + EndpointGetConquer = endpointInterface + "?func=get_conquer&since=%d" EndpointGetServers = "/backend/get_servers.php" ) diff --git a/tw/twdataloader/helpers.go b/tw/twdataloader/helpers.go index 27f25f8..cbd18b4 100644 --- a/tw/twdataloader/helpers.go +++ b/tw/twdataloader/helpers.go @@ -5,6 +5,7 @@ import ( "encoding/csv" "io" "net/http" + "net/http/httptest" "time" ) @@ -22,3 +23,144 @@ func uncompressAndReadCsvLines(r io.Reader) ([][]string, error) { defer uncompressedStream.Close() return csv.NewReader(uncompressedStream).ReadAll() } + +type handlers struct { + getServers http.HandlerFunc + killAll http.HandlerFunc + killAtt http.HandlerFunc + killDef http.HandlerFunc + killSup http.HandlerFunc + killAllTribe http.HandlerFunc + killAttTribe http.HandlerFunc + killDefTribe http.HandlerFunc + player http.HandlerFunc + tribe http.HandlerFunc + village http.HandlerFunc + conquer http.HandlerFunc + getConquer http.HandlerFunc +} + +func (h *handlers) init() { + noop := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + }) + if h.getServers == nil { + h.getServers = noop + } + if h.killAll == nil { + h.killAll = noop + } + if h.killAtt == nil { + h.killAtt = noop + } + if h.killDef == nil { + h.killDef = noop + } + if h.killSup == nil { + h.killSup = noop + } + if h.killAllTribe == nil { + h.killAllTribe = noop + } + if h.killAttTribe == nil { + h.killAttTribe = noop + } + if h.killDefTribe == nil { + h.killDefTribe = noop + } + if h.player == nil { + h.player = noop + } + if h.tribe == nil { + h.tribe = noop + } + if h.village == nil { + h.village = noop + } + if h.conquer == nil { + h.conquer = noop + } + if h.getConquer == nil { + h.getConquer = noop + } +} + +func prepareTestServer(h *handlers) *httptest.Server { + if h == nil { + h = &handlers{} + } + h.init() + + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + w.WriteHeader(http.StatusNotFound) + return + } + switch r.URL.Path { + case EndpointGetServers: + h.getServers(w, r) + return + case EndpointKillAll: + h.killAll(w, r) + return + case EndpointKillAtt: + h.killAtt(w, r) + return + case EndpointKillDef: + h.killDef(w, r) + return + case EndpointKillSup: + h.killSup(w, r) + return + case EndpointKillAllTribe: + h.killAllTribe(w, r) + return + case EndpointKillAttTribe: + h.killAttTribe(w, r) + return + case EndpointKillDefTribe: + h.killDefTribe(w, r) + return + case EndpointPlayer: + h.player(w, r) + return + case EndpointTribe: + h.tribe(w, r) + return + case EndpointVillage: + h.village(w, r) + return + case EndpointConquer: + h.conquer(w, r) + return + case endpointInterface: + h.getConquer(w, r) + return + default: + w.WriteHeader(http.StatusNotFound) + } + })) +} + +func createWriteStringHandler(resp string) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(resp)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + }) +} + +func createWriteCompressedStringHandler(resp string) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + gzipWriter := gzip.NewWriter(w) + defer gzipWriter.Close() + _, err := gzipWriter.Write([]byte(resp)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + if err := gzipWriter.Flush(); err != nil { + w.WriteHeader(http.StatusInternalServerError) + } + }) +} diff --git a/tw/twdataloader/server_data_loader.go b/tw/twdataloader/server_data_loader.go index a8fdee3..bc20235 100644 --- a/tw/twdataloader/server_data_loader.go +++ b/tw/twdataloader/server_data_loader.go @@ -49,7 +49,7 @@ type parsedODLine struct { Score int } -func (d *ServerDataLoader) parseODLine(line []string) (*parsedODLine, error) { +func (dl *ServerDataLoader) parseODLine(line []string) (*parsedODLine, error) { if len(line) != 3 { return nil, errors.New("invalid line format (should be rank,id,score)") } @@ -70,19 +70,19 @@ func (d *ServerDataLoader) parseODLine(line []string) (*parsedODLine, error) { return p, nil } -func (d *ServerDataLoader) LoadOD(tribe bool) (map[int]*twmodel.OpponentsDefeated, error) { +func (dl *ServerDataLoader) LoadOD(tribe bool) (map[int]*twmodel.OpponentsDefeated, error) { m := make(map[int]*twmodel.OpponentsDefeated) formattedURLs := []string{ - fmt.Sprintf("%s%s", d.baseURL, EndpointKillAll), - fmt.Sprintf("%s%s", d.baseURL, EndpointKillAtt), - fmt.Sprintf("%s%s", d.baseURL, EndpointKillDef), - fmt.Sprintf("%s%s", d.baseURL, EndpointKillSup), + fmt.Sprintf("%s%s", dl.baseURL, EndpointKillAll), + fmt.Sprintf("%s%s", dl.baseURL, EndpointKillAtt), + fmt.Sprintf("%s%s", dl.baseURL, EndpointKillDef), + fmt.Sprintf("%s%s", dl.baseURL, EndpointKillSup), } if tribe { formattedURLs = []string{ - fmt.Sprintf("%s%s", d.baseURL, EndpointKillAllTribe), - fmt.Sprintf("%s%s", d.baseURL, EndpointKillAttTribe), - fmt.Sprintf("%s%s", d.baseURL, EndpointKillDefTribe), + fmt.Sprintf("%s%s", dl.baseURL, EndpointKillAllTribe), + fmt.Sprintf("%s%s", dl.baseURL, EndpointKillAttTribe), + fmt.Sprintf("%s%s", dl.baseURL, EndpointKillDefTribe), "", } } @@ -90,12 +90,12 @@ func (d *ServerDataLoader) LoadOD(tribe bool) (map[int]*twmodel.OpponentsDefeate if formattedURL == "" { continue } - lines, err := d.getCSVData(formattedURL, true) + lines, err := dl.getCSVData(formattedURL, true) if err != nil { return nil, errors.Wrapf(err, "couldn't load data, formattedURL %s", formattedURL) } for _, line := range lines { - parsed, err := d.parseODLine(line) + parsed, err := dl.parseODLine(line) if err != nil { return nil, errors.Wrapf(err, "couldn't parse the line, url %s, line %s", formattedURL, strings.Join(line, ",")) } @@ -121,7 +121,7 @@ func (d *ServerDataLoader) LoadOD(tribe bool) (map[int]*twmodel.OpponentsDefeate return m, nil } -func (d *ServerDataLoader) parsePlayerLine(line []string) (*twmodel.Player, error) { +func (dl *ServerDataLoader) parsePlayerLine(line []string) (*twmodel.Player, error) { if len(line) != 6 { return nil, errors.New("invalid line format (should be id,name,tribeid,villages,points,rank)") } @@ -159,16 +159,16 @@ func (d *ServerDataLoader) parsePlayerLine(line []string) (*twmodel.Player, erro return player, nil } -func (d *ServerDataLoader) LoadPlayers() ([]*twmodel.Player, error) { - formattedURL := d.baseURL + EndpointPlayer - lines, err := d.getCSVData(formattedURL, true) +func (dl *ServerDataLoader) LoadPlayers() ([]*twmodel.Player, error) { + formattedURL := dl.baseURL + EndpointPlayer + lines, err := dl.getCSVData(formattedURL, true) if err != nil { return nil, errors.Wrapf(err, "couldn't load data, url %s", formattedURL) } var players []*twmodel.Player for _, line := range lines { - player, err := d.parsePlayerLine(line) + player, err := dl.parsePlayerLine(line) if err != nil { return nil, errors.Wrapf(err, "couldn't parse the line, url %s, line %s", formattedURL, strings.Join(line, ",")) } @@ -178,7 +178,7 @@ func (d *ServerDataLoader) LoadPlayers() ([]*twmodel.Player, error) { return players, nil } -func (d *ServerDataLoader) parseTribeLine(line []string) (*twmodel.Tribe, error) { +func (dl *ServerDataLoader) parseTribeLine(line []string) (*twmodel.Tribe, error) { if len(line) != 8 { return nil, errors.New("invalid line format (should be id,name,tag,members,villages,points,allpoints,rank)") } @@ -224,15 +224,15 @@ func (d *ServerDataLoader) parseTribeLine(line []string) (*twmodel.Tribe, error) return tribe, nil } -func (d *ServerDataLoader) LoadTribes() ([]*twmodel.Tribe, error) { - formattedURL := d.baseURL + EndpointTribe - lines, err := d.getCSVData(formattedURL, true) +func (dl *ServerDataLoader) LoadTribes() ([]*twmodel.Tribe, error) { + formattedURL := dl.baseURL + EndpointTribe + lines, err := dl.getCSVData(formattedURL, true) if err != nil { return nil, errors.Wrapf(err, "couldn't load data, url %s", formattedURL) } var tribes []*twmodel.Tribe for _, line := range lines { - tribe, err := d.parseTribeLine(line) + tribe, err := dl.parseTribeLine(line) if err != nil { return nil, errors.Wrapf(err, "couldn't parse the line, url %s, line %s", formattedURL, strings.Join(line, ",")) } @@ -241,7 +241,7 @@ func (d *ServerDataLoader) LoadTribes() ([]*twmodel.Tribe, error) { return tribes, nil } -func (d *ServerDataLoader) parseVillageLine(line []string) (*twmodel.Village, error) { +func (dl *ServerDataLoader) parseVillageLine(line []string) (*twmodel.Village, error) { if len(line) != 7 { return nil, errors.New("invalid line format (should be id,name,x,y,playerID,points,bonus)") } @@ -278,15 +278,15 @@ func (d *ServerDataLoader) parseVillageLine(line []string) (*twmodel.Village, er return village, nil } -func (d *ServerDataLoader) LoadVillages() ([]*twmodel.Village, error) { - formattedURL := d.baseURL + EndpointVillage - lines, err := d.getCSVData(formattedURL, true) +func (dl *ServerDataLoader) LoadVillages() ([]*twmodel.Village, error) { + formattedURL := dl.baseURL + EndpointVillage + lines, err := dl.getCSVData(formattedURL, true) if err != nil { return nil, errors.Wrapf(err, "couldn't load data, formattedURL %s", formattedURL) } var villages []*twmodel.Village for _, line := range lines { - village, err := d.parseVillageLine(line) + village, err := dl.parseVillageLine(line) if err != nil { return nil, errors.Wrapf(err, "couldn't parse the line, formattedURL %s, line %s", formattedURL, strings.Join(line, ",")) } @@ -295,7 +295,7 @@ func (d *ServerDataLoader) LoadVillages() ([]*twmodel.Village, error) { return villages, nil } -func (d *ServerDataLoader) parseEnnoblementLine(line []string) (*twmodel.Ennoblement, error) { +func (dl *ServerDataLoader) parseEnnoblementLine(line []string) (*twmodel.Ennoblement, error) { if len(line) != 4 { return nil, errors.New("invalid line format (should be village_id,timestamp,new_owner_id,old_owner_id)") } @@ -326,25 +326,25 @@ type LoadEnnoblementsConfig struct { EnnobledAtGT time.Time } -func (d *ServerDataLoader) LoadEnnoblements(cfg *LoadEnnoblementsConfig) ([]*twmodel.Ennoblement, error) { +func (dl *ServerDataLoader) LoadEnnoblements(cfg *LoadEnnoblementsConfig) ([]*twmodel.Ennoblement, error) { if cfg == nil { cfg = &LoadEnnoblementsConfig{} } yesterdaysDate := time.Now().Add(-23 * time.Hour) - formattedURL := d.baseURL + EndpointConquer + formattedURL := dl.baseURL + EndpointConquer compressed := true if cfg.EnnobledAtGT.After(yesterdaysDate) || cfg.EnnobledAtGT.Equal(yesterdaysDate) { - formattedURL = d.baseURL + fmt.Sprintf(EndpointGetConquer, cfg.EnnobledAtGT.Unix()) + formattedURL = dl.baseURL + fmt.Sprintf(EndpointGetConquer, cfg.EnnobledAtGT.Unix()) compressed = false } - lines, err := d.getCSVData(formattedURL, compressed) + lines, err := dl.getCSVData(formattedURL, compressed) if err != nil { return nil, errors.Wrapf(err, "couldn't load data, formattedURL %s", formattedURL) } var ennoblements []*twmodel.Ennoblement for _, line := range lines { - ennoblement, err := d.parseEnnoblementLine(line) + ennoblement, err := dl.parseEnnoblementLine(line) if err != nil { return nil, errors.Wrapf(err, "couldn't parse the line, formattedURL %s, line %s", formattedURL, strings.Join(line, ",")) } @@ -355,38 +355,38 @@ func (d *ServerDataLoader) LoadEnnoblements(cfg *LoadEnnoblementsConfig) ([]*twm return ennoblements, nil } -func (d *ServerDataLoader) GetConfig() (*twmodel.ServerConfig, error) { - formattedURL := d.baseURL + EndpointConfig +func (dl *ServerDataLoader) GetConfig() (*twmodel.ServerConfig, error) { + formattedURL := dl.baseURL + EndpointConfig cfg := &twmodel.ServerConfig{} - err := d.getXML(formattedURL, cfg) + err := dl.getXML(formattedURL, cfg) if err != nil { return nil, errors.Wrap(err, "getConfig") } return cfg, nil } -func (d *ServerDataLoader) GetBuildingConfig() (*twmodel.BuildingConfig, error) { - formattedURL := d.baseURL + EndpointBuildingConfig +func (dl *ServerDataLoader) GetBuildingConfig() (*twmodel.BuildingConfig, error) { + formattedURL := dl.baseURL + EndpointBuildingConfig cfg := &twmodel.BuildingConfig{} - err := d.getXML(formattedURL, cfg) + err := dl.getXML(formattedURL, cfg) if err != nil { return nil, errors.Wrap(err, "getBuildingConfig") } return cfg, nil } -func (d *ServerDataLoader) GetUnitConfig() (*twmodel.UnitConfig, error) { - formattedURL := d.baseURL + EndpointUnitConfig +func (dl *ServerDataLoader) GetUnitConfig() (*twmodel.UnitConfig, error) { + formattedURL := dl.baseURL + EndpointUnitConfig cfg := &twmodel.UnitConfig{} - err := d.getXML(formattedURL, cfg) + err := dl.getXML(formattedURL, cfg) if err != nil { return nil, errors.Wrap(err, "getUnitConfig") } return cfg, nil } -func (d *ServerDataLoader) getCSVData(url string, compressed bool) ([][]string, error) { - resp, err := d.client.Get(url) +func (dl *ServerDataLoader) getCSVData(url string, compressed bool) ([][]string, error) { + resp, err := dl.client.Get(url) if err != nil { return nil, err } @@ -397,8 +397,8 @@ func (d *ServerDataLoader) getCSVData(url string, compressed bool) ([][]string, return uncompressAndReadCsvLines(resp.Body) } -func (d *ServerDataLoader) getXML(url string, decode interface{}) error { - resp, err := d.client.Get(url) +func (dl *ServerDataLoader) getXML(url string, decode interface{}) error { + resp, err := dl.client.Get(url) if err != nil { return err } diff --git a/tw/twdataloader/server_data_loader_test.go b/tw/twdataloader/server_data_loader_test.go new file mode 100644 index 0000000..f4afe03 --- /dev/null +++ b/tw/twdataloader/server_data_loader_test.go @@ -0,0 +1,626 @@ +package twdataloader + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" + "time" + + "github.com/tribalwarshelp/shared/tw/twmodel" +) + +func TestLoadOD(t *testing.T) { + type scenario struct { + respKillAll string + respKillAtt string + respKillDef string + respKillSup string + respKillAllTribe string + respKillAttTribe string + respKillDefTribe string + tribe bool + expectedResult map[int]*twmodel.OpponentsDefeated + expectedErrMsg string + } + + scenarios := []scenario{ + { + respKillAll: "1,1", + expectedErrMsg: "invalid line format (should be rank,id,score)", + }, + { + respKillAllTribe: "1,1", + expectedErrMsg: "invalid line format (should be rank,id,score)", + tribe: true, + }, + { + respKillAll: "1,1,1", + respKillAtt: "1,1,1", + respKillDef: "1,1,1", + respKillSup: "1,1", + expectedErrMsg: "invalid line format (should be rank,id,score)", + }, + { + respKillAllTribe: "1,1,1", + respKillAttTribe: "1,1,1", + respKillDefTribe: "1,1", + expectedErrMsg: "invalid line format (should be rank,id,score)", + tribe: true, + }, + { + respKillAll: "1,1,asd", + expectedErrMsg: "parsedODLine.Score: strconv.Atoi: parsing \"asd\"", + }, + { + respKillAll: "1,asd,1", + expectedErrMsg: "parsedODLine.ID: strconv.Atoi: parsing \"asd\":", + }, + { + respKillAll: "asd,1,1", + expectedErrMsg: "parsedODLine.Rank: strconv.Atoi: parsing \"asd\":", + }, + { + respKillAllTribe: "1,1,asd", + expectedErrMsg: "parsedODLine.Score: strconv.Atoi: parsing \"asd\"", + tribe: true, + }, + { + respKillAllTribe: "1,asd,1", + expectedErrMsg: "parsedODLine.ID: strconv.Atoi: parsing \"asd\":", + tribe: true, + }, + { + respKillAllTribe: "asd,1,1", + expectedErrMsg: "parsedODLine.Rank: strconv.Atoi: parsing \"asd\":", + tribe: true, + }, + { + respKillAll: "1,1,1\n4,2,4\n2,3,2", + respKillAtt: "2,1,2\n3,2,3\n1,3,1", + respKillDef: "3,1,3\n2,2,2\n4,3,4", + respKillSup: "4,1,4\n1,2,1\n3,3,3", + expectedResult: map[int]*twmodel.OpponentsDefeated{ + 1: { + RankAtt: 2, + ScoreAtt: 2, + RankDef: 3, + ScoreDef: 3, + RankSup: 4, + ScoreSup: 4, + RankTotal: 1, + ScoreTotal: 1, + }, + 2: { + RankAtt: 3, + ScoreAtt: 3, + RankDef: 2, + ScoreDef: 2, + RankSup: 1, + ScoreSup: 1, + ScoreTotal: 4, + RankTotal: 4, + }, + 3: { + RankAtt: 1, + ScoreAtt: 1, + RankDef: 4, + ScoreDef: 4, + RankSup: 3, + ScoreSup: 3, + ScoreTotal: 2, + RankTotal: 2, + }, + }, + }, + { + respKillAllTribe: "1,1,1\n2,2,2\n3,3,3", + respKillAttTribe: "1,1,1\n2,2,2\n3,3,3", + respKillDefTribe: "1,1,1\n2,2,2\n3,3,3", + expectedResult: map[int]*twmodel.OpponentsDefeated{ + 1: { + RankAtt: 1, + ScoreAtt: 1, + RankDef: 1, + ScoreDef: 1, + ScoreTotal: 1, + RankTotal: 1, + }, + 2: { + RankAtt: 2, + ScoreAtt: 2, + RankDef: 2, + ScoreDef: 2, + ScoreTotal: 2, + RankTotal: 2, + }, + 3: { + RankAtt: 3, + ScoreAtt: 3, + RankDef: 3, + ScoreDef: 3, + ScoreTotal: 3, + RankTotal: 3, + }, + }, + tribe: true, + }, + } + + for _, scenario := range scenarios { + ts := prepareTestServer(&handlers{ + killAll: createWriteCompressedStringHandler(scenario.respKillAll), + killAtt: createWriteCompressedStringHandler(scenario.respKillAtt), + killDef: createWriteCompressedStringHandler(scenario.respKillDef), + killSup: createWriteCompressedStringHandler(scenario.respKillSup), + killAllTribe: createWriteCompressedStringHandler(scenario.respKillAllTribe), + killAttTribe: createWriteCompressedStringHandler(scenario.respKillAttTribe), + killDefTribe: createWriteCompressedStringHandler(scenario.respKillDefTribe), + }) + + dl := NewServerDataLoader(&ServerDataLoaderConfig{ + BaseURL: ts.URL, + Client: ts.Client(), + }) + + res, err := dl.LoadOD(scenario.tribe) + if scenario.expectedErrMsg != "" { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), scenario.expectedErrMsg) + } else { + assert.Nil(t, err) + } + + if scenario.expectedResult != nil { + assert.Len(t, res, len(scenario.expectedResult)) + for id, singleResult := range res { + expected, ok := scenario.expectedResult[id] + assert.True(t, ok) + assert.NotNil(t, expected) + assert.EqualValues(t, expected, singleResult) + } + } + + ts.Close() + } +} + +func TestLoadPlayers(t *testing.T) { + type scenario struct { + resp string + expectedResult []*twmodel.Player + expectedErrMsg string + } + + exists := true + scenarios := []scenario{ + { + resp: "1,1,1,1", + expectedErrMsg: "invalid line format (should be id,name,tribeid,villages,points,rank)", + }, + { + resp: "1,name,1,500,500", + expectedErrMsg: "invalid line format (should be id,name,tribeid,villages,points,rank)", + }, + { + resp: "asd,name,1,500,500,500", + expectedErrMsg: "player.ID: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "1,name,asd,500,500,500", + expectedErrMsg: "player.TribeID: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "1,name,123,asd,500,500", + expectedErrMsg: "player.TotalVillages: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "1,name,123,500,asd,500", + expectedErrMsg: "player.Points: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "1,name,123,500,123,asd", + expectedErrMsg: "player.Rank: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "1,name,123,500,500,asd", + expectedErrMsg: "player.Rank: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "1,name,123,124,125,126\n2,name2,256,257,258,259", + expectedResult: []*twmodel.Player{ + { + ID: 1, + Name: "name", + TribeID: 123, + TotalVillages: 124, + Points: 125, + Rank: 126, + Exists: &exists, + }, + { + ID: 2, + Name: "name2", + TribeID: 256, + TotalVillages: 257, + Points: 258, + Rank: 259, + Exists: &exists, + }, + }, + }, + } + + for _, scenario := range scenarios { + ts := prepareTestServer(&handlers{ + player: createWriteCompressedStringHandler(scenario.resp), + }) + + dl := NewServerDataLoader(&ServerDataLoaderConfig{ + BaseURL: ts.URL, + Client: ts.Client(), + }) + + res, err := dl.LoadPlayers() + if scenario.expectedErrMsg != "" { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), scenario.expectedErrMsg) + } else { + assert.Nil(t, err) + } + + if scenario.expectedResult != nil { + assert.Len(t, res, len(scenario.expectedResult)) + for _, singleResult := range res { + found := false + var player *twmodel.Player + for _, expected := range scenario.expectedResult { + if expected.ID == singleResult.ID { + found = true + player = expected + break + } + } + assert.True(t, found) + assert.NotNil(t, player) + assert.EqualValues(t, player, singleResult) + } + } + + ts.Close() + } +} + +func TestLoadTribes(t *testing.T) { + type scenario struct { + resp string + expectedResult []*twmodel.Tribe + expectedErrMsg string + } + + ex := true + scenarios := []scenario{ + { + resp: "1,1,1,1", + expectedErrMsg: "invalid line format (should be id,name,tag,members,villages,points,allpoints,rank)", + }, + { + resp: "1,name,1,500,500", + expectedErrMsg: "invalid line format (should be id,name,tag,members,villages,points,allpoints,rank)", + }, + { + resp: "asd,name,tag,500,500,500,500,500", + expectedErrMsg: "tribe.ID: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,tag,asd,500,500,500,500", + expectedErrMsg: "tribe.TotalMembers: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,tag,500,asd,500,500,500", + expectedErrMsg: "tribe.TotalVillages: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,tag,500,500,asd,500,500", + expectedErrMsg: "tribe.Points: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,tag,500,500,500,asd,500", + expectedErrMsg: "tribe.AllPoints: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,tag,500,500,500,500,asd", + expectedErrMsg: "tribe.Rank: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,tag,500,501,502,503,504\n1234,name2,tag2,5000,5001,5002,5003,5004", + expectedResult: []*twmodel.Tribe{ + { + ID: 123, + Name: "name", + Tag: "tag", + TotalMembers: 500, + TotalVillages: 501, + Points: 502, + AllPoints: 503, + Rank: 504, + Exists: &ex, + }, + { + ID: 1234, + Name: "name2", + Tag: "tag2", + TotalMembers: 5000, + TotalVillages: 5001, + Points: 5002, + AllPoints: 5003, + Rank: 5004, + Exists: &ex, + }, + }, + }, + } + + for _, scenario := range scenarios { + ts := prepareTestServer(&handlers{ + tribe: createWriteCompressedStringHandler(scenario.resp), + }) + + dl := NewServerDataLoader(&ServerDataLoaderConfig{ + BaseURL: ts.URL, + Client: ts.Client(), + }) + + res, err := dl.LoadTribes() + if scenario.expectedErrMsg != "" { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), scenario.expectedErrMsg) + } else { + assert.Nil(t, err) + } + + if scenario.expectedResult != nil { + assert.Len(t, res, len(scenario.expectedResult)) + for _, singleResult := range res { + found := false + var tribe *twmodel.Tribe + for _, expected := range scenario.expectedResult { + if expected.ID == singleResult.ID { + found = true + tribe = expected + break + } + } + assert.True(t, found) + assert.NotNil(t, tribe) + assert.EqualValues(t, tribe, singleResult) + } + } + + ts.Close() + } +} + +func TestLoadVillages(t *testing.T) { + type scenario struct { + resp string + expectedResult []*twmodel.Village + expectedErrMsg string + } + + scenarios := []scenario{ + { + resp: "1,1,1,1", + expectedErrMsg: "invalid line format (should be id,name,x,y,playerID,points,bonus)", + }, + { + resp: "1,name,1,500,500", + expectedErrMsg: "invalid line format (should be id,name,x,y,playerID,points,bonus)", + }, + { + resp: "asd,name,500,500,500,500,0", + expectedErrMsg: "village.ID: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,asd,500,500,500,0", + expectedErrMsg: "village.X: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,500,asd,500,500,0", + expectedErrMsg: "village.Y: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,500,500,asd,500,0", + expectedErrMsg: "village.PlayerID: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,500,500,500,asd,0", + expectedErrMsg: "village.Points: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,name,500,500,500,500,asd", + expectedErrMsg: "village.Bonus: strconv.Atoi: parsing \"asd\"", + }, + { + resp: "123,village 1,500,501,502,503,504\n124,village 2,100,101,102,103,104", + expectedResult: []*twmodel.Village{ + { + ID: 123, + Name: "village 1", + X: 500, + Y: 501, + PlayerID: 502, + Points: 503, + Bonus: 504, + }, + { + ID: 124, + Name: "village 2", + X: 100, + Y: 101, + PlayerID: 102, + Points: 103, + Bonus: 104, + }, + }, + }, + } + + for _, scenario := range scenarios { + ts := prepareTestServer(&handlers{ + village: createWriteCompressedStringHandler(scenario.resp), + }) + + dl := NewServerDataLoader(&ServerDataLoaderConfig{ + BaseURL: ts.URL, + Client: ts.Client(), + }) + + res, err := dl.LoadVillages() + if scenario.expectedErrMsg != "" { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), scenario.expectedErrMsg) + } else { + assert.Nil(t, err) + } + + if scenario.expectedResult != nil { + assert.Len(t, res, len(scenario.expectedResult)) + for _, singleResult := range res { + found := false + var village *twmodel.Village + for _, expected := range scenario.expectedResult { + if expected.ID == singleResult.ID { + found = true + village = expected + break + } + } + assert.True(t, found) + assert.NotNil(t, village) + assert.EqualValues(t, village, singleResult) + } + } + + ts.Close() + } +} + +func TestLoadEnnoblements(t *testing.T) { + type scenario struct { + respConquer string + respGetConquer string + cfg *LoadEnnoblementsConfig + expectedResult []*twmodel.Ennoblement + expectedErrMsg string + } + + nowMinus5h := time.Now().Add(-5 * time.Hour).Unix() + nowMinus6h := time.Now().Add(-6 * time.Hour).Unix() + scenarios := []scenario{ + { + respConquer: "1,1,1", + expectedErrMsg: "invalid line format (should be village_id,timestamp,new_owner_id,old_owner_id)", + }, + { + respConquer: "1,name,1,500,500", + expectedErrMsg: "invalid line format (should be village_id,timestamp,new_owner_id,old_owner_id)", + }, + { + respConquer: "asd,123,123,123", + expectedErrMsg: "ennoblement.VillageID: strconv.Atoi: parsing \"asd\"", + }, + { + respConquer: "123,asd,123,123", + expectedErrMsg: "timestamp: strconv.Atoi: parsing \"asd\"", + }, + { + respConquer: "123,123,asd,123", + expectedErrMsg: "ennoblement.NewOwnerID: strconv.Atoi: parsing \"asd\"", + }, + { + respConquer: "123,123,123,asd", + expectedErrMsg: "ennoblement.OldOwnerID: strconv.Atoi: parsing \"asd\"", + }, + { + respConquer: "123,124,125,126\n127,128,129,130", + expectedResult: []*twmodel.Ennoblement{ + { + VillageID: 123, + EnnobledAt: time.Unix(124, 0), + NewOwnerID: 125, + OldOwnerID: 126, + }, + { + VillageID: 127, + EnnobledAt: time.Unix(128, 0), + NewOwnerID: 129, + OldOwnerID: 130, + }, + }, + }, + { + respGetConquer: "123,124,125,126\n127,128,129,130", + cfg: &LoadEnnoblementsConfig{ + EnnobledAtGT: time.Now().Add(5 * time.Hour), + }, + expectedResult: []*twmodel.Ennoblement{}, + }, + { + respConquer: fmt.Sprintf("123,%d,125,126\n127,%d,129,130", nowMinus5h, nowMinus6h), + expectedResult: []*twmodel.Ennoblement{ + { + VillageID: 123, + EnnobledAt: time.Unix(nowMinus5h, 0), + NewOwnerID: 125, + OldOwnerID: 126, + }, + { + VillageID: 127, + EnnobledAt: time.Unix(nowMinus6h, 0), + NewOwnerID: 129, + OldOwnerID: 130, + }, + }, + }, + } + + for _, scenario := range scenarios { + ts := prepareTestServer(&handlers{ + conquer: createWriteCompressedStringHandler(scenario.respConquer), + getConquer: createWriteStringHandler(scenario.respGetConquer), + }) + + dl := NewServerDataLoader(&ServerDataLoaderConfig{ + BaseURL: ts.URL, + Client: ts.Client(), + }) + + res, err := dl.LoadEnnoblements(scenario.cfg) + if scenario.expectedErrMsg != "" { + assert.NotNil(t, err) + assert.Contains(t, err.Error(), scenario.expectedErrMsg) + } else { + assert.Nil(t, err) + } + + if scenario.expectedResult != nil { + assert.Len(t, res, len(scenario.expectedResult)) + for _, singleResult := range res { + found := false + var ennoblement *twmodel.Ennoblement + for _, expected := range scenario.expectedResult { + if expected.VillageID == singleResult.VillageID { + found = true + ennoblement = expected + break + } + } + assert.True(t, found) + assert.NotNil(t, ennoblement) + assert.EqualValues(t, ennoblement, singleResult) + } + } + + ts.Close() + } +} diff --git a/tw/twdataloader/version_data_loader.go b/tw/twdataloader/version_data_loader.go index 2cbafc4..1e832c7 100644 --- a/tw/twdataloader/version_data_loader.go +++ b/tw/twdataloader/version_data_loader.go @@ -1,7 +1,6 @@ package twdataloader import ( - "fmt" phpserialize "github.com/Kichiyaki/go-php-serialize" "github.com/pkg/errors" "io/ioutil" @@ -40,8 +39,8 @@ func NewVersionDataLoader(cfg *VersionDataLoaderConfig) *VersionDataLoader { } } -func (d *VersionDataLoader) LoadServers() ([]*Server, error) { - resp, err := d.client.Get(fmt.Sprintf("https://%s%s", d.host, EndpointGetServers)) +func (dl *VersionDataLoader) LoadServers() ([]*Server, error) { + resp, err := dl.client.Get(dl.host + EndpointGetServers) if err != nil { return nil, errors.Wrap(err, "couldn't load servers") } @@ -52,14 +51,28 @@ func (d *VersionDataLoader) LoadServers() ([]*Server, error) { return nil, errors.Wrap(err, "couldn't read the response body") } body, err := phpserialize.Decode(string(bodyBytes)) - if err != nil { - return nil, errors.Wrap(err, "couldn't decode the response body into the go value") + if err != nil || body == nil { + fmtedErr := errors.New("couldn't decode the response body into a go value") + if err != nil { + fmtedErr = errors.Wrap(err, fmtedErr.Error()) + } + return nil, fmtedErr + } + bodyMap, ok := body.(map[interface{}]interface{}) + if !ok { + return nil, errors.Errorf("expected map, got %T", body) } var servers []*Server - for serverKey, url := range body.(map[interface{}]interface{}) { - serverKeyStr := serverKey.(string) - urlStr := url.(string) + for serverKey, url := range bodyMap { + serverKeyStr, ok := serverKey.(string) + if !ok { + return nil, errors.Errorf("expected string as the key of the map, got %T", serverKey) + } + urlStr, ok := url.(string) + if !ok { + return nil, errors.Errorf("expected string as the value of the map, got %T", url) + } if serverKeyStr != "" && urlStr != "" { servers = append(servers, &Server{ Key: serverKeyStr, diff --git a/tw/twdataloader/version_data_loader_test.go b/tw/twdataloader/version_data_loader_test.go new file mode 100644 index 0000000..1b3f633 --- /dev/null +++ b/tw/twdataloader/version_data_loader_test.go @@ -0,0 +1,83 @@ +package twdataloader + +import ( + "github.com/stretchr/testify/assert" + "strconv" + "strings" + "testing" +) + +func TestLoadServers(t *testing.T) { + t.Run("invalid response", func(t *testing.T) { + type scenario struct { + resp string + expectedErrMsg string + } + + scenarios := []scenario{ + { + resp: `:"https://pl165.plemiona.pl";s:5:"pl166";s:25:"https://pl166.plemiona.pl";s:5:"pl167";s:25:"https://pl167.plemiona.pl";}`, + expectedErrMsg: "couldn't decode the response body into a go value", + }, + { + resp: `a:19:{s:5:"pl150"s:25"https://pl150.plemiona.pl";}`, + expectedErrMsg: "expected string as the value of the map, got ", + }, + { + resp: "a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}", + expectedErrMsg: "expected string as the key of the map, got int64", + }, + { + resp: `O:8:"stdClass":0:{}`, + expectedErrMsg: "expected map, got *phpserialize.PhpObject", + }, + { + resp: `a:2:{s:3:"asd";i:123;s:4:"asd2";i:123;}`, + expectedErrMsg: "expected string as the value of the map, got int64", + }, + } + + for _, scenario := range scenarios { + ts := prepareTestServer(&handlers{ + getServers: createWriteStringHandler(scenario.resp), + }) + + dl := NewVersionDataLoader(&VersionDataLoaderConfig{ + Host: ts.URL, + Client: ts.Client(), + }) + + _, err := dl.LoadServers() + assert.NotNil(t, err) + assert.Contains(t, err.Error(), scenario.expectedErrMsg) + + ts.Close() + } + }) + + t.Run("success", func(t *testing.T) { + resp := `a:19:{s:5:"pl150";s:25:"https://pl150.plemiona.pl";s:5:"pl151";s:25:"https://pl151.plemiona.pl";s:5:"pl152";s:25:"https://pl152.plemiona.pl";s:5:"pl153";s:25:"https://pl153.plemiona.pl";s:5:"pl154";s:25:"https://pl154.plemiona.pl";s:5:"pl155";s:25:"https://pl155.plemiona.pl";s:5:"pl156";s:25:"https://pl156.plemiona.pl";s:5:"pl157";s:25:"https://pl157.plemiona.pl";s:5:"pl158";s:25:"https://pl158.plemiona.pl";s:5:"pl159";s:25:"https://pl159.plemiona.pl";s:5:"pl160";s:25:"https://pl160.plemiona.pl";s:5:"pl161";s:25:"https://pl161.plemiona.pl";s:5:"pl162";s:25:"https://pl162.plemiona.pl";s:5:"pl163";s:25:"https://pl163.plemiona.pl";s:5:"pl164";s:25:"https://pl164.plemiona.pl";s:4:"plp7";s:24:"https://plp7.plemiona.pl";s:5:"pl165";s:25:"https://pl165.plemiona.pl";s:5:"pl166";s:25:"https://pl166.plemiona.pl";s:5:"pl167";s:25:"https://pl167.plemiona.pl";}` + ts := prepareTestServer(&handlers{ + getServers: createWriteStringHandler(resp), + }) + defer ts.Close() + + expectedLength, err := strconv.Atoi(strings.Split(resp, ":")[1]) + assert.Nil(t, err) + + dl := NewVersionDataLoader(&VersionDataLoaderConfig{ + Host: ts.URL, + Client: ts.Client(), + }) + + servers, err := dl.LoadServers() + assert.Nil(t, err) + cnt := 0 + for _, server := range servers { + if strings.Contains(resp, server.URL) && strings.Contains(resp, server.Key) { + cnt++ + } + } + assert.Equal(t, expectedLength, cnt) + }) +}