Caching.Tests fixtures: element ids are 12-hex literals, the form the id rule accepts

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-14 19:41:50 +02:00
commit b86becca93
3 changed files with 51 additions and 51 deletions

View file

@ -308,7 +308,7 @@ namespace GameCult.Caching.Tests
{
Id = "leaves", Flag = true, Blob = new byte[] { 1, 2, 3 }, I = -7, L = 5_000_000_000, F = 0.1f, D = 0.1, MaybeI = 9,
Tags = new List<string> { "a", "b" }, Names = new[] { "x", "y" }, Any = "text",
Nested = new LeafInner { N = "inner", W = 0.1f, Id = "n" }, Items = new List<LeafInner> { new() { N = "one", W = 0.1f, Id = "i1" }, new() { N = "two", W = 1.5f, Id = "i2" } },
Nested = new LeafInner { N = "inner", W = 0.1f, Id = "000000000011" }, Items = new List<LeafInner> { new() { N = "one", W = 0.1f, Id = "000000000012" }, new() { N = "two", W = 1.5f, Id = "000000000013" } },
Shape = new LeafCircle { R = 0.1f }, Floats = new[] { 0.1f, 1.5f }, MaybeF = 0.1f,
};

View file

@ -43,7 +43,7 @@ namespace GameCult.Caching.Tests
};
// Unit8: slots [id].
private static Emit UnitArm(int key) => (ref MessagePackWriter w) => { w.WriteArrayHeader(2); w.Write(key); w.WriteArrayHeader(1); w.Write("u"); };
private static Emit UnitArm(int key) => (ref MessagePackWriter w) => { w.WriteArrayHeader(2); w.Write(key); w.WriteArrayHeader(1); w.Write("000000000007"); };
// Branch: slots [children, next, id].
private static Emit BranchArm(int key, Emit next, params Emit[] children) => (ref MessagePackWriter w) =>
@ -53,7 +53,7 @@ namespace GameCult.Caching.Tests
w.WriteArrayHeader(children.Length);
foreach (var child in children) child(ref w);
next(ref w);
w.Write("b");
w.Write("000000000008");
};
private static Emit List(params Emit[] items) => (ref MessagePackWriter w) =>
@ -123,22 +123,22 @@ namespace GameCult.Caching.Tests
[TestCase(7)]
[TestCase(10)]
public void UnknownArmInFieldRefused(int key) =>
AssertRefused(Doc(LeafArm(key, "r", Canary, 7)), NodeName, key, "3, 5, 8, 9");
AssertRefused(Doc(LeafArm(key, "000000000005", Canary, 7)), NodeName, key, "3, 5, 8, 9");
[TestCase(0)]
[TestCase(4)]
[TestCase(10)]
public void UnknownArmInListRefused(int key) =>
AssertRefused(Doc(LeafArm(3, "r", "ok", 1), List(UnitArm(8), LeafArm(key, "i", Canary, 7))), NodeName, key, "3, 5, 8, 9");
AssertRefused(Doc(LeafArm(3, "000000000005", "ok", 1), List(UnitArm(8), LeafArm(key, "000000000004", Canary, 7))), NodeName, key, "3, 5, 8, 9");
// Inside an arm's own list, and inside an arm's own field: the union nested under a union.
[TestCase(4)]
[TestCase(10)]
public void UnknownArmNestedInArmRefused(int key)
{
AssertRefused(Doc(BranchArm(5, Nil, UnitArm(8), LeafArm(key, "c", Canary, 7))), NodeName, key, "3, 5, 8, 9");
AssertRefused(Doc(BranchArm(5, LeafArm(key, "n", Canary, 7))), NodeName, key, "3, 5, 8, 9");
AssertRefused(Doc(BranchArm(5, Nil, BranchArm(5, Nil, BranchArm(5, Nil, LeafArm(key, "deep", Canary, 7))))), NodeName, key, "3, 5, 8, 9");
AssertRefused(Doc(BranchArm(5, Nil, UnitArm(8), LeafArm(key, "000000000006", Canary, 7))), NodeName, key, "3, 5, 8, 9");
AssertRefused(Doc(BranchArm(5, LeafArm(key, "000000000003", Canary, 7))), NodeName, key, "3, 5, 8, 9");
AssertRefused(Doc(BranchArm(5, Nil, BranchArm(5, Nil, BranchArm(5, Nil, LeafArm(key, "000000000002", Canary, 7))))), NodeName, key, "3, 5, 8, 9");
}
// A union whose arms are also a union has its own key set: 7 is Outer's (OuterOnly) and not Inner's.
@ -175,9 +175,9 @@ namespace GameCult.Caching.Tests
foreach (var doc in new[]
{
new GuardDoc { Root = new Rogue() },
new GuardDoc { Items = { new Unit8 { Id = "u" }, new Rogue() } },
new GuardDoc { Root = new Branch { Id = "b", Children = { new Rogue() } } },
new GuardDoc { Root = new Branch { Id = "b", Next = new Rogue() } },
new GuardDoc { Items = { new Unit8 { Id = "000000000007" }, new Rogue() } },
new GuardDoc { Root = new Branch { Id = "000000000008", Children = { new Rogue() } } },
new GuardDoc { Root = new Branch { Id = "000000000008", Next = new Rogue() } },
new GuardDoc { Root = new DerivedLeaf() },
new GuardDoc { Crafted = new Unlisted() },
})
@ -206,26 +206,26 @@ namespace GameCult.Caching.Tests
Id = "d1",
Root = new Branch
{
Id = "b",
Id = "000000000008",
Children =
{
new Leaf { Id = "a", Content = "x", Weight = 1 },
new Unit8 { Id = "u" },
new Branch { Id = "b", Children = { new Branch { Id = "b", Children = { new Leaf { Id = "deep", Content = "y", Weight = 2 } } } } }
new Leaf { Id = "000000000001", Content = "x", Weight = 1 },
new Unit8 { Id = "000000000007" },
new Branch { Id = "000000000008", Children = { new Branch { Id = "000000000008", Children = { new Leaf { Id = "000000000002", Content = "y", Weight = 2 } } } } }
},
Next = new Leaf { Id = "n", Content = "z", Weight = 3 }
Next = new Leaf { Id = "000000000003", Content = "z", Weight = 3 }
},
Items = { new Unit8 { Id = "u" }, new Leaf { Id = "i", Content = "w", Weight = 4 }, null! },
Items = { new Unit8 { Id = "000000000007" }, new Leaf { Id = "000000000004", Content = "w", Weight = 4 }, null! },
Crafted = new InnerB { N = 9 },
Any = new InnerA { N = 8 }
};
private static byte[] KnownArmBytes() => Doc(
BranchArm(5, LeafArm(3, "n", "z", 3),
LeafArm(3, "a", "x", 1),
BranchArm(5, LeafArm(3, "000000000003", "z", 3),
LeafArm(3, "000000000001", "x", 1),
UnitArm(8),
BranchArm(5, Nil, BranchArm(5, Nil, LeafArm(3, "deep", "y", 2)))),
List(UnitArm(8), LeafArm(3, "i", "w", 4), Nil),
BranchArm(5, Nil, BranchArm(5, Nil, LeafArm(3, "000000000002", "y", 2)))),
List(UnitArm(8), LeafArm(3, "000000000004", "w", 4), Nil),
InnerArm(1, 9),
InnerArm(1, 8));
@ -458,13 +458,13 @@ namespace GameCult.Caching.Tests
new CultPersistedRecord
{
Key = "base", SchemaId = descriptor.SchemaId, StoredAt = "2026-01-01T00:00:00.0000000Z",
Payload = Doc(LeafArm(3, "r", "ok", 1))
Payload = Doc(LeafArm(3, "000000000005", "ok", 1))
},
new CultPersistedRecord
{
Key = "variant", SchemaId = descriptor.SchemaId, StoredAt = "2026-01-01T00:00:00.0000000Z",
Payload = Array.Empty<byte>(),
Variant = new CultVariantDelta("base", new[] { CultVariantOverride.Set(1, Raw(LeafArm(4, "r", Canary, 7))) })
Variant = new CultVariantDelta("base", new[] { CultVariantOverride.Set(1, Raw(LeafArm(4, "000000000005", Canary, 7))) })
}
}
}));
@ -498,14 +498,14 @@ namespace GameCult.Caching.Tests
try
{
var path = Path.Combine(folder, "guard.cc");
var genuine = Doc(LeafArm(3, "r", Canary, 7));
var genuine = Doc(LeafArm(3, "000000000005", Canary, 7));
using (var writer = CultCacheMessagePack.Create(path, new CultCacheOpenOptions { Registry = Registry, UseDirectoryStore = directory, StoreFlushOnDispose = true }))
{
await writer.UpsertAsync(typeof(GuardDoc), new GuardDoc { Id = "d1", Root = new Leaf { Id = "r", Content = Canary, Weight = 7 } }, new CultRecordKey("d1"));
await writer.UpsertAsync(typeof(GuardDoc), new GuardDoc { Id = "d1", Root = new Leaf { Id = "000000000005", Content = Canary, Weight = 7 } }, new CultRecordKey("d1"));
await writer.FlushAsync();
}
var forged = Doc(LeafArm(key, "r", Canary, 7));
var forged = Doc(LeafArm(key, "000000000005", Canary, 7));
Assert.That(forged.Length, Is.EqualTo(genuine.Length));
// A directory store names a page by the SHA-256 of its bytes and its manifest commits to that hash, so a forged
// page is renamed and the manifest told its new hash: the store then holds a well-formed page the reader must refuse.

View file

@ -162,24 +162,24 @@ namespace GameCult.Caching.Tests
Version = 3,
Root = root ?? new Box
{
Id = "root",
Id = "0000000000c0",
Children =
{
new Text { Id = "t1", Content = "hello" },
new Text { Id = "0000000000c1", Content = "hello" },
new Use
{
Id = "u1",
Id = "0000000000c2",
Composition = new Composition
{
Name = "comp",
Body = new Box { Id = "inner", Children = { new Text { Id = "deep", Content = "x" }, new Image { Id = "img", Uri = "uri://a", Alt = "alt" } } }
Body = new Box { Id = "0000000000d0", Children = { new Text { Id = "0000000000d1", Content = "x" }, new Image { Id = "0000000000d2", Uri = "uri://a", Alt = "alt" } } }
},
Slots = { new Text { Id = "s1", Content = "slot" }, null!, new Box { Id = "b2" } }
Slots = { new Text { Id = "0000000000e1", Content = "slot" }, null!, new Box { Id = "0000000000e2" } }
},
new Image { Id = "i2", Uri = "uri://b", Alt = "" }
new Image { Id = "0000000000c3", Uri = "uri://b", Alt = "" }
}
},
Outcomes = { new Pending { Message = "wait", Id = "p" }, new Completed { SourceVersion = 7, Id = "c" }, new Denied { Id = "d" } },
Outcomes = { new Pending { Message = "wait", Id = "0000000000a1" }, new Completed { SourceVersion = 7, Id = "0000000000a2" }, new Denied { Id = "0000000000a3" } },
Part = new Tool { Name = "hammer", Uses = 12 },
Crafted = new Part { Name = "gear", Count = 4 },
Missing = null
@ -255,13 +255,13 @@ namespace GameCult.Caching.Tests
private static byte[] Surface(Emit? root = null, Emit? outcomes = null, Emit? crafted = null, Extra? extra = null, Emit? trailing = null)
{
extra ??= new Extra();
root ??= BoxArm("root", Slots(
TextArm("t1", "hello"),
ArmOf(2, Str("u1"),
Plus(extra.Composition, Str("comp"), BoxArm("inner", Slots(TextArm("deep", "x"), ImageArm("img", "uri://a", "alt")))),
Slots(TextArm("s1", "slot", extra.ListElement), Nil, BoxArm("b2", Slots()))),
ImageArm("i2", "uri://b", "")), extra.RootArm);
outcomes ??= Slots(ArmOf(0, Str("wait"), Str("p")), ArmOf(1, Num(7), Str("c")), ArmOf(2, Str("d")));
root ??= BoxArm("0000000000c0", Slots(
TextArm("0000000000c1", "hello"),
ArmOf(2, Str("0000000000c2"),
Plus(extra.Composition, Str("comp"), BoxArm("0000000000d0", Slots(TextArm("0000000000d1", "x"), ImageArm("0000000000d2", "uri://a", "alt")))),
Slots(TextArm("0000000000e1", "slot", extra.ListElement), Nil, BoxArm("0000000000e2", Slots()))),
ImageArm("0000000000c3", "uri://b", "")), extra.RootArm);
outcomes ??= Slots(ArmOf(0, Str("wait"), Str("0000000000a1")), ArmOf(1, Num(7), Str("0000000000a2")), ArmOf(2, Str("0000000000a3")));
crafted ??= ArmOf(1, Str("gear"), Num(4));
var slots = new[] { Str("surface"), Num(3), root, outcomes, ArmOf(1, Str("hammer"), Num(12)), crafted, Nil };
return Raw(Slots(trailing != null ? slots.Append(trailing).ToArray() : extra.Record ? slots.Append(ExtraSlot).ToArray() : slots));
@ -284,7 +284,7 @@ namespace GameCult.Caching.Tests
new Case("unknown-arm-list.msgpack", "refused", "Outcome", 7, "unknown-arm",
Surface(outcomes: Slots(ArmOf(0, Str("wait")), ArmOf(7, Str(Canary))))),
new Case("unknown-arm-nested.msgpack", "refused", "Node", 9, "unknown-arm",
Surface(root: BoxArm("root", Slots(BoxArm("l1", Slots(BoxArm("l2", Slots(ArmOf(9, Str("deep"), Str(Canary)))))))))),
Surface(root: BoxArm("0000000000c0", Slots(BoxArm("0000000000d3", Slots(BoxArm("0000000000d4", Slots(ArmOf(9, Str("deep"), Str(Canary)))))))))),
new Case("inner-union-outer-key.msgpack", "refused", "CraftedItem", 2, "unknown-arm",
Surface(crafted: ArmOf(2, Str("gear"), Num(4)))),
new Case("arm-arity.msgpack", "refused", "Node", 1, "arity",
@ -461,30 +461,30 @@ namespace GameCult.Caching.Tests
Slots(slots)(ref w);
};
private static Emit WithOutcome(Emit element) => Slots(ArmOf(0, Str("wait"), Str("p")), ArmOf(1, Num(7), Str("c")), ArmOf(2, Str("d")), element);
private static Emit WithOutcome(Emit element) => Slots(ArmOf(0, Str("wait"), Str("0000000000a1")), ArmOf(1, Num(7), Str("0000000000a2")), ArmOf(2, Str("0000000000a3")), element);
private static Emit WrapChain(int wraps, Emit leaf) => wraps == 0 ? leaf : ArmOf(6, WrapChain(wraps - 1, leaf), Str("w"));
private static Emit WrapChain(int wraps, Emit leaf) => wraps == 0 ? leaf : ArmOf(6, WrapChain(wraps - 1, leaf), Str("0000000000b1"));
private static Node WrapChainValue(int wraps, Node leaf)
{
for (var i = 0; i < wraps; i++) leaf = new Wrap { Inner = leaf, Id = "w" };
for (var i = 0; i < wraps; i++) leaf = new Wrap { Inner = leaf, Id = "0000000000b1" };
return leaf;
}
// n arrays nested inside one another around a single string.
private static Emit Nested(int n) => n == 1 ? Slots(Str(Canary)) : Slots(Nested(n - 1));
private static Emit AtLimitLeaf => BoxArm("leaf", Slots());
private static Emit PastLimitLeaf => TextArm("leaf", Canary);
private static Emit AtLimitLeaf => BoxArm("0000000000f0", Slots());
private static Emit PastLimitLeaf => TextArm("0000000000f0", Canary);
private static IReadOnlyList<Case> BoundsCases() => new[]
{
new Case("arm-key-int32-min.msgpack", "accepted", "Outcome", int.MinValue, "arm-key-int32-min.msgpack",
Surface(outcomes: WithOutcome(ArmOf(int.MinValue, Str("f"))))),
Surface(outcomes: WithOutcome(ArmOf(int.MinValue, Str("0000000000a4"))))),
new Case("arm-key-int32-max.msgpack", "accepted", "Outcome", int.MaxValue, "arm-key-int32-max.msgpack",
Surface(outcomes: WithOutcome(ArmOf(int.MaxValue, Str("x"))))),
Surface(outcomes: WithOutcome(ArmOf(int.MaxValue, Str("0000000000a5"))))),
new Case("arm-key-wide-coding.msgpack", "accepted", "Outcome", int.MaxValue, "arm-key-int32-max.msgpack",
Surface(outcomes: WithOutcome(RawKey((ref MessagePackWriter w) => w.WriteUInt64(int.MaxValue), Str("x"))))),
Surface(outcomes: WithOutcome(RawKey((ref MessagePackWriter w) => w.WriteUInt64(int.MaxValue), Str("0000000000a5"))))),
new Case("arm-key-above-int32.msgpack", "refused", "Outcome", null, "key-out-of-range",
Surface(outcomes: WithOutcome(RawKey((ref MessagePackWriter w) => w.Write(0x80000000u))))),
new Case("arm-key-below-int32.msgpack", "refused", "Outcome", null, "key-out-of-range",
@ -650,7 +650,7 @@ namespace GameCult.Caching.Tests
var payload = Committed("depth-at-limit.msgpack");
Assert.That(DepthOf(payload), Is.EqualTo(Limit), "the vector nests exactly the limit, counted independently");
Assert.That(Convert.ToHexString(SerializeValue(Decode(payload))), Is.EqualTo(Convert.ToHexString(payload)), "read, then written again");
var built = Constructed(WrapChainValue(AtLimitWraps, new Box { Id = "leaf" }));
var built = Constructed(WrapChainValue(AtLimitWraps, new Box { Id = "0000000000f0" }));
Assert.That(Convert.ToHexString(SerializeValue(built)), Is.EqualTo(Convert.ToHexString(payload)), "the constructed value writes the vector");
}
@ -663,7 +663,7 @@ namespace GameCult.Caching.Tests
Assert.That(read, Does.Contain(typeof(UnionSurface).FullName).And.Contain($"the limit is {Limit}").And.Contain("refused on read").And.Not.Contain(Canary));
byte[]? written = null;
var write = Chain(Refusal(() => written = SerializeValue(Constructed(WrapChainValue(PastLimitWraps, new Text { Id = "leaf", Content = Canary })))));
var write = Chain(Refusal(() => written = SerializeValue(Constructed(WrapChainValue(PastLimitWraps, new Text { Id = "0000000000f0", Content = Canary })))));
Assert.That(written, Is.Null, "nothing is returned");
Assert.That(write, Does.Contain(typeof(UnionSurface).FullName).And.Contain($"the limit is {Limit}").And.Contain("refused on write").And.Not.Contain(Canary));