Entries that share a schema id are one schema seen by different writers, so the writer lists every id any of them lists, in any order: C# single-file and directory (CultSchemaCatalogEntry.MergeById), TypeScript and Python. Rust synthesises one entry per id and has no such merge. An id that one entry owns and another lists as compatible names the owner in Rust, TypeScript, Python and the TS inspector, as C# already did (own-id-over-compatible-v3). Also: a directory manifest in an unreadable format is the typed refusal naming it, cultnet-py applies and reports a record under the local schema id, and Rust's error prints its cause once. Vectors for the Mesh layout discriminator: a nil and an integer at catalog slot 5. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
83 lines
3.8 KiB
C#
83 lines
3.8 KiB
C#
using System;
|
|
using System.IO;
|
|
using FluentAssertions;
|
|
using GameCult.Caching;
|
|
using NUnit.Framework;
|
|
|
|
#nullable enable
|
|
|
|
namespace GameCult.Mesh.Tests;
|
|
|
|
// Shared refusal vectors: tests/vectors/document-variants-c0, read by every runtime's tests.
|
|
public sealed class CultMeshVariantRefusalTests
|
|
{
|
|
private const string ItemSchemaId = "sha256:88d3fdf0a927acf3b163940d8f8c7fe62b3316542ce771a67ec8bc038f594788";
|
|
|
|
private static string VectorPath(string name)
|
|
{
|
|
for (var dir = new DirectoryInfo(TestContext.CurrentContext.TestDirectory); dir != null; dir = dir.Parent)
|
|
{
|
|
var candidate = Path.Combine(dir.FullName, "tests", "vectors", "document-variants-c0", name);
|
|
if (File.Exists(candidate)) return candidate;
|
|
}
|
|
throw new FileNotFoundException($"Shared vector {name} not found above {TestContext.CurrentContext.TestDirectory}.");
|
|
}
|
|
|
|
private static CultStoreUnreadableException Refusal(string vector) =>
|
|
Assert.Throws<CultStoreUnreadableException>(() =>
|
|
CultMesh.ReadSingleFileDocumentPayload(VectorPath(vector), new CultRecordKey("item:bellows"), ItemSchemaId))!;
|
|
|
|
[Test]
|
|
public void ReadSingleFileDocument_RefusesUnknownHeaderByName() =>
|
|
Refusal("unknown-header.msgpack").Message.Should().Contain("cultcache.store.v9");
|
|
|
|
[Test]
|
|
public void ReadSingleFileDocument_RefusesExtraRecordSlotNamingTheRecord()
|
|
{
|
|
var message = Refusal("extra-slot-full-payload.msgpack").Message;
|
|
message.Should().Contain("item:anvil").And.Contain(ItemSchemaId);
|
|
}
|
|
|
|
[Test]
|
|
public void ReadSingleFileDocument_RefusesExtraRecordSlotInTheLegacyCatalogLayoutToo()
|
|
{
|
|
var message = Refusal("legacy-catalog-extra-slot.msgpack").Message;
|
|
message.Should().Contain("item:anvil").And.Contain(ItemSchemaId);
|
|
}
|
|
|
|
// The older catalog layout is read by the same store reader, so it is held to one array too: the same store with bytes after it
|
|
// is refused, and only the bytes make the difference.
|
|
[Test]
|
|
public void ReadSingleFileDocument_ReadsTheLegacyCatalogLayoutButNotWithBytesAfterIt()
|
|
{
|
|
CultMesh.ReadSingleFileDocumentPayload(VectorPath("legacy-catalog-plain.msgpack"), new CultRecordKey("item:bellows"), ItemSchemaId)
|
|
.Should().NotBeEmpty();
|
|
|
|
Refusal("legacy-catalog-trailing.msgpack").Message.Should().Contain("after its MessagePack array");
|
|
}
|
|
|
|
// A store in the current layout with one catalog slot malformed is refused as CultCache refuses it. It is not the older layout,
|
|
// so it is not read with the older reader, which would take the malformed canonical schema slot for a slot to skip.
|
|
[TestCase("current-catalog-bad-canonical.msgpack")]
|
|
[TestCase("current-catalog-bad-members.msgpack")]
|
|
[TestCase("current-catalog-bad-compat.msgpack")]
|
|
[TestCase("current-catalog-bad-slot5-nil.msgpack")]
|
|
[TestCase("current-catalog-bad-slot5-int.msgpack")]
|
|
public void ReadSingleFileDocument_RefusesAMalformedCurrentCatalogEntry(string vector)
|
|
{
|
|
Refusal(vector).Path.Should().Be(VectorPath(vector));
|
|
}
|
|
|
|
// Q6: CultMesh does not resolve variants. It refuses a variant key naming it and its base, and still reads the plain
|
|
// records of the same v2 store.
|
|
[Test]
|
|
public void ReadSingleFileDocument_RefusesAVariantKeyByNameButReadsItsPlainNeighbours()
|
|
{
|
|
var refusal = Assert.Throws<NotSupportedException>(() =>
|
|
CultMesh.ReadSingleFileDocumentPayload(VectorPath("variant-v2.msgpack"), new CultRecordKey("item:anvil-big"), ItemSchemaId))!;
|
|
refusal.Message.Should().Contain("item:anvil-big").And.Contain("item:anvil'");
|
|
|
|
CultMesh.ReadSingleFileDocumentPayload(VectorPath("variant-v2.msgpack"), new CultRecordKey("item:bellows"), ItemSchemaId)
|
|
.Should().NotBeEmpty();
|
|
}
|
|
}
|