1package workbench23import (4 "bytes"5 "encoding/json"6 "os"7 "path/filepath"8 "strconv"9 "testing"1011 "github.com/FelineStateMachine/atlas/format/bundle"12 "github.com/FelineStateMachine/atlas/format/semconv"13)1415// The bundles these tests measure.16//17// Every bundle is stated: written here from a few lines of Go, so a page test18// can say exactly what a build holds and then read the page back. They go19// through format/bundle's real writer: nothing opaque is checked in, and a20// bundle a test measures is a bundle the application could open.2122type bundleSpec struct {23 slug string24 title string25 createdAt string26 stamp string27 revision int28 conventions int29 worlds []worldSpec30}3132type worldSpec struct {33 slug string34 title string35 icon string36 attrs map[string]string37 features []featureSpec38 // merged is the world's ledger, stated as the payload carries it.39 merged []map[string]any40 lenses []map[string]any41}4243type featureSpec struct {44 id int6445 title string46 description string47}4849// write builds one bundle in dir and answers with its path.50func (b bundleSpec) write(t *testing.T, dir string) string {51 t.Helper()52 if b.slug == "" {53 b.slug = "hollowmere"54 }55 if b.title == "" {56 b.title = "Hollowmere"57 }58 if b.createdAt == "" {59 b.createdAt = "2026-01-01T00:00:00Z"60 }61 if b.stamp == "" {62 b.stamp = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"63 }64 if b.conventions == 0 {65 b.conventions = semconv.Version66 }6768 manifest := bundle.Manifest{69 Format: bundle.Format,70 FormatVersion: bundle.FormatVersion,71 Conventions: b.conventions,72 Volume: bundle.Volume{Slug: b.slug, Title: b.title},73 Version: bundle.Version{74 Stamp: b.stamp,75 CreatedAt: b.createdAt,76 Revision: b.revision,77 },78 TileGrid: bundle.TileGrid{SourceZoom: 5, FirstTile: 0, TileSize: 256, Size: 8192},79 }80 for _, world := range b.worlds {81 manifest.Worlds = append(manifest.Worlds, bundle.WorldEntry{82 Slug: world.slug,83 Title: world.titleOr(),84 Points: len(world.features),85 UpdatedAt: b.createdAt,86 })87 }8889 path := filepath.Join(dir, bundle.VersionedFileName(manifest))90 file, err := os.Create(path)91 if err != nil {92 t.Fatal(err)93 }94 defer file.Close()95 writer, err := bundle.NewWriter(file, manifest)96 if err != nil {97 t.Fatal(err)98 }99 for _, world := range b.worlds {100 payload, text, packed := world.parts(t)101 if err := writer.AddDeflated(bundle.WorldEntryName(world.slug, bundle.WorldSuffix), payload); err != nil {102 t.Fatal(err)103 }104 if err := writer.AddDeflated(bundle.WorldEntryName(world.slug, bundle.TextSuffix), text); err != nil {105 t.Fatal(err)106 }107 if err := writer.AddStored(bundle.WorldEntryName(world.slug, bundle.PackedSuffix), bytes.NewReader(packed)); err != nil {108 t.Fatal(err)109 }110 }111 if err := writer.Close(); err != nil {112 t.Fatal(err)113 }114 return path115}116117func (w worldSpec) titleOr() string {118 if w.title != "" {119 return w.title120 }121 return "Overworld"122}123124// parts is one world's three payloads: the collections and the ledger, the125// prose, and the packed point features.126func (w worldSpec) parts(t *testing.T) (payload, text, packed []byte) {127 t.Helper()128 attrs := w.attrs129 if attrs == nil {130 attrs = map[string]string{semconv.KeyGeometrySurface: semconv.SurfacePlane}131 }132 lenses := w.lenses133 if lenses == nil {134 lenses = []map[string]any{{"tiles": w.slug, "minZoom": 0, "maxZoom": 2, "formats": []string{"jpg"}}}135 }136 collection := map[string]any{137 "id": 1,138 "title": "Markers",139 "group": "Places",140 "kind": semconv.GeometryPoint,141 "attrs": map[string]string{semconv.KeyRenderAs: semconv.RenderAsPin},142 }143 if w.icon != "" {144 collection["iconAsset"] = w.icon145 }146 body := map[string]any{147 "attrs": attrs,148 "lenses": lenses,149 "collections": []any{collection},150 }151 if w.merged != nil {152 body["merged"] = w.merged153 }154 payload = marshal(t, body)155156 prose := map[string]any{}157 locations := make([]bundle.Location, 0, len(w.features))158 for at, feature := range w.features {159 if feature.description != "" {160 prose[strconv.FormatInt(feature.id, 10)] = map[string]any{"d": feature.description}161 }162 locations = append(locations, bundle.Location{163 ID: feature.id,164 Owner: 0,165 Lat: float64(at),166 Lng: float64(at),167 Title: feature.title,168 })169 }170 return payload, marshal(t, prose), bundle.PackLocations(locations)171}172173func marshal(t *testing.T, value any) []byte {174 t.Helper()175 data, err := json.Marshal(value)176 if err != nil {177 t.Fatal(err)178 }179 return data180}