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 / internal / authoring / build_test.go
build_test.go 3.7K
1package authoring23import (4 "bytes"5 "context"6 "os"7 "path/filepath"8 "strings"9 "testing"1011 "github.com/FelineStateMachine/atlas/format/vnext"12)1314func TestBuildSampleRegionIsNativeDeterministicAndFused(t *testing.T) {15 root := t.TempDir()16 projectPath := filepath.Join(root, "sample-region"+ProjectExtension)17 projectBody := strings.Replace(sampleProject, "sources:\n", `sources:18 - id: sample-features-secondary19 adapter: geojson20 locator: sample-region-secondary.geojson21 license: CC0-1.022 attribution: Secondary sample publisher23 mapping:24 feature-set: places25 identity: properties.id26 feature-title: properties.name27 geometry:28 source-space: sample-region/space29 transform:30 kind: identity31 properties:32 - field: sample.kind33 source: properties.kind34`, 1)35 if err := os.WriteFile(projectPath, []byte(projectBody), 0o644); err != nil {36 t.Fatal(err)37 }38 primary := `{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":"one","name":"Sample One","kind":"primary"},"geometry":{"type":"Point","coordinates":[32,48]}}]}`39 secondary := `{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"id":"one","name":"Sample One","kind":"secondary"},"geometry":{"type":"Point","coordinates":[33,49]}}]}`40 if err := os.WriteFile(filepath.Join(root, "sample-region.geojson"), []byte(primary), 0o644); err != nil {41 t.Fatal(err)42 }43 if err := os.WriteFile(filepath.Join(root, "sample-region-secondary.geojson"), []byte(secondary), 0o644); err != nil {44 t.Fatal(err)45 }46 cache, library := filepath.Join(root, "cache"), filepath.Join(root, "library")47 first, err := Build(context.Background(), BuildOptions{ProjectPath: projectPath, CacheDir: cache, LibraryDir: library})48 if err != nil {49 t.Fatal(err)50 }51 firstBytes, err := os.ReadFile(first.Path)52 if err != nil {53 t.Fatal(err)54 }55 second, err := Build(context.Background(), BuildOptions{ProjectPath: projectPath, CacheDir: cache, LibraryDir: library, Offline: true})56 if err != nil {57 t.Fatal(err)58 }59 secondBytes, err := os.ReadFile(second.Path)60 if err != nil {61 t.Fatal(err)62 }63 if !second.Present || first.Path != second.Path || !bytes.Equal(firstBytes, secondBytes) {64 t.Fatalf("rebuild differs: first=%+v second=%+v", first, second)65 }66 file, err := vnext.OpenFile(first.Path, vnext.StandardSchema())67 if err != nil {68 t.Fatal(err)69 }70 defer file.Close()71 volume, err := file.Volume()72 if err != nil {73 t.Fatal(err)74 }75 if len(volume.Worlds) != 1 || len(volume.Worlds[0].FeatureSets) != 1 || len(volume.Worlds[0].FeatureSets[0].Features) != 1 {76 t.Fatalf("unexpected semantic volume: %+v", volume)77 }78 feature := volume.Worlds[0].FeatureSets[0].Features[0]79 if len(feature.Provenance) != 2 {80 t.Fatalf("fused feature has provenance %+v", feature.Provenance)81 }82 if got := feature.Properties[0].Value.String; got != "secondary" {83 // The manifest puts the secondary source first; source order is the84 // explicit deterministic conflict policy.85 t.Fatalf("fused property = %q, want secondary", got)86 }87}8889func TestPlanOnlyDoesNotCreateCacheOrLibrary(t *testing.T) {90 root := t.TempDir()91 project := writeSampleProject(t, strings.Replace(sampleProject, "sample-region.geojson", filepath.Join(root, "missing.geojson"), 1))92 cache, library := filepath.Join(root, "cache"), filepath.Join(root, "library")93 result, err := Build(context.Background(), BuildOptions{ProjectPath: project, CacheDir: cache, LibraryDir: library, PlanOnly: true})94 if err != nil {95 t.Fatal(err)96 }97 if len(result.Plan.Requests) != 1 {98 t.Fatalf("plan has %d requests", len(result.Plan.Requests))99 }100 for _, path := range []string{cache, library} {101 if _, err := os.Stat(path); !os.IsNotExist(err) {102 t.Fatalf("plan created %s", path)103 }104 }105}