clone

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

feat/atlas-next-horizon
atlas / main_test.go
main_test.go 6.7K
1package main23import (4 "bytes"5 "image/jpeg"6 "io"7 "io/fs"8 "net/http"9 "net/http/httptest"10 "os"11 "path/filepath"12 "strings"13 "testing"1415 "github.com/FelineStateMachine/atlas/format/vnext"16 "github.com/FelineStateMachine/atlas/internal/app"17 "github.com/FelineStateMachine/atlas/internal/app/hostenv/oshost"18)1920func includedEarth(t *testing.T) []byte {21 t.Helper()22 names, err := fs.Glob(included, "included/*.atlas")23 if err != nil {24 t.Fatal(err)25 }26 if len(names) != 1 {27 t.Fatalf("embedded %d volumes, want one Earth", len(names))28 }29 data, err := included.ReadFile(names[0])30 if err != nil {31 t.Fatal(err)32 }33 return data34}3536func openIncluded(t *testing.T) *vnext.Reader {37 t.Helper()38 data := includedEarth(t)39 reader, err := vnext.Open(bytes.NewReader(data), int64(len(data)), vnext.StandardSchema())40 if err != nil {41 t.Fatal(err)42 }43 return reader44}4546func TestIncludedEarthIsNativeAndSemantic(t *testing.T) {47 reader := openIncluded(t)48 if err := reader.Validate(); err != nil {49 t.Fatal(err)50 }51 if reader.VolumeID != "earth" || reader.Release.Title != "Earth" || reader.Release.CreatedAt != "2026-08-03T16:21:07Z" {52 t.Fatalf("identity/release = %s %#v", reader.VolumeID, reader.Release)53 }54 volume, err := reader.Volume()55 if err != nil {56 t.Fatal(err)57 }58 if len(volume.Worlds) != 1 || volume.Worlds[0].ID != "earth" {59 t.Fatalf("worlds = %#v", volume.Worlds)60 }61 points, areas := 0, 062 for _, set := range volume.Worlds[0].FeatureSets {63 for _, feature := range set.Features {64 switch feature.Geometry.Kind {65 case vnext.GeometryPoint:66 points++67 case vnext.GeometryPolygon:68 areas++69 }70 }71 }72 if points != 202 || areas != 177 {73 t.Fatalf("Earth holds %d points and %d areas", points, areas)74 }75 if len(reader.TableNames()) != 16 {76 t.Fatalf("native bundle has %d typed tables", len(reader.TableNames()))77 }78}7980func TestIncludedEarthPyramidOpensAtEveryLevel(t *testing.T) {81 reader := openIncluded(t)82 perLevel := map[string]int{}83 for _, name := range reader.BlobNames() {84 if !strings.HasPrefix(name, "tiles/earth/") {85 continue86 }87 parts := strings.Split(name, "/")88 perLevel[parts[2]]++89 }90 want := map[string]int{"0": 1, "1": 2, "2": 8, "3": 32, "4": 128, "5": 512, "6": 2048}91 for level, count := range want {92 if perLevel[level] != count {93 t.Errorf("level %s has %d tiles, want %d", level, perLevel[level], count)94 }95 }96 for _, name := range []string{"tiles/earth/0/0/0.jpg", "tiles/earth/6/63/31.jpg"} {97 data, err := reader.Blob(name)98 if err != nil {99 t.Fatal(err)100 }101 if _, err := jpeg.Decode(bytes.NewReader(data)); err != nil {102 t.Fatalf("%s: %v", name, err)103 }104 }105}106107func TestInstallIncludedIsIdempotentAndPreservesOtherBuilds(t *testing.T) {108 library := t.TempDir()109 if err := installIncluded(library); err != nil {110 t.Fatal(err)111 }112 entries, _ := os.ReadDir(library)113 if len(entries) != 1 {114 t.Fatalf("first install wrote %d files", len(entries))115 }116 installed := filepath.Join(library, entries[0].Name())117 before, _ := os.Stat(installed)118 if err := installIncluded(library); err != nil {119 t.Fatal(err)120 }121 after, _ := os.Stat(installed)122 if !after.ModTime().Equal(before.ModTime()) || after.Size() != before.Size() {123 t.Fatal("idempotent install touched file")124 }125 other := writeDistinctEarth(t, library)126 if err := installIncluded(library); err != nil {127 t.Fatal(err)128 }129 if _, err := os.Stat(other); err != nil {130 t.Fatal("other build was removed")131 }132 entries, _ = os.ReadDir(library)133 if len(entries) != 2 {134 t.Fatalf("library holds %d files, want two builds", len(entries))135 }136}137138func writeDistinctEarth(t *testing.T, dir string) string {139 t.Helper()140 volume := vnext.Volume{ID: "earth", Title: "Earth", Worlds: []vnext.World{{141 ID: "flat", Title: "Flat", CoordinateSpace: vnext.CoordinateSpace{ID: "flat-space", Kind: "synthetic", Unit: "pixel", Definition: "atlas:plane", Extent: [4]float64{0, 0, 1, 1}},142 Presentation: vnext.Presentation{ID: "default", Title: "Default"},143 }}}144 packed, err := vnext.Compile(volume)145 if err != nil {146 t.Fatal(err)147 }148 packed.Release.CreatedAt = "2026-09-01T00:00:00Z"149 temporary := filepath.Join(dir, "other.tmp")150 file, err := os.Create(temporary)151 if err != nil {152 t.Fatal(err)153 }154 if err := vnext.Write(file, packed); err != nil {155 file.Close()156 t.Fatal(err)157 }158 if err := file.Close(); err != nil {159 t.Fatal(err)160 }161 descriptor, err := vnext.Describe(temporary, vnext.StandardSchema())162 if err != nil {163 t.Fatal(err)164 }165 target := filepath.Join(dir, vnext.VersionedFileName(descriptor.Slug, vnext.Release{Title: descriptor.Title, CreatedAt: descriptor.CreatedAt, Revision: descriptor.Revision, Stamp: descriptor.Stamp, Worlds: descriptor.Worlds}))166 if err := os.Rename(temporary, target); err != nil {167 t.Fatal(err)168 }169 return target170}171172func TestDesktopStartupAndApplicationServeNativeEarth(t *testing.T) {173 data := t.TempDir()174 library := filepath.Join(data, "bundles")175 if err := os.MkdirAll(library, 0o755); err != nil {176 t.Fatal(err)177 }178 if err := installIncluded(library); err != nil {179 t.Fatal(err)180 }181 host, err := oshost.New(oshost.Options{BundlesDir: library, SessionsDir: filepath.Join(data, "sessions")})182 if err != nil {183 t.Fatal(err)184 }185 t.Cleanup(func() { _ = host.Volumes().(interface{ Close() error }).Close() })186 volumes := host.Volumes().Volumes()187 if len(volumes) != 1 || volumes[0].Info().Slug != "earth" {188 t.Fatalf("startup volumes = %#v", volumes)189 }190 server := httptest.NewServer(app.New(host, app.Options{}))191 defer server.Close()192 catalog := get(t, server.Client(), server.URL+"/data/catalog.json")193 if !strings.Contains(string(catalog), `"earth"`) {194 t.Fatalf("catalog = %s", catalog)195 }196 page := get(t, server.Client(), server.URL+"/v/earth/earth")197 if !strings.Contains(string(page), "Earth") {198 t.Fatal("Earth page did not open")199 }200 stamp := vnext.ShortStamp(volumes[0].Info().Stamp)201 schema := get(t, server.Client(), server.URL+"/data/v/earth/"+stamp+"/schema.json")202 if !bytes.Contains(schema, []byte(`"types"`)) {203 t.Fatal("native schema endpoint is not canonical schema JSON")204 }205 features := get(t, server.Client(), server.URL+"/data/v/earth/"+stamp+"/data/features.pack")206 if !bytes.HasPrefix(features, []byte("ATLASPK\x00")) {207 t.Fatal("native feature table endpoint has no typed-block framing")208 }209 tile := get(t, server.Client(), server.URL+"/data/v/earth/"+stamp+"/tiles/earth/0/0/0.jpg")210 if _, err := jpeg.Decode(bytes.NewReader(tile)); err != nil {211 t.Fatal(err)212 }213}214215func get(t *testing.T, client *http.Client, url string) []byte {216 t.Helper()217 response, err := client.Get(url)218 if err != nil {219 t.Fatal(err)220 }221 defer response.Body.Close()222 if response.StatusCode != http.StatusOK {223 t.Fatalf("GET %s answered %d", url, response.StatusCode)224 }225 body, err := io.ReadAll(response.Body)226 if err != nil {227 t.Fatal(err)228 }229 return body230}