github.com/PDOK/gokoala@v0.50.6/internal/ogc/tiles/main_test.go (about) 1 package tiles 2 3 import ( 4 "context" 5 "log" 6 "net" 7 "net/http" 8 "net/http/httptest" 9 "net/url" 10 "os" 11 "path" 12 "runtime" 13 "testing" 14 15 "github.com/PDOK/gokoala/config" 16 17 "github.com/PDOK/gokoala/internal/engine" 18 "golang.org/x/text/language" 19 20 "github.com/go-chi/chi/v5" 21 "github.com/stretchr/testify/assert" 22 ) 23 24 func init() { 25 // change working dir to root, to mimic behavior of 'go run' in order to resolve template files. 26 _, filename, _, _ := runtime.Caller(0) 27 dir := path.Join(path.Dir(filename), "../../../") 28 err := os.Chdir(dir) 29 if err != nil { 30 panic(err) 31 } 32 } 33 34 func TestNewTiles(t *testing.T) { 35 type args struct { 36 e *engine.Engine 37 } 38 tests := []struct { 39 name string 40 args args 41 }{ 42 { 43 name: "Test render templates with OGC Tiles config", 44 args: args{ 45 e: engine.NewEngineWithConfig(&config.Config{ 46 Version: "3.3.0", 47 Title: "Test API", 48 Abstract: "Test API description", 49 AvailableLanguages: []config.Language{{Tag: language.Dutch}}, 50 BaseURL: config.URL{URL: &url.URL{Scheme: "https", Host: "api.foobar.example", Path: "/"}}, 51 OgcAPI: config.OgcAPI{ 52 GeoVolumes: nil, 53 Tiles: &config.OgcAPITiles{ 54 TileServer: config.URL{URL: &url.URL{Scheme: "https", Host: "tiles.foobar.example", Path: "/somedataset"}}, 55 Types: []config.TilesType{config.TilesTypeVector}, 56 SupportedSrs: []config.SupportedSrs{ 57 { 58 Srs: "EPSG:28992", 59 ZoomLevelRange: config.ZoomLevelRange{ 60 Start: 0, 61 End: 6, 62 }, 63 }, 64 }, 65 }, 66 Styles: &config.OgcAPIStyles{ 67 Default: "foo", 68 SupportedStyles: nil, 69 }, 70 }, 71 }, "", false, true), 72 }, 73 }, 74 { 75 name: "Test render templates with OGC Tiles config and one SRS", 76 args: args{ 77 e: engine.NewEngineWithConfig(&config.Config{ 78 Version: "3.3.0", 79 Title: "Test API", 80 Abstract: "Test API description", 81 AvailableLanguages: []config.Language{{Tag: language.Dutch}}, 82 BaseURL: config.URL{URL: &url.URL{Scheme: "https", Host: "api.foobar.example", Path: "/"}}, 83 OgcAPI: config.OgcAPI{ 84 GeoVolumes: nil, 85 Tiles: &config.OgcAPITiles{ 86 TileServer: config.URL{URL: &url.URL{Scheme: "https", Host: "tiles.foobar.example", Path: "/somedataset"}}, 87 Types: []config.TilesType{config.TilesTypeVector}, 88 SupportedSrs: []config.SupportedSrs{ 89 { 90 Srs: "EPSG:28992", 91 ZoomLevelRange: config.ZoomLevelRange{ 92 Start: 0, 93 End: 6, 94 }, 95 }, 96 }, 97 }, 98 Styles: &config.OgcAPIStyles{ 99 Default: "foo", 100 SupportedStyles: nil, 101 }, 102 }, 103 }, "", false, true), 104 }, 105 }, 106 } 107 for _, test := range tests { 108 t.Run(test.name, func(t *testing.T) { 109 tiles := NewTiles(test.args.e) 110 assert.NotEmpty(t, tiles.engine.Templates.RenderedTemplates) 111 }) 112 } 113 } 114 115 func TestTiles_Tile(t *testing.T) { 116 type fields struct { 117 configFile string 118 url string 119 tileMatrixSetID string 120 tileMatrix string 121 tileRow string 122 tileCol string 123 } 124 type want struct { 125 body string 126 statusCode int 127 } 128 tests := []struct { 129 name string 130 fields fields 131 want want 132 }{ 133 { 134 name: "NetherlandsRDNewQuad/0/0/0?f=mvt", 135 fields: fields{ 136 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 137 url: "http://localhost:8080/tiles/:tileMatrixSetId/:tileMatrix/:tileRow/:tileCol?f=mvt", 138 tileMatrixSetID: "NetherlandsRDNewQuad", 139 tileMatrix: "0", 140 tileRow: "0", 141 tileCol: "0", 142 }, 143 want: want{ 144 body: "/NetherlandsRDNewQuad/0/0/0.pbf", 145 statusCode: http.StatusOK, 146 }, 147 }, 148 { 149 name: "NetherlandsRDNewQuad/5/10/15?f=mvt", 150 fields: fields{ 151 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 152 url: "http://localhost:8080/tiles/:tileMatrixSetId/:tileMatrix/:tileRow/:tileCol?f=mvt", 153 tileMatrixSetID: "NetherlandsRDNewQuad", 154 tileMatrix: "5", 155 tileRow: "10", 156 tileCol: "15", 157 }, 158 want: want{ 159 body: "/NetherlandsRDNewQuad/5/15/10.pbf", 160 statusCode: http.StatusOK, 161 }, 162 }, 163 { 164 name: "NetherlandsRDNewQuad/5/10/15?f=pbf", 165 fields: fields{ 166 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 167 url: "http://localhost:8080/tiles/:tileMatrixSetId/:tileMatrix/:tileRow/:tileCol?f=pbf", 168 tileMatrixSetID: "NetherlandsRDNewQuad", 169 tileMatrix: "5", 170 tileRow: "10", 171 tileCol: "15", 172 }, 173 want: want{ 174 body: "/NetherlandsRDNewQuad/5/15/10.pbf", 175 statusCode: http.StatusOK, 176 }, 177 }, 178 { 179 name: "NetherlandsRDNewQuad/5/10/15", 180 fields: fields{ 181 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 182 url: "http://localhost:8080/tiles/:tileMatrixSetId/:tileMatrix/:tileRow/:tileCol", 183 tileMatrixSetID: "NetherlandsRDNewQuad", 184 tileMatrix: "5", 185 tileRow: "10", 186 tileCol: "15", 187 }, 188 want: want{ 189 body: "/NetherlandsRDNewQuad/5/15/10.pbf", 190 statusCode: http.StatusOK, 191 }, 192 }, 193 { 194 name: "NetherlandsRDNewQuad/5/10/15.pbf", 195 fields: fields{ 196 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 197 url: "http://localhost:8080/tiles/:tileMatrixSetId/:tileMatrix/:tileRow/:tileCol", 198 tileMatrixSetID: "NetherlandsRDNewQuad", 199 tileMatrix: "5", 200 tileRow: "10", 201 tileCol: "15.pbf", 202 }, 203 want: want{ 204 body: "/NetherlandsRDNewQuad/5/15/10.pbf", 205 statusCode: http.StatusOK, 206 }, 207 }, 208 { 209 name: "NetherlandsRDNewQuad/5/10/15?f=tilejson", 210 fields: fields{ 211 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 212 url: "http://localhost:8080/tiles/:tileMatrixSetId/:tileMatrix/:tileRow/:tileCol?f=tilejson", 213 tileMatrixSetID: "NetherlandsRDNewQuad", 214 tileMatrix: "5", 215 tileRow: "10", 216 tileCol: "15", 217 }, 218 want: want{ 219 body: "Specify tile format. Currently only Mapbox Vector Tiles (?f=mvt) tiles are supported", 220 statusCode: http.StatusBadRequest, 221 }, 222 }, 223 { 224 name: "different uriTemplateTiles", 225 fields: fields{ 226 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles_2.yaml", 227 url: "http://localhost:8080/tiles/:tileMatrixSetId/:tileMatrix/:tileRow/:tileCol?f=mvt", 228 tileMatrixSetID: "NetherlandsRDNewQuad", 229 tileMatrix: "5", 230 tileRow: "10", 231 tileCol: "15", 232 }, 233 want: want{ 234 body: "/foo/NetherlandsRDNewQuad/5/10/15", 235 statusCode: http.StatusOK, 236 }, 237 }, 238 } 239 for _, tt := range tests { 240 t.Run(tt.name, func(t *testing.T) { 241 req, err := createTileRequest(tt.fields.url, tt.fields.tileMatrixSetID, tt.fields.tileMatrix, tt.fields.tileRow, tt.fields.tileCol) 242 if err != nil { 243 log.Fatal(err) 244 } 245 rr, ts := createMockServer() 246 defer ts.Close() 247 248 newEngine, err := engine.NewEngine(tt.fields.configFile, "", false, true) 249 assert.NoError(t, err) 250 tiles := NewTiles(newEngine) 251 handler := tiles.Tile() 252 handler.ServeHTTP(rr, req) 253 254 assert.Equal(t, tt.want.statusCode, rr.Code) 255 assert.Contains(t, rr.Body.String(), tt.want.body) 256 }) 257 } 258 } 259 260 func TestTile_TilesetsList(t *testing.T) { 261 type fields struct { 262 configFile string 263 url string 264 } 265 type want struct { 266 bodyContains string 267 statusCode int 268 } 269 tests := []struct { 270 name string 271 fields fields 272 want want 273 }{ 274 { 275 name: "test NetherlandsRDNewQuad present", 276 fields: fields{ 277 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 278 url: "http://localhost:8080/tiles", 279 }, 280 want: want{ 281 bodyContains: "NetherlandsRDNewQuad", 282 statusCode: http.StatusOK, 283 }, 284 }, 285 { 286 name: "test EuropeanETRS89_LAEAQuad present", 287 fields: fields{ 288 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles_2.yaml", 289 url: "http://localhost:8080/tiles", 290 }, 291 want: want{ 292 bodyContains: "EuropeanETRS89_LAEAQuad", 293 statusCode: http.StatusOK, 294 }, 295 }, 296 } 297 for _, tt := range tests { 298 t.Run(tt.name, func(t *testing.T) { 299 req, err := createTilesetsListRequest(tt.fields.url) 300 if err != nil { 301 log.Fatal(err) 302 } 303 rr, ts := createMockServer() 304 defer ts.Close() 305 306 newEngine, err := engine.NewEngine(tt.fields.configFile, "", false, true) 307 assert.NoError(t, err) 308 tiles := NewTiles(newEngine) 309 handler := tiles.TilesetsList() 310 handler.ServeHTTP(rr, req) 311 312 assert.Equal(t, tt.want.statusCode, rr.Code) 313 assert.Contains(t, rr.Body.String(), tt.want.bodyContains) 314 }) 315 } 316 } 317 318 func TestTile_Tileset(t *testing.T) { 319 type fields struct { 320 configFile string 321 url string 322 tileMatrixSetID string 323 } 324 type want struct { 325 bodyContains string 326 statusCode int 327 } 328 tests := []struct { 329 name string 330 fields fields 331 want want 332 }{ 333 { 334 name: "NetherlandsRDNewQuad", 335 fields: fields{ 336 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 337 url: "http://localhost:8080/tiles/NetherlandsRDNewQuad", 338 tileMatrixSetID: "NetherlandsRDNewQuad", 339 }, 340 want: want{ 341 bodyContains: "NetherlandsRDNewQuad", 342 statusCode: http.StatusOK, 343 }, 344 }, 345 { 346 name: "EuropeanETRS89_LAEAQuad", 347 fields: fields{ 348 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 349 url: "http://localhost:8080/tiles/EuropeanETRS89_LAEAQuad", 350 tileMatrixSetID: "EuropeanETRS89_LAEAQuad", 351 }, 352 want: want{ 353 bodyContains: "EuropeanETRS89_LAEAQuad", 354 statusCode: http.StatusOK, 355 }, 356 }, 357 { 358 name: "WebMercatorQuad", 359 fields: fields{ 360 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 361 url: "http://localhost:8080/tiles/WebMercatorQuad", 362 tileMatrixSetID: "WebMercatorQuad", 363 }, 364 want: want{ 365 bodyContains: "WebMercatorQuad", 366 statusCode: http.StatusOK, 367 }, 368 }, 369 { 370 name: "Invalid", 371 fields: fields{ 372 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 373 url: "http://localhost:8080/tiles/Invalid", 374 tileMatrixSetID: "Invalid", 375 }, 376 want: want{ 377 bodyContains: "request doesn't conform to OpenAPI spec: parameter \\\"tileMatrixSetId\\\" in path has an error: value is not one of the allowed values", 378 statusCode: http.StatusBadRequest, 379 }, 380 }, 381 } 382 for _, tt := range tests { 383 t.Run(tt.name, func(t *testing.T) { 384 req, err := createTilesetRequest(tt.fields.url, tt.fields.tileMatrixSetID) 385 if err != nil { 386 log.Fatal(err) 387 } 388 rr, ts := createMockServer() 389 defer ts.Close() 390 391 newEngine, err := engine.NewEngine(tt.fields.configFile, "", false, true) 392 assert.NoError(t, err) 393 tiles := NewTiles(newEngine) 394 handler := tiles.Tileset() 395 handler.ServeHTTP(rr, req) 396 397 assert.Equal(t, tt.want.statusCode, rr.Code) 398 assert.Contains(t, rr.Body.String(), tt.want.bodyContains) 399 }) 400 } 401 } 402 403 func TestTile_TilematrixSet(t *testing.T) { 404 type fields struct { 405 configFile string 406 url string 407 tileMatrixSetID string 408 } 409 type want struct { 410 bodyContains string 411 statusCode int 412 } 413 tests := []struct { 414 name string 415 fields fields 416 want want 417 }{ 418 { 419 name: "NetherlandsRDNewQuad", 420 fields: fields{ 421 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 422 url: "http://localhost:8080/tileMatrixSets/NetherlandsRDNewQuad", 423 tileMatrixSetID: "NetherlandsRDNewQuad", 424 }, 425 want: want{ 426 bodyContains: "NetherlandsRDNewQuad", 427 statusCode: http.StatusOK, 428 }, 429 }, 430 { 431 name: "EuropeanETRS89_LAEAQuad", 432 fields: fields{ 433 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 434 url: "http://localhost:8080/tileMatrixSets/EuropeanETRS89_LAEAQuad", 435 tileMatrixSetID: "EuropeanETRS89_LAEAQuad", 436 }, 437 want: want{ 438 bodyContains: "EuropeanETRS89_LAEAQuad", 439 statusCode: http.StatusOK, 440 }, 441 }, 442 { 443 name: "WebMercatorQuad", 444 fields: fields{ 445 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 446 url: "http://localhost:8080/tileMatrixSets/WebMercatorQuad", 447 tileMatrixSetID: "WebMercatorQuad", 448 }, 449 want: want{ 450 bodyContains: "WebMercatorQuad", 451 statusCode: http.StatusOK, 452 }, 453 }, 454 { 455 name: "Invalid", 456 fields: fields{ 457 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 458 url: "http://localhost:8080/tileMatrixSets/Invalid", 459 tileMatrixSetID: "Invalid", 460 }, 461 want: want{ 462 bodyContains: "request doesn't conform to OpenAPI spec: parameter \\\"tileMatrixSetId\\\" in path has an error: value is not one of the allowed values", 463 statusCode: http.StatusBadRequest, 464 }, 465 }, 466 } 467 for _, tt := range tests { 468 t.Run(tt.name, func(t *testing.T) { 469 req, err := createTilematrixSetRequest(tt.fields.url, tt.fields.tileMatrixSetID) 470 if err != nil { 471 log.Fatal(err) 472 } 473 rr, ts := createMockServer() 474 defer ts.Close() 475 476 newEngine, err := engine.NewEngine(tt.fields.configFile, "", false, true) 477 assert.NoError(t, err) 478 tiles := NewTiles(newEngine) 479 handler := tiles.TileMatrixSet() 480 handler.ServeHTTP(rr, req) 481 482 assert.Equal(t, tt.want.statusCode, rr.Code) 483 assert.Contains(t, rr.Body.String(), tt.want.bodyContains) 484 }) 485 } 486 } 487 488 func TestTile_TilematrixSets(t *testing.T) { 489 type fields struct { 490 configFile string 491 url string 492 } 493 type want struct { 494 bodyContains string 495 statusCode int 496 } 497 tests := []struct { 498 name string 499 fields fields 500 want want 501 }{ 502 { 503 name: "NetherlandsRDNewQuad", 504 fields: fields{ 505 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 506 url: "http://localhost:8080/tileMatrixSets", 507 }, 508 want: want{ 509 bodyContains: "NetherlandsRDNewQuad", 510 statusCode: http.StatusOK, 511 }, 512 }, 513 { 514 name: "EuropeanETRS89_LAEAQuad", 515 fields: fields{ 516 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles_2.yaml", 517 url: "http://localhost:8080/tileMatrixSets", 518 }, 519 want: want{ 520 bodyContains: "EuropeanETRS89_LAEAQuad", 521 statusCode: http.StatusOK, 522 }, 523 }, 524 { 525 name: "EuropeanETRS89_LAEAQuad", 526 fields: fields{ 527 configFile: "internal/ogc/tiles/testdata/config_minimal_tiles.yaml", 528 url: "http://localhost:8080/tileMatrixSets", 529 }, 530 want: want{ 531 bodyContains: "WebMercatorQuad", 532 statusCode: http.StatusOK, 533 }, 534 }, 535 } 536 for _, tt := range tests { 537 t.Run(tt.name, func(t *testing.T) { 538 req, err := createTilematrixSetsRequest(tt.fields.url) 539 if err != nil { 540 log.Fatal(err) 541 } 542 rr, ts := createMockServer() 543 defer ts.Close() 544 545 newEngine, err := engine.NewEngine(tt.fields.configFile, "", false, true) 546 assert.NoError(t, err) 547 tiles := NewTiles(newEngine) 548 handler := tiles.TileMatrixSets() 549 handler.ServeHTTP(rr, req) 550 551 assert.Equal(t, tt.want.statusCode, rr.Code) 552 assert.Contains(t, rr.Body.String(), tt.want.bodyContains) 553 }) 554 } 555 } 556 557 func createMockServer() (*httptest.ResponseRecorder, *httptest.Server) { 558 rr := httptest.NewRecorder() 559 l, err := net.Listen("tcp", "localhost:9090") 560 if err != nil { 561 log.Fatal(err) 562 } 563 ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 564 engine.SafeWrite(w.Write, []byte(r.URL.String())) 565 })) 566 ts.Listener.Close() 567 ts.Listener = l 568 ts.Start() 569 return rr, ts 570 } 571 572 func createTileRequest(url string, tileMatrixSetID string, tileMatrix string, tileRow string, tileCol string) (*http.Request, error) { 573 req, err := http.NewRequest(http.MethodGet, url, nil) 574 rctx := chi.NewRouteContext() 575 rctx.URLParams.Add("tileMatrixSetId", tileMatrixSetID) 576 rctx.URLParams.Add("tileMatrix", tileMatrix) 577 rctx.URLParams.Add("tileRow", tileRow) 578 rctx.URLParams.Add("tileCol", tileCol) 579 580 req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) 581 return req, err 582 } 583 584 func createTilesetsListRequest(url string) (*http.Request, error) { 585 req, err := http.NewRequest(http.MethodGet, url, nil) 586 rctx := chi.NewRouteContext() 587 588 req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) 589 return req, err 590 } 591 592 func createTilesetRequest(url string, tileMatrixSetID string) (*http.Request, error) { 593 req, err := http.NewRequest(http.MethodGet, url, nil) 594 rctx := chi.NewRouteContext() 595 rctx.URLParams.Add("tileMatrixSetId", tileMatrixSetID) 596 597 req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) 598 return req, err 599 } 600 601 func createTilematrixSetRequest(url string, tileMatrixSetID string) (*http.Request, error) { 602 req, err := http.NewRequest(http.MethodGet, url, nil) 603 rctx := chi.NewRouteContext() 604 rctx.URLParams.Add("tileMatrixSetId", tileMatrixSetID) 605 606 req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) 607 return req, err 608 } 609 610 func createTilematrixSetsRequest(url string) (*http.Request, error) { 611 req, err := http.NewRequest(http.MethodGet, url, nil) 612 rctx := chi.NewRouteContext() 613 614 req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx)) 615 return req, err 616 }