1package main23import (4 "net/url"5 "strings"6 "testing"78 "github.com/FelineStateMachine/atlas/internal/arcgismap"9)1011var testWindow = arcgismap.Window{West: -105.2, South: 39.8, East: -104.9, North: 40.0}1213func testDataset() arcgismap.Dataset {14 for _, dataset := range arcgismap.National {15 if dataset.Slug == "streams" {16 return dataset17 }18 }19 panic("streams is not curated")20}2122func TestMapServerQueryURLSpellsThePage(t *testing.T) {23 address := mapServerQueryURL(testDataset(), testWindow, 4000)24 parsed, err := url.Parse(address)25 if err != nil {26 t.Fatal(err)27 }28 if !strings.HasSuffix(parsed.Path, "/6/query") {29 t.Fatalf("path is %q", parsed.Path)30 }31 values := parsed.Query()32 wants := map[string]string{33 "f": "geojson",34 "where": "gnis_name IS NOT NULL",35 "geometry": "-105.2,39.8,-104.9,40",36 "geometryType": "esriGeometryEnvelope",37 "inSR": "4326",38 "outSR": "4326",39 "spatialRel": "esriSpatialRelIntersects",40 "outFields": "OBJECTID,gnis_name",41 "geometryPrecision": "7",42 "orderByFields": "OBJECTID",43 "resultOffset": "4000",44 "resultRecordCount": "2000",45 }46 for key, want := range wants {47 if got := values.Get(key); got != want {48 t.Fatalf("%s is %q, want %q", key, got, want)49 }50 }51 // The where-clause must actually be escaped on the wire.52 if strings.Contains(address, "gnis_name IS") {53 t.Fatalf("where rides unescaped: %s", address)54 }55}5657func TestDecodeMapServerPageReadsFeaturesAndClips(t *testing.T) {58 body := []byte(`{"type":"FeatureCollection","features":[59 {"properties":{"OBJECTID":7,"gnis_name":"Big Dry Creek"},60 "geometry":{"type":"LineString","coordinates":[[-105.1,39.9],[-105.0,39.9]]}},61 {"properties":{"OBJECTID":8,"gnis_name":"Elsewhere Creek"},62 "geometry":{"type":"LineString","coordinates":[[-99.0,39.9],[-98.9,39.9]]}}63 ]}`)64 features, more, err := decodeMapServerPage(testDataset(), testWindow, body, "test")65 if err != nil {66 t.Fatal(err)67 }68 if more {69 t.Fatal("a short page claims another waits")70 }71 if len(features) != 1 || features[0].ID != 7 {72 t.Fatalf("features are %+v; the out-of-window line should be cut away", features)73 }74 if features[0].Fields["gnis_name"] != "Big Dry Creek" {75 t.Fatalf("fields are %v", features[0].Fields)76 }77}7879func TestDecodeMapServerPageReadsTheTruncationFlag(t *testing.T) {80 spellings := []string{81 `{"type":"FeatureCollection","exceededTransferLimit":true,"features":[]}`,82 `{"type":"FeatureCollection","properties":{"exceededTransferLimit":true},"features":[]}`,83 }84 for _, body := range spellings {85 _, more, err := decodeMapServerPage(testDataset(), testWindow, []byte(body), "test")86 if err != nil {87 t.Fatal(err)88 }89 if !more {90 t.Fatalf("truncation went unread in %s", body)91 }92 }93}9495func TestDecodeMapServerPageRefusesAnErrorAnswer(t *testing.T) {96 body := []byte(`{"error":{"code":400,"message":"Invalid parameters"}}`)97 if _, _, err := decodeMapServerPage(testDataset(), testWindow, body, "test"); err == nil {98 t.Fatal("an error answer decoded as a page")99 }100}101102func TestMapServerFeatureIDPrefersTheCuratedDerivation(t *testing.T) {103 var subwatersheds arcgismap.Dataset104 for _, dataset := range arcgismap.National {105 if dataset.Slug == arcgismap.SlugSubwatersheds {106 subwatersheds = dataset107 }108 }109 id, err := mapServerFeatureID(subwatersheds,110 arcgismap.Fields{"huc12": "101900030304"}, map[string]any{"objectid": float64(9)})111 if err != nil {112 t.Fatal(err)113 }114 if id != 101900030304 {115 t.Fatalf("id is %d, want the hydrologic unit code", id)116 }117 if _, err := mapServerFeatureID(subwatersheds, arcgismap.Fields{}, nil); err == nil {118 t.Fatal("a row without its code passed")119 }120}