CultLib/tests/vectors/document-variants-c2a/readability/generate.py
Libby bacde9fb8e Add legacy-dup-key readability vector: legacy envelope array with key a under two types
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-11 14:42:25 +00:00

82 lines
6 KiB
Python

"""Generates the shared store-readability vectors. Every runtime's tests read the committed bytes and the verdicts in
manifest.txt; nothing regenerates them at test time. Run: python generate.py (needs msgpack).
A file may be replaced by a flush or a commit exactly when the runtime's own reader opens it, so one verdict per runtime
covers open, flush and commit. Each vector is the bytes named; the trailing and truncated ones are cut from v3-base.
"""
import pathlib
import unicodedata
import msgpack
here = pathlib.Path(__file__).resolve().parent
whole = (here.parent / "v3-base.msgpack").read_bytes()
pack = lambda value: msgpack.packb(value, use_bin_type=True)
base = msgpack.unpackb(whole, raw=False)
# A second published schema, and a record of it under the key alpha.
other_entry = ["vectors.other", "vectors.other", "vectors.other.v1", "vectors.other", '{"schemaName":"vectors.other","schemaVersion":"vectors.other.v1","members":[]}', ["vectors.other"], []]
other_record = ["alpha", "vectors.other"] + base[2][0][2:]
# The same visible key as U+00E9 (NFC) and U+0065 U+0301 (NFD): equal after normalisation, unequal ordinally.
nfc, nfd = "\u00e9", "e\u0301"
assert unicodedata.normalize("NFC", nfd) == unicodedata.normalize("NFC", nfc) and nfc != nfd
assert "Alpha".casefold() == "alpha" and "Alpha" != "alpha"
vectors = {
"zero-byte": b"",
"empty-array": pack([]),
# A legacy envelope array: an array of maps. Rust, TypeScript and Python read it; C# has no legacy reader.
"legacy-envelopes": pack([{"key": "old", "type": "old.type", "payload": pack([]), "storedAt": "2026-01-01T00:00:00Z"}]),
# A legacy envelope array holding key a under two types: a key is one record, whatever the type. Refused like a v3 store with a shared key.
"legacy-dup-key": pack([{"key": "a", "type": "old.one", "payload": pack([]), "storedAt": "2026-01-01T00:00:00Z"}, {"key": "a", "type": "old.two", "payload": pack([]), "storedAt": "2026-01-01T00:00:00Z"}]),
"legacy-trailing": None, # filled below: the legacy array plus bytes after it
"ints": pack([1, 2, 3]),
"nil-first": pack([None, "x"]),
"nested-array": pack([["cultcache.store.v9", [], []]]),
"string-first": pack(["hello", 1]),
"header-v9": pack(["cultcache.store.v9", [], []]),
# A header stored as MessagePack bin, not str: every runtime refuses it as unreadable, never reads or names it.
"bin-header-v1": pack([b"cultcache.store.v1", [], []]),
"bin-header-v3": pack([b"cultcache.store.v3", [], []]),
"bin-header-v9": pack([b"cultcache.store.v9", [], []]),
"scalar": b"\x01",
"map": b"\x80",
"truncated": whole[:-1],
"trailing-byte": whole + b"\x01",
"trailing-bytes": whole + b"\x01\x02\x03",
"trailing-store": whole + whole,
# A header and a body of the wrong shape, for both readable headers.
"bad-body-v1": pack(["cultcache.store.v1", 5, 6]),
"bad-body-v3": pack(["cultcache.store.v3", 5, 6]),
# A header and nothing else: not a store, whatever the header says.
"header-only-v1": pack(["cultcache.store.v1"]),
"header-only-v3": pack(["cultcache.store.v3"]),
# v3-base with its schema catalog emptied: every record names a schema the store does not publish.
"missing-schema-v3": pack([base[0], [], base[2]]),
# The same, with each payload opening with its schema version: no runtime recovers a schema from a payload.
"missing-schema-versioned-v3": pack([base[0], [], [record[:3] + [pack(["vectors.item.v1", "n", 1])] for record in base[2]]]),
# The record names its schema by an id the catalog publishes only as a compatible id of an entry with another id.
"compatible-id-only-v3": pack([base[0], [[base[1][0][0] + ".next"] + base[1][0][1:]], base[2]]),
# An id that one entry owns and a later entry lists as compatible names the entry that owns it (here, vectors.item, which does not list itself as compatible).
"own-id-over-compatible-v3": pack([base[0], [base[1][0][:5] + [[]] + base[1][0][6:]] + [["vectors.item.next", "vectors.other", "vectors.other.v1", "vectors.item.next", "{}", ["vectors.item"], []]], base[2]]),
# The same, with the entry that lists the id as compatible before the entry that owns it: a reader that takes the first entry
# naming an id in one pass reads the record as vectors.other.
"compatible-before-owner-v3": pack([base[0], [["vectors.item.next", "vectors.other", "vectors.other.v1", "vectors.item.next", "{}", ["vectors.item"], []], base[1][0][:5] + [[]] + base[1][0][6:]], base[2]]),
# v3-base plus a variant of alpha: a fifth record slot, [baseKey, overrides[]], over an empty payload, overriding the name. Only C# reads it.
"variant-slot-v3": pack([base[0], base[1], base[2] + [["gamma"] + base[2][0][1:3] + [b"", ["alpha", [[0, [[0, ""]], "", "gamma"]]]]]]),
# v3-base with a fourth top-level slot after the records.
"extra-top-slot": pack(base + [0]),
# A store holding two records under one key is unreadable, whatever else they agree or differ on. Every record names a
# schema the catalog publishes, so the shared key is the only defect.
"dup-key-same-schema-v3": pack([base[0], base[1], base[2] + [base[2][0]]]),
# Two records under one key, each naming a different published schema: the key alone is the identity.
"dup-key-other-schema-v3": pack([base[0], base[1] + [other_entry], [base[2][0], other_record, base[2][1]]]),
# alpha twice with different payload bytes: last-wins would silently pick one.
"dup-key-different-payload-v3": pack([base[0], base[1], base[2] + [base[2][0][:3] + [b"\x92\xa5alpha\x09"]]]),
# Keys are ordinal strings: no case folding and no Unicode normalisation, so these are distinct keys and the store reads.
"distinct-keys-case-v3": pack([base[0], base[1], base[2] + [["Alpha"] + base[2][0][1:]]]),
"distinct-keys-nfd-v3": pack([base[0], base[1], [[nfc] + base[2][0][1:], [nfd] + base[2][1][1:]]]),
}
vectors["legacy-trailing"] = vectors["legacy-envelopes"] + b"\x01\x02\x03"
for name, data in vectors.items():
(here / f"{name}.bin").write_bytes(data)
print(len(vectors), "vectors")