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 / main_test.go
main_test.go 15.4K
1package main23// The included Earth volume, held to its promises: the committed bundle is a4// valid, ordinary format-v3 volume; startup installs it through the format's5// own registry rules; and the installed file serves through the same6// application paths any imported bundle serves through. Every test here reads7// the embedded asset -- the same bytes a release build ships -- so what is8// judged is what a first launch actually opens.910import (11 "bytes"12 "encoding/json"13 "image/jpeg"14 "io"15 "io/fs"16 "net/http"17 "net/http/httptest"18 "os"19 "path/filepath"20 "strings"21 "testing"2223 "github.com/FelineStateMachine/atlas/format/bundle"24 "github.com/FelineStateMachine/atlas/format/semconv"25 "github.com/FelineStateMachine/atlas/internal/app"26 "github.com/FelineStateMachine/atlas/internal/app/hostenv/oshost"27)2829// includedEarth reads the one embedded bundle. There being exactly one is part30// of the contract: the embed pattern would happily carry a stray second build,31// and two included Earths would be two answers to what a first launch shows.32func includedEarth(t *testing.T) []byte {33 t.Helper()34 names, err := fs.Glob(included, "included/*.atlas")35 if err != nil {36 t.Fatal(err)37 }38 if len(names) != 1 {39 t.Fatalf("the executable embeds %d bundles %v, want exactly the one Earth", len(names), names)40 }41 data, err := included.ReadFile(names[0])42 if err != nil {43 t.Fatal(err)44 }45 return data46}4748func openIncluded(t *testing.T) *bundle.Reader {49 t.Helper()50 data := includedEarth(t)51 reader, err := bundle.NewReader(bytes.NewReader(data), int64(len(data)))52 if err != nil {53 t.Fatal(err)54 }55 return reader56}5758// earthPayload is as much of the world payload as these tests read.59type earthPayload struct {60 Lenses []struct {61 Name string `json:"name"`62 Tiles string `json:"tiles"`63 MinZoom int `json:"minZoom"`64 MaxZoom int `json:"maxZoom"`65 FullZoom int `json:"fullZoom"`66 SourceZoom int `json:"sourceZoom"`67 Formats []string `json:"formats"`68 Bounds *struct {69 X, Y, Width, Height int70 } `json:"bounds"`71 } `json:"lenses"`72 Collections []struct {73 Title string `json:"title"`74 Group string `json:"group"`75 Kind string `json:"kind"`76 IconAsset string `json:"iconAsset"`77 Attrs map[string]string `json:"attrs"`78 Features []json.RawMessage `json:"features"`79 } `json:"collections"`80 Attrs map[string]string `json:"attrs"`81 Merged []struct {82 Source string `json:"source"`83 Slug string `json:"slug"`84 Origin bool `json:"origin"`85 } `json:"merged"`86}8788func readEarthPayload(t *testing.T, reader *bundle.Reader) earthPayload {89 t.Helper()90 data, err := reader.ReadEntry(bundle.WorldEntryName("earth", bundle.WorldSuffix))91 if err != nil {92 t.Fatal(err)93 }94 var payload earthPayload95 if err := json.Unmarshal(data, &payload); err != nil {96 t.Fatal(err)97 }98 return payload99}100101// TestIncludedEarthIsAValidOrdinaryBundle: the committed built-in passes the102// format's whole validation surface and says exactly what the included volume103// is specified to say -- format 3, conventions 2, volume earth, one world104// earth, raster-only.105func TestIncludedEarthIsAValidOrdinaryBundle(t *testing.T) {106 reader := openIncluded(t)107 if err := reader.Validate(); err != nil {108 t.Fatalf("the committed built-in does not validate: %v", err)109 }110 m := reader.Manifest111 if m.FormatVersion != 3 || m.Conventions != 2 {112 t.Errorf("format %d conventions %d, want 3 and 2", m.FormatVersion, m.Conventions)113 }114 if m.Volume.Slug != "earth" || m.Volume.Title != "Earth" {115 t.Errorf("volume %s/%s", m.Volume.Slug, m.Volume.Title)116 }117 if len(m.Worlds) != 1 || m.Worlds[0].Slug != "earth" || m.Worlds[0].Title != "Earth" {118 t.Fatalf("worlds %+v, want exactly the one earth", m.Worlds)119 }120 if m.Worlds[0].Points != 202 || m.Worlds[0].Paths != 0 || m.Worlds[0].Areas != 177 {121 t.Errorf("the manifest counts %+v; the demo carries 202 capitals and 177 countries", m.Worlds[0])122 }123 if m.Version.CreatedAt != "2026-08-03T16:21:07Z" {124 t.Errorf("createdAt %s is not the pinned capture time", m.Version.CreatedAt)125 }126}127128// TestIncludedEarthDeclaresTheWholeSphere: one Blue Marble lens over the129// complete 8192×4096 Earth surface, the demo's capitals and countries, and130// exactly the spherical equirectangular declarations the analysis contracts131// recognise.132func TestIncludedEarthDeclaresTheWholeSphere(t *testing.T) {133 reader := openIncluded(t)134 payload := readEarthPayload(t, reader)135136 if len(payload.Collections) != 7 {137 t.Fatalf("%d collections, want six continents of capitals and the countries", len(payload.Collections))138 }139 for _, held := range payload.Collections[:6] {140 if held.Group != "Capitals" || held.Kind != "point" || held.IconAsset == "" {141 t.Errorf("capitals collection %q arrives as %s/%s with icon %q",142 held.Title, held.Group, held.Kind, held.IconAsset)143 }144 }145 countries := payload.Collections[6]146 if countries.Title != "Countries" || countries.Kind != "area" ||147 countries.Attrs[semconv.KeyLabelPolicy] != semconv.LabelQuiet || len(countries.Features) != 177 {148 t.Errorf("the ground arrives as %q %s (%d features, %v)",149 countries.Title, countries.Kind, len(countries.Features), countries.Attrs)150 }151 if len(payload.Lenses) != 1 {152 t.Fatalf("%d lenses, want the one Blue Marble", len(payload.Lenses))153 }154 lens := payload.Lenses[0]155 if lens.Name != "Blue Marble" {156 t.Errorf("lens %q", lens.Name)157 }158 if lens.MinZoom != 0 || lens.MaxZoom != 6 || lens.FullZoom != 6 || lens.SourceZoom != 6 {159 t.Errorf("zooms min %d max %d full %d source %d, want 0/6/6/6",160 lens.MinZoom, lens.MaxZoom, lens.FullZoom, lens.SourceZoom)161 }162 if len(lens.Formats) != 7 {163 t.Errorf("%d formats for 7 levels", len(lens.Formats))164 }165 for _, format := range lens.Formats {166 if format != "jpg" {167 t.Errorf("a level is %q, want jpg throughout", format)168 }169 }170 if lens.Bounds == nil || *lens.Bounds != struct{ X, Y, Width, Height int }{0, 0, 8192, 4096} {171 t.Errorf("bounds %+v, want the complete 0,0,8192,4096 surface", lens.Bounds)172 }173174 want := map[string]string{175 semconv.KeyIconOutset: "dark",176 semconv.KeyGeometrySurface: "sphere",177 semconv.KeyGeometryProjection: "equirect",178 semconv.KeyGeometryEquirectPx: "0,0,8192,4096",179 semconv.KeyGeometryEquirectDeg: "-180,90,180,-90",180 semconv.KeyGeometryBody: "earth",181 semconv.KeyGeometryRadiusKM: "6371.0088",182 }183 if len(payload.Attrs) != len(want) {184 t.Errorf("the world carries %d attributes %v, want %d", len(payload.Attrs), payload.Attrs, len(want))185 }186 for key, value := range want {187 if payload.Attrs[key] != value {188 t.Errorf("world says %s=%q, want %q", key, payload.Attrs[key], value)189 }190 }191192 if len(payload.Merged) != 1 || !payload.Merged[0].Origin ||193 payload.Merged[0].Source != "NASA Earth Observatory" ||194 payload.Merged[0].Slug != "nasa-blue-marble" {195 t.Errorf("provenance ledger %+v; the credit is part of the artifact", payload.Merged)196 }197}198199// TestIncludedEarthPyramidOpensAtEveryLevel: every advertised level holds its200// whole half-height window of stored, decodable JPEG tiles.201func TestIncludedEarthPyramidOpensAtEveryLevel(t *testing.T) {202 reader := openIncluded(t)203 perLevel := map[string]int{}204 for _, name := range reader.Names() {205 if !strings.HasPrefix(name, "tiles/earth/") {206 continue207 }208 parts := strings.Split(name, "/")209 perLevel[parts[2]]++210 if !reader.Stored(name) {211 t.Fatalf("%s is compressed; format v3 stores tiles", name)212 }213 }214 wantPerLevel := map[string]int{"0": 1, "1": 2, "2": 8, "3": 32, "4": 128, "5": 512, "6": 2048}215 for level, want := range wantPerLevel {216 if perLevel[level] != want {217 t.Errorf("level %s holds %d tiles, want %d", level, perLevel[level], want)218 }219 }220 // One tile of every level decodes as the JPEG its name claims.221 for _, name := range []string{222 "tiles/earth/0/0/0.jpg", "tiles/earth/1/1/0.jpg", "tiles/earth/2/3/1.jpg",223 "tiles/earth/3/7/3.jpg", "tiles/earth/4/15/7.jpg", "tiles/earth/5/31/15.jpg",224 "tiles/earth/6/63/31.jpg",225 } {226 data, err := reader.ReadEntry(name)227 if err != nil {228 t.Fatalf("%s: %v", name, err)229 }230 if _, err := jpeg.Decode(bytes.NewReader(data)); err != nil {231 t.Fatalf("%s does not decode: %v", name, err)232 }233 }234 if !reader.Stored(bundle.WorldEntryName("earth", bundle.PackedSuffix)) {235 t.Error("the packed payload is compressed; format v3 stores it")236 }237}238239// TestIncludedEarthCarriesNoRuntimeURL is offline purity, asserted directly on240// the shipped bytes over and above Validate's own scan.241func TestIncludedEarthCarriesNoRuntimeURL(t *testing.T) {242 reader := openIncluded(t)243 for _, name := range []string{244 bundle.WorldEntryName("earth", bundle.WorldSuffix),245 bundle.WorldEntryName("earth", bundle.TextSuffix),246 } {247 data, err := reader.ReadEntry(name)248 if err != nil {249 t.Fatal(err)250 }251 for _, scheme := range []string{"http://", "https://"} {252 if bytes.Contains(data, []byte(scheme)) {253 t.Errorf("%s carries %q", name, scheme)254 }255 }256 }257}258259// TestInstallIncludedIsIdempotentAndOverwritesNothing: a first launch installs260// one versioned Earth file; a second launch changes nothing; and a distinct261// Earth build already in the library stays exactly where it is, side by side.262func TestInstallIncludedIsIdempotentAndOverwritesNothing(t *testing.T) {263 library := t.TempDir()264 if err := installIncluded(library); err != nil {265 t.Fatal(err)266 }267 entries, err := os.ReadDir(library)268 if err != nil {269 t.Fatal(err)270 }271 if len(entries) != 1 {272 t.Fatalf("a first launch installed %d files", len(entries))273 }274 installed := filepath.Join(library, entries[0].Name())275 if !strings.HasPrefix(entries[0].Name(), "earth-20260803-") || !strings.HasSuffix(entries[0].Name(), ".atlas") {276 t.Errorf("installed as %s, want the versioned earth name", entries[0].Name())277 }278 if _, err := bundle.Describe(installed); err != nil {279 t.Fatalf("the installed file does not describe: %v", err)280 }281 before, err := os.Stat(installed)282 if err != nil {283 t.Fatal(err)284 }285286 if err := installIncluded(library); err != nil {287 t.Fatal(err)288 }289 entries, err = os.ReadDir(library)290 if err != nil {291 t.Fatal(err)292 }293 if len(entries) != 1 {294 t.Fatalf("a second launch grew the library to %d files", len(entries))295 }296 after, err := os.Stat(installed)297 if err != nil {298 t.Fatal(err)299 }300 if !after.ModTime().Equal(before.ModTime()) || after.Size() != before.Size() {301 t.Error("a second launch touched the installed file")302 }303304 // A distinct Earth build -- another capture day, another stamp -- is not305 // overwritten and not deleted; the registry fold decides which one serves.306 other := writeDistinctEarth(t, library)307 if err := installIncluded(library); err != nil {308 t.Fatal(err)309 }310 if _, err := os.Stat(other); err != nil {311 t.Errorf("a launch removed the other Earth build: %v", err)312 }313 entries, err = os.ReadDir(library)314 if err != nil {315 t.Fatal(err)316 }317 if len(entries) != 2 {318 t.Errorf("the library holds %d files; two Earth builds sit side by side", len(entries))319 }320}321322// writeDistinctEarth states a second, minimal Earth build through the format's323// real writer, dated after the included one so the fold prefers it.324func writeDistinctEarth(t *testing.T, dir string) string {325 t.Helper()326 manifest := bundle.Manifest{327 Format: bundle.Format,328 FormatVersion: bundle.FormatVersion,329 Volume: bundle.Volume{Slug: "earth", Title: "Earth"},330 Version: bundle.Version{Stamp: "ffff", CreatedAt: "2026-09-01T00:00:00Z"},331 TileGrid: bundle.TileGrid{SourceZoom: 13, FirstTile: 4064, TileSize: 256, Size: 8192},332 Worlds: []bundle.WorldEntry{{333 Slug: "flat", Title: "Flat", UpdatedAt: "2026-09-01T00:00:00Z",334 }},335 }336 path := filepath.Join(dir, bundle.VersionedFileName(manifest))337 file, err := os.Create(path)338 if err != nil {339 t.Fatal(err)340 }341 writer, err := bundle.NewWriter(file, manifest)342 if err != nil {343 t.Fatal(err)344 }345 if err := writer.AddDeflated("worlds/flat.json",346 []byte(`{"lenses":[{"name":"Flat","tiles":"flat","minZoom":0,"maxZoom":0,"fullZoom":0,"sourceZoom":0,"formats":["jpg"],"interpolate":true}],"collections":[]}`)); err != nil {347 t.Fatal(err)348 }349 if err := writer.AddStored("worlds/flat.bin", bytes.NewReader(bundle.PackLocations(nil))); err != nil {350 t.Fatal(err)351 }352 if err := writer.AddDeflated("worlds/flat.text", []byte(`{}`)); err != nil {353 t.Fatal(err)354 }355 if err := writer.AddStored("tiles/flat/0/0/0.jpg", bytes.NewReader(statedJPEG(t))); err != nil {356 t.Fatal(err)357 }358 if err := writer.Close(); err != nil {359 t.Fatal(err)360 }361 if err := file.Close(); err != nil {362 t.Fatal(err)363 }364 return path365}366367func statedJPEG(t *testing.T) []byte {368 t.Helper()369 reader := openIncluded(t)370 data, err := reader.ReadEntry("tiles/earth/0/0/0.jpg")371 if err != nil {372 t.Fatal(err)373 }374 return data375}376377// TestDesktopStartupExposesEarthOnTheFirstScan walks the shell's own startup378// order over a temporary empty data directory: library created, built-in379// installed, host constructed -- and the host's first scan already serves380// Earth, with no network anywhere in the path.381func TestDesktopStartupExposesEarthOnTheFirstScan(t *testing.T) {382 data := t.TempDir()383 library := filepath.Join(data, "bundles")384 if err := os.MkdirAll(library, 0o755); err != nil {385 t.Fatal(err)386 }387 if err := installIncluded(library); err != nil {388 t.Fatal(err)389 }390 host, err := oshost.New(oshost.Options{391 BundlesDir: library,392 SessionsDir: filepath.Join(data, "sessions"),393 })394 if err != nil {395 t.Fatal(err)396 }397 t.Cleanup(func() { host.Volumes().(interface{ Close() error }).Close() })398399 volumes := host.Volumes().Volumes()400 if len(volumes) != 1 {401 t.Fatalf("the first scan lists %d volumes", len(volumes))402 }403 manifest := volumes[0].Manifest()404 if manifest.Volume.Slug != "earth" || manifest.Volume.Title != "Earth" {405 t.Errorf("the first scan serves %s/%s", manifest.Volume.Slug, manifest.Volume.Title)406 }407}408409// TestEarthServesThroughTheApplication: the installed file -- never the410// embedded copy -- answers the application's own data plane: the catalog lists411// Earth, the world page opens, and a raster tile reads back as the JPEG it is.412func TestEarthServesThroughTheApplication(t *testing.T) {413 data := t.TempDir()414 library := filepath.Join(data, "bundles")415 if err := os.MkdirAll(library, 0o755); err != nil {416 t.Fatal(err)417 }418 if err := installIncluded(library); err != nil {419 t.Fatal(err)420 }421 host, err := oshost.New(oshost.Options{422 BundlesDir: library,423 SessionsDir: filepath.Join(data, "sessions"),424 })425 if err != nil {426 t.Fatal(err)427 }428 t.Cleanup(func() { host.Volumes().(interface{ Close() error }).Close() })429 handler := app.New(host, app.Options{})430 server := httptest.NewServer(handler)431 defer server.Close()432433 catalog := get(t, server.Client(), server.URL+"/data/catalog.json")434 if !strings.Contains(string(catalog), `"earth"`) {435 t.Errorf("the catalog does not list earth: %s", catalog)436 }437438 page := get(t, server.Client(), server.URL+"/v/earth/earth")439 if !strings.Contains(string(page), "Earth") {440 t.Error("the world page does not open on Earth")441 }442443 stamp := bundle.ShortStamp(host.Volumes().Volumes()[0].Manifest().Version.Stamp)444 tile := get(t, server.Client(), server.URL+"/data/v/earth/"+stamp+"/tiles/earth/0/0/0.jpg")445 if _, err := jpeg.Decode(bytes.NewReader(tile)); err != nil {446 t.Fatalf("the served tile does not decode: %v", err)447 }448}449450func get(t *testing.T, client *http.Client, url string) []byte {451 t.Helper()452 response, err := client.Get(url)453 if err != nil {454 t.Fatal(err)455 }456 defer response.Body.Close()457 if response.StatusCode != http.StatusOK {458 t.Fatalf("GET %s answered %d", url, response.StatusCode)459 }460 body, err := io.ReadAll(response.Body)461 if err != nil {462 t.Fatal(err)463 }464 return body465}