diff --git a/tests/GameCult.Caching.Tests/PythonWrittenStoreTests.cs b/tests/GameCult.Caching.Tests/PythonWrittenStoreTests.cs index 39c78ded..f83ae98d 100644 --- a/tests/GameCult.Caching.Tests/PythonWrittenStoreTests.cs +++ b/tests/GameCult.Caching.Tests/PythonWrittenStoreTests.cs @@ -154,7 +154,7 @@ namespace GameCult.Caching.Tests var py = pySnapshot.Records.Single(record => record.Key == "py"); change?.Invoke(py); snapshot.Records = snapshot.Records.Concat(new[] { py }).ToArray(); - snapshot.SchemaCatalog = pySnapshot.SchemaCatalog; + snapshot.SchemaCatalog = snapshot.SchemaCatalog.Concat(pySnapshot.SchemaCatalog).GroupBy(entry => entry.SchemaId).Select(group => group.First()).ToArray(); File.WriteAllBytes(path, CultDocumentMessagePackSerialization.SerializeSnapshot(snapshot)); Assert.That(HeaderOf(path), Is.EqualTo(CultPersistedStoreSnapshot.FormatV1), "the foreign writer keeps the v1 it found"); return opened; diff --git a/tests/GameCult.Mesh.Tests/CultMeshSingleFileElementIdTests.cs b/tests/GameCult.Mesh.Tests/CultMeshSingleFileElementIdTests.cs index 7d2f31c1..c4f86d1e 100644 --- a/tests/GameCult.Mesh.Tests/CultMeshSingleFileElementIdTests.cs +++ b/tests/GameCult.Mesh.Tests/CultMeshSingleFileElementIdTests.cs @@ -173,45 +173,41 @@ public sealed class CultMeshSingleFileElementIdTests cache.Get(Key)!.Representations.Select(entry => entry.Id).Should().Equal(first); } - // cultcache-py's record (tests/vectors/numeric-leaves/py-store/leaf-record.cc): v1 header, 12-hex element ids on its list. - // This assembly declares the same document, member for member, so its registry claims and reads the python record. - [CultDocument("cultcache.leaf-record", "cultcache.leaf_record.v1")] + // A list document defined in this assembly: the record another writer leaves holding ids under an unmarked header. + [CultDocument("mesh.test.id_list", "mesh.test.id_list.v1")] [MessagePackObject] - public sealed class MeshLeaf + public sealed class MeshIdList { - [Key(0)] [CultName] public string Id = string.Empty; - [Key(1)] public bool Flag; - [Key(2)] public byte[] Blob = Array.Empty(); - [Key(3)] public int I; - [Key(4)] public long L; - [Key(5)] public float F; - [Key(6)] public double D; - [Key(7)] public int? MaybeI; - [Key(8)] public List Tags = new(); - [Key(9)] public string[] Names = Array.Empty(); - [Key(10)] public object? Any; - [Key(11)] public MeshLeafInner? Nested; - [Key(12)] public List Items = new(); - [Key(13)] public IMeshLeafShape? Shape; - [Key(14)] public float[] Floats = Array.Empty(); - [Key(15)] public float? MaybeF; + [Key(0)] [CultName] public string Name { get; set; } = ""; + [Key(1)] public List Items { get; set; } = new(); } [MessagePackObject] - public sealed class MeshLeafInner + public sealed class MeshIdItem { - [Key(0)] public string N = string.Empty; - [Key(1)] public float W; - [Key(2)] [CultElementId] public string Id = string.Empty; + [Key(0)] public string N { get; set; } = ""; + [Key(1)] [CultElementId] public string Id { get; set; } = ""; } - [Union(0, typeof(MeshLeafCircle))] - public interface IMeshLeafShape { } + private static readonly CultRecordKey IdsKey = new("ids"); + private const string HexB = "bbbbbbbbbbbb"; - [MessagePackObject] - public sealed class MeshLeafCircle : IMeshLeafShape { [Key(0)] public float R; } - - private static readonly string[] PythonIds = { "000000000021", "000000000022" }; + // A v1 file whose record holds 12-hex element ids: the document is written with the type (v3), then the header is put back + // to the v1 a writer that does not decide would have left. + private string UnmarkedStoreHoldingIds() + { + var path = Path_("unmarked.cc"); + CultMesh.WriteSingleFileDocument( + path, + IdsKey, + new MeshIdList { Name = "ids", Items = { new MeshIdItem { Id = HexA }, new MeshIdItem { Id = HexB } } }, + registry: Registry(typeof(MeshIdList))); + var snapshot = CultDocumentMessagePackSerialization.DeserializeSnapshot(File.ReadAllBytes(path)); + snapshot.FormatVersion = CultPersistedStoreSnapshot.FormatV1; + File.WriteAllBytes(path, CultDocumentMessagePackSerialization.SerializeSnapshot(snapshot)); + HeaderOf(path).Should().Be("cultcache.store.v1"); + return path; + } private string PythonStore() { @@ -234,14 +230,25 @@ public sealed class CultMeshSingleFileElementIdTests // A CultMesh write is a writer with a type: it decides for the copied record from the bytes the file holds, whatever any // earlier read saw (a fresh store per call has read nothing). [Test] - public void ACultMeshWriteThatCopiesAPythonRecordHoldingIdsMarksTheStore() + public void ACultMeshWriteThatCopiesARecordHoldingIdsMarksTheStore() { - var path = PythonStore(); + var path = UnmarkedStoreHoldingIds(); - CultMesh.WriteSingleFileDocument(path, Key, Publication(), registry: Registry(typeof(MeshLeaf), typeof(CultMeshBodyPublicationDocument))); + CultMesh.WriteSingleFileDocument(path, Key, Publication(), registry: Registry(typeof(MeshIdList), typeof(CultMeshBodyPublicationDocument))); HeaderOf(path).Should().Be("cultcache.store.v3"); - CultMesh.ReadSingleFileDocument(path, new CultRecordKey("py")).Items.Select(item => item.Id).Should().Equal(PythonIds); + CultMesh.ReadSingleFileDocument(path, IdsKey, Registry(typeof(MeshIdList))).Items.Select(item => item.Id).Should().Equal(HexA, HexB); + } + + // A raw payload is opaque by design: it attaches no registry and decides nothing, even beside a record it could have read. + [Test] + public void ARawCultMeshWriteBesideARecordHoldingIdsLeavesTheHeaderAlone() + { + var path = UnmarkedStoreHoldingIds(); + + CultMesh.WriteSingleFileDocumentPayload(path, RawKey, new CultMeshSingleFileDocumentSchema("raw:schema", "RawSchema", "1"), null, new byte[] { 0x90 }); + + HeaderOf(path).Should().Be("cultcache.store.v1"); } // Soul s1 P8, committed: the writer's registry has only the type it writes, so the python record is foreign to it and it @@ -256,18 +263,7 @@ public sealed class CultMeshSingleFileElementIdTests HeaderOf(path).Should().Be("cultcache.store.v1"); } - // A raw payload is opaque by design: it attaches no registry and decides nothing, even beside a record it could have read. - [Test] - public void ARawCultMeshWriteBesideAPythonRecordHoldingIdsLeavesTheHeaderAlone() - { - var path = PythonStore(); - - CultMesh.WriteSingleFileDocumentPayload(path, RawKey, new CultMeshSingleFileDocumentSchema("raw:schema", "RawSchema", "1"), null, new byte[] { 0x90 }); - - HeaderOf(path).Should().Be("cultcache.store.v1"); - } - - // The check only marks: a typed write over a store already marked does not look at, and cannot drop, the mark. + // Typed writes beside records that hold no id leave the store unmarked: the check marks only on an id it found. [Test] public void ATypedWriteBesideARecordThatHoldsNoIdKeepsTheStoreUnmarked() {