clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

main
atlas / internal / generate / sources / piggyback / piggyback_test.go
piggyback_test.go 5.6K
1package piggyback23import (4 "encoding/json"5 "strings"6 "testing"78 "github.com/FelineStateMachine/atlas/format/semconv"9 "github.com/FelineStateMachine/atlas/internal/generate/doc"10)1112// A capture that is everything a good one is, so a test can spoil exactly one13// thing about it and see the reader refuse for exactly that reason.14func wellFormed() capture {15 return capture{16 Source: source,17 GameSlug: "cyberpunk-2077",18 MapSlug: "night-city",19 GameTitle: "Cyberpunk 2077",20 MapTitle: "Night City",21 Map: sheet{22 TileServer: "https://tiles.example/tiles/cbp/{z}/{x}/{y}.webp",23 MaxZoom: 5,24 Transform: verifiedTransforms["cyberpunk-2077"],25 },26 Levels: []level{{Zoom: 0, MaxX: 0, MaxY: 0}},27 Categories: []category{{28 Key: "shops",29 Types: []kind{{Key: "weapon-shop"}},30 }},31 Pins: []pin{{ID: "a", X: "0", Y: "0", CategoryKey: "shops", TypeKey: "weapon-shop", Name: "One"}},32 }33}3435func TestCheckAcceptsAVerifiedCapture(t *testing.T) {36 raw := wellFormed()37 if err := check(&raw); err != nil {38 t.Fatalf("a well-formed capture was refused: %v", err)39 }40}4142// The gate issue #5 ยง5.2 names: Piggyback refuses unverified transforms. A43// Piggyback map is placed by numbers read off its own page, and a wrong pair44// puts every pin in the volume somewhere plausible and wrong.45func TestCheckRefusesAnUnverifiedTransform(t *testing.T) {46 tests := []struct {47 name string48 spoil func(*capture)49 says string50 }{51 {52 name: "a game nobody has checked",53 spoil: func(c *capture) { c.GameSlug = "some-other-game" },54 says: "no transformation has been verified",55 },56 {57 name: "numbers that are not the verified ones",58 spoil: func(c *capture) { c.Map.Transform.A = 0.03125 },59 says: "not the verified",60 },61 {62 name: "no transformation at all",63 spoil: func(c *capture) { c.Map.Transform = Transform{} },64 says: "not the verified",65 },66 }67 for _, test := range tests {68 t.Run(test.name, func(t *testing.T) {69 raw := wellFormed()70 test.spoil(&raw)71 err := check(&raw)72 if err == nil {73 t.Fatal("the capture was accepted, so every pin in it would be misplaced")74 }75 if !strings.Contains(err.Error(), test.says) {76 t.Errorf("refused with %q, which does not say %q", err, test.says)77 }78 })79 }80}8182func TestCheckRefusesAMalformedCapture(t *testing.T) {83 tests := []struct {84 name string85 spoil func(*capture)86 says string87 }{88 {"another source's bytes", func(c *capture) { c.Source = "ign-wikimaps" }, "not \"piggyback\""},89 {"no map", func(c *capture) { c.MapSlug = "" }, "names no map"},90 {"no observed level", func(c *capture) { c.Levels = nil }, "observed no tile level"},91 }92 for _, test := range tests {93 t.Run(test.name, func(t *testing.T) {94 raw := wellFormed()95 test.spoil(&raw)96 if err := check(&raw); err == nil {97 t.Fatal("the capture was accepted")98 } else if !strings.Contains(err.Error(), test.says) {99 t.Errorf("refused with %q, which does not say %q", err, test.says)100 }101 })102 }103}104105// A pin of a type no category declares would be silently dropped by anything106// that filtered instead of failing, and a dropped pin is invisible in a bundle.107func TestCollectionsRefuseAnUndeclaredType(t *testing.T) {108 raw := wellFormed()109 raw.Pins = append(raw.Pins, pin{110 ID: "b", X: "0", Y: "0", CategoryKey: "shops", TypeKey: "mystery", Name: "Two",111 })112 raw.normalize()113 if _, err := collectionsOf(&raw, doc.NewIDSpace(), "cyberpunk-2077/night-city"); err == nil {114 t.Fatal("a pin of an undeclared type translated anyway, so it would have vanished")115 }116}117118// District names are the map's own region labels, filed by Piggyback under the119// reader's favourites. They are text on its map and they stay text here.120func TestDistrictNamesBecomeTextLabels(t *testing.T) {121 raw := wellFormed()122 raw.Categories = append(raw.Categories, category{Key: "favorites", Types: []kind{}})123 raw.Pins = append(raw.Pins, pin{124 ID: "c", X: "0", Y: "0", CategoryKey: "favorites", TypeKey: "province", Name: "Watson",125 })126 raw.normalize()127 collections, err := collectionsOf(&raw, doc.NewIDSpace(), "cyberpunk-2077/night-city")128 if err != nil {129 t.Fatal(err)130 }131 var found bool132 for _, collection := range collections {133 if collection.Icon != "province" {134 continue135 }136 found = true137 if collection.Group != "Districts" {138 t.Errorf("district labels file under %q", collection.Group)139 }140 if collection.Attrs[semconv.KeyRenderAs] != semconv.RenderAsText {141 t.Errorf("district labels render as %q", collection.Attrs[semconv.KeyRenderAs])142 }143 }144 if !found {145 t.Error("the district labels were dropped")146 }147}148149func TestTileSetPath(t *testing.T) {150 tests := []struct {151 template string152 want string153 refuse bool154 }{155 {"https://tiles.example/tiles/cbp/{z}/{x}/{y}.webp", "cbp", false},156 {"https://tiles.example/cbp/{z}/{x}/{y}.webp", "", true},157 {"https://tiles.example/tiles/{z}/{x}/{y}.webp", "", true},158 }159 for _, test := range tests {160 got, err := TileSetPath(test.template)161 if test.refuse {162 if err == nil {163 t.Errorf("%s named layer %q, want a refusal", test.template, got)164 }165 continue166 }167 if err != nil || got != test.want {168 t.Errorf("%s named %q (%v), want %q", test.template, got, err, test.want)169 }170 }171}172173// A capture round-trips through JSON on its way to the archive, so the shape174// this package declares has to be the shape the archive holds.175func TestCaptureRoundTrips(t *testing.T) {176 raw := wellFormed()177 data, err := json.Marshal(raw)178 if err != nil {179 t.Fatal(err)180 }181 var back capture182 if err := json.Unmarshal(data, &back); err != nil {183 t.Fatal(err)184 }185 if back.Map.Transform != raw.Map.Transform || back.GameSlug != raw.GameSlug {186 t.Errorf("a capture came back as %+v", back)187 }188}