1package vnext23import (4 "archive/zip"5 "bytes"6 "crypto/sha256"7 "encoding/hex"8 "encoding/json"9 "io"10 "strings"11 "testing"12)1314func TestBundleIsDeterministicAndKeepsPackedDataStored(t *testing.T) {15 t.Parallel()1617 bundle, err := Compile(minimalVolume())18 if err != nil {19 t.Fatalf("compile: %v", err)20 }21 bundle.Release.CreatedAt = "2026-08-01T20:13:08Z"22 bundle.Release.Revision = 423 var first, second bytes.Buffer24 if err := Write(&first, bundle); err != nil {25 t.Fatalf("write first: %v", err)26 }27 if err := Write(&second, bundle); err != nil {28 t.Fatalf("write second: %v", err)29 }30 if !bytes.Equal(first.Bytes(), second.Bytes()) {31 t.Fatal("same semantic volume produced different archive bytes")32 }33 opened, err := Open(bytes.NewReader(first.Bytes()), int64(first.Len()), StandardSchema())34 if err != nil {35 t.Fatalf("open release: %v", err)36 }37 if opened.Release.Title != "Minimal" || opened.Release.CreatedAt != "2026-08-01T20:13:08Z" || opened.Release.Revision != 4 {38 t.Fatalf("release metadata = %#v", opened.Release)39 }40 if len(opened.Release.Stamp) != 64 {41 t.Fatalf("release stamp = %q, want sha256", opened.Release.Stamp)42 }4344 archive, err := zip.NewReader(bytes.NewReader(first.Bytes()), int64(first.Len()))45 if err != nil {46 t.Fatalf("open zip: %v", err)47 }48 if archive.File[0].Name != manifestName {49 t.Fatalf("first entry = %s, want %s", archive.File[0].Name, manifestName)50 }51 for _, entry := range archive.File {52 if (bytes.HasSuffix([]byte(entry.Name), []byte(".pack")) || bytes.HasPrefix([]byte(entry.Name), []byte("assets/"))) && entry.Method != zip.Store {53 t.Fatalf("range-readable entry %s uses compression method %d", entry.Name, entry.Method)54 }55 }56}5758func TestReaderAcceptsKnownContainerFramings(t *testing.T) {59 t.Parallel()6061 current := nativeBytes(t, minimalVolume())62 for _, framing := range []uint16{minimumFraming, containerFraming} {63 data := rewriteFraming(t, current, framing)64 if _, err := Open(bytes.NewReader(data), int64(len(data)), StandardSchema()); err != nil {65 t.Fatalf("open framing %d: %v", framing, err)66 }67 }68 unknown := rewriteFraming(t, current, containerFraming+1)69 if _, err := Open(bytes.NewReader(unknown), int64(len(unknown)), StandardSchema()); err == nil || !strings.Contains(err.Error(), "unsupported") {70 t.Fatalf("unknown framing accepted: %v", err)71 }72}7374func TestNewerApplicationReadsOlderFileSchema(t *testing.T) {75 t.Parallel()7677 bundle, err := Compile(minimalVolume())78 if err != nil {79 t.Fatalf("compile: %v", err)80 }81 bundle.Release.CreatedAt = "2026-08-01T20:13:08Z"82 var archive bytes.Buffer83 if err := Write(&archive, bundle); err != nil {84 t.Fatalf("write: %v", err)85 }86 newField := Field{ID: IDFromName("com.example.future", "feature.score"), Name: "score", Kind: KindFloat64, Optional: true}87 newer, err := UnionSchemas(StandardSchema(), Schema{Types: []Type{{88 ID: CoreID("type.feature"), Name: "feature", Fields: []Field{newField},89 }}})90 if err != nil {91 t.Fatalf("make newer application schema: %v", err)92 }93 reader, err := Open(bytes.NewReader(archive.Bytes()), int64(archive.Len()), newer)94 if err != nil {95 t.Fatalf("newer application open older file: %v", err)96 }97 featureType, ok := reader.RuntimeSchema.Type(CoreID("type.feature"))98 if !ok {99 t.Fatal("runtime schema lost Feature")100 }101 if _, ok := featureType.Field(newField.ID); !ok {102 t.Fatal("runtime schema lost application-only field")103 }104 if _, err := reader.Volume(); err != nil {105 t.Fatalf("restore older volume: %v", err)106 }107}108109func TestBlockRejectsCorruptColumn(t *testing.T) {110 t.Parallel()111112 field := Field{ID: IDFromName("com.example", "name"), Name: "name", Kind: KindString}113 table := Table{TypeID: IDFromName("com.example", "type"), Rows: 1, Columns: []Column{{Field: field, Values: []Value{StringValue("atlas")}}}}114 data, err := EncodeBlock([32]byte{1}, table)115 if err != nil {116 t.Fatalf("encode: %v", err)117 }118 data[len(data)-1] ^= 0xff119 if _, err := DecodeBlock(data); err == nil {120 t.Fatal("decoder accepted a corrupt column")121 }122}123124func TestOpenWithLimitsRejectsImplausibleContainersBeforeReadingPayloads(t *testing.T) {125 t.Parallel()126127 volume := minimalVolume()128 volume.Assets[0].Data = []byte("sample asset")129 data := nativeBytes(t, volume)130 for name, limits := range map[string]Limits{131 "entries": {MaxEntries: 1},132 "blob": {MaxBlobBytes: 1},133 "total": {MaxTotalBytes: 1},134 } {135 t.Run(name, func(t *testing.T) {136 if _, err := OpenWithLimits(bytes.NewReader(data), int64(len(data)), StandardSchema(), limits); err == nil {137 t.Fatal("container outside its explicit limit was accepted")138 }139 })140 }141}142143func TestOpenRejectsUnreferencedAndCompressedPackedEntries(t *testing.T) {144 t.Parallel()145146 data := nativeBytes(t, minimalVolume())147 compressed := rewriteAtlas(t, data, func(header *zip.FileHeader) {148 if strings.HasSuffix(header.Name, ".pack") {149 header.Method = zip.Deflate150 }151 }, false)152 if _, err := Open(bytes.NewReader(compressed), int64(len(compressed)), StandardSchema()); err == nil || !strings.Contains(err.Error(), "not stored") {153 t.Fatalf("compressed packed entry = %v", err)154 }155 orphaned := rewriteAtlas(t, data, nil, true)156 if _, err := Open(bytes.NewReader(orphaned), int64(len(orphaned)), StandardSchema()); err == nil || !strings.Contains(err.Error(), "unreferenced") {157 t.Fatalf("orphan entry = %v", err)158 }159}160161func nativeBytes(t *testing.T, volume Volume) []byte {162 t.Helper()163 bundle, err := Compile(volume)164 if err != nil {165 t.Fatal(err)166 }167 bundle.Release.CreatedAt = "2026-08-08T12:00:00Z"168 var data bytes.Buffer169 if err := Write(&data, bundle); err != nil {170 t.Fatal(err)171 }172 return data.Bytes()173}174175func rewriteAtlas(t *testing.T, source []byte, mutate func(*zip.FileHeader), orphan bool) []byte {176 t.Helper()177 archive, err := zip.NewReader(bytes.NewReader(source), int64(len(source)))178 if err != nil {179 t.Fatal(err)180 }181 var output bytes.Buffer182 writer := zip.NewWriter(&output)183 for _, entry := range archive.File {184 reader, err := entry.Open()185 if err != nil {186 t.Fatal(err)187 }188 data, err := io.ReadAll(reader)189 reader.Close()190 if err != nil {191 t.Fatal(err)192 }193 header := entry.FileHeader194 if mutate != nil {195 mutate(&header)196 }197 destination, err := writer.CreateHeader(&header)198 if err != nil {199 t.Fatal(err)200 }201 if _, err := destination.Write(data); err != nil {202 t.Fatal(err)203 }204 }205 if orphan {206 destination, err := writer.CreateHeader(&zip.FileHeader{Name: "orphan.bin", Method: zip.Store})207 if err != nil {208 t.Fatal(err)209 }210 if _, err := destination.Write([]byte("orphan")); err != nil {211 t.Fatal(err)212 }213 }214 if err := writer.Close(); err != nil {215 t.Fatal(err)216 }217 return output.Bytes()218}219220func rewriteFraming(t *testing.T, source []byte, framing uint16) []byte {221 t.Helper()222 archive, err := zip.NewReader(bytes.NewReader(source), int64(len(source)))223 if err != nil {224 t.Fatal(err)225 }226 var output bytes.Buffer227 writer := zip.NewWriter(&output)228 for _, entry := range archive.File {229 reader, err := entry.Open()230 if err != nil {231 t.Fatal(err)232 }233 data, err := io.ReadAll(reader)234 reader.Close()235 if err != nil {236 t.Fatal(err)237 }238 if entry.Name == manifestName {239 var manifest bootstrap240 if err := json.Unmarshal(data, &manifest); err != nil {241 t.Fatal(err)242 }243 manifest.Framing = framing244 manifest.Release.Stamp = ""245 stampInput, err := json.Marshal(manifest)246 if err != nil {247 t.Fatal(err)248 }249 stamp := sha256.Sum256(stampInput)250 manifest.Release.Stamp = hex.EncodeToString(stamp[:])251 data, err = json.Marshal(manifest)252 if err != nil {253 t.Fatal(err)254 }255 }256 destination, err := writer.CreateHeader(&entry.FileHeader)257 if err != nil {258 t.Fatal(err)259 }260 if _, err := destination.Write(data); err != nil {261 t.Fatal(err)262 }263 }264 if err := writer.Close(); err != nil {265 t.Fatal(err)266 }267 return output.Bytes()268}269270func minimalVolume() Volume {271 return Volume{272 ID: "minimal", Title: "Minimal",273 Worlds: []World{{274 ID: "world", Title: "World",275 CoordinateSpace: CoordinateSpace{ID: "space", Kind: "synthetic", Unit: "unit", Definition: "atlas:plane", Extent: [4]float64{0, 0, 1, 1}},276 Presentation: Presentation{ID: "default", Title: "Default"},277 }},278 Assets: []Asset{{ID: "empty", MediaType: "application/octet-stream"}},279 }280}