CultLib/packages/cultmesh-ts/test/codec-documents.test.ts
Meta Vens b0956ebc10 cultmesh-ts tests: drop the reads that found a record under another schema id by parsing it
Main's codec-document tests and the RUDP document-handle test read a record
stored under codec_note, plain_note or a foreign id through a handle for an
alias schema, and one store test picked among foreign-id records by whether
they parse. retire-schema-alias makes that read refuse; C2a's own tests
(a handle reads only records under an id its definition declares) pin the
rule. The codec reads and syncs under the definition's own id stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 04:31:43 +02:00

209 lines
10 KiB
TypeScript

import assert from "node:assert/strict";
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { z } from "zod";
import {
CultCachePayloadDepthError,
MAX_PAYLOAD_DEPTH,
SingleFileMessagePackBackingStore,
codecs,
decodeDocumentPayload,
defineDocumentType,
documentPayloadBytes,
documentPayloadFromBytes,
encodeDocumentPayload,
} from "@gamecult/cultcache-ts";
import {
CultNetDocumentRegistry,
defineCultNetDocumentBinding,
type CultNetPeer,
} from "cultnet-ts";
import { CultMesh, cultMeshDocumentFromSingleFile, type CultMeshRudpDocumentPut } from "../src/index";
// A document written in the reference's positional shape, and a plain map document beside it. Every mesh reader of a
// payload reads it the way the cache does, so both read through the mesh.
const codecNote = defineDocumentType({
type: "cultmesh.codec_note",
schemaId: "cultmesh.codec_note.v0",
schema: z.object({ noteId: z.string(), body: z.string() }),
name: "noteId",
codec: codecs.slots<{ noteId: string; body: string }>({
0: ["noteId", codecs.string],
1: ["body", codecs.string],
}, "Tests.CodecNote"),
});
const codecReceipt = defineDocumentType({
type: "cultmesh.codec_receipt",
schemaId: "cultmesh.codec_receipt.v0",
schema: z.object({ commandId: z.string(), body: z.string() }),
name: "commandId",
codec: codecs.slots<{ commandId: string; body: string }>({
0: ["commandId", codecs.string],
1: ["body", codecs.string],
}, "Tests.CodecReceipt"),
});
const plainNote = defineDocumentType({
type: "cultmesh.plain_note",
schemaId: "cultmesh.plain_note.v0",
schema: z.object({ noteId: z.string(), body: z.string() }),
name: "noteId",
});
test("the mesh reads a stored codec document as the cache does, and a plain document unchanged", async () => {
const dir = await mkdtemp(join(tmpdir(), "cultmesh-ts-codec-store-"));
const codecPath = join(dir, "codec.ccmp");
const plainPath = join(dir, "plain.ccmp");
const codecNode = await CultMesh.startNode(codecPath, { documents: [codecNote] });
await codecNode.put(codecNote, "n1", { noteId: "n1", body: "positional" });
const plainNode = await CultMesh.startNode(plainPath, { documents: [plainNote] });
await plainNode.put(plainNote, "n2", { noteId: "n2", body: "map" });
assert.deepEqual(await cultMeshDocumentFromSingleFile(codecPath, codecNote).latest(), { noteId: "n1", body: "positional" });
assert.deepEqual(await cultMeshDocumentFromSingleFile(plainPath, plainNote).latest(), { noteId: "n2", body: "map" });
});
test("the mesh syncs a codec document from a peer snapshot and returns a codec receipt", async () => {
const connectionId = 0x10203060;
const dir = await mkdtemp(join(tmpdir(), "cultmesh-ts-codec-sync-"));
const source = await CultMesh.startNode(join(dir, "source.ccmp"), { documents: [codecNote, plainNote] });
await source.put(codecNote, "n1", { noteId: "n1", body: "synced positional" });
const target = await CultMesh.startNode(join(dir, "target.ccmp"), { documents: [codecNote] });
const commandBinding = defineCultNetDocumentBinding({ definition: codecNote });
const plainBinding = defineCultNetDocumentBinding({ definition: plainNote });
const receiptBinding = defineCultNetDocumentBinding({ definition: codecReceipt });
const puts: unknown[] = [];
const server = CultMesh.createRudpDocumentServer("cultmesh-ts-codec-server", connectionId, {
documents: new CultNetDocumentRegistry([commandBinding, plainBinding, receiptBinding]),
getCache: () => source.cache,
bindHost: "127.0.0.1", bindPort: 0, resendDelayMs: 25, resendPollMs: 5,
onDocumentPutRaw: async (document) => (puts.push(document.payload), {
binding: receiptBinding,
recordKey: document.recordKey,
value: { commandId: document.recordKey, body: "applied" },
}),
});
let peer: CultNetPeer | undefined;
try {
await server.start();
peer = await CultMesh.createRudpPeer("cultmesh-ts-codec-client", connectionId, `rudp://127.0.0.1:${server.bind.port}`, {
resendDelayMs: 25, resendPollMs: 5, connectTimeoutMs: 1_000,
});
const synced = await target.syncDocumentFromPeerSnapshot(peer, codecNote, "n1", { timeoutMs: 1_000 });
assert.deepEqual(synced, { noteId: "n1", body: "synced positional" });
assert.deepEqual(target.getRequired(codecNote, "n1"), { noteId: "n1", body: "synced positional" });
// A peer snapshot handle reads the same way.
assert.deepEqual(await CultMesh.documentFromPeerSnapshot(peer, codecNote, "n1", { timeoutMs: 1_000 }).latest(), { noteId: "n1", body: "synced positional" });
const receipt = await CultMesh.publishRudpDocumentAndWaitForReceipt(
"cultmesh-ts-codec-receipt", connectionId, `rudp://127.0.0.1:${server.bind.port}`,
commandBinding, "n2", { noteId: "n2", body: "command" }, receiptBinding,
{ messageId: "n2", receiptTimeoutMs: 1_000, resendPollMs: 5 },
);
assert.deepEqual(receipt, { commandId: "n2", body: "applied" });
// The handler is handed the document, not its wire shape: the server reads a put through the registered definition.
await CultMesh.publishRudpDocumentAndWaitForReceipt(
"cultmesh-ts-plain-receipt", connectionId, `rudp://127.0.0.1:${server.bind.port}`,
plainBinding, "n4", { noteId: "n4", body: "plain command" }, receiptBinding,
{ messageId: "n4", receiptTimeoutMs: 1_000, resendPollMs: 5 },
);
assert.deepEqual(puts, [{ noteId: "n2", body: "command" }, { noteId: "n4", body: "plain command" }]);
} finally {
peer?.close();
server.close();
}
});
test("a RUDP put naming a schema the server has no definition for reaches the handler as the untouched opaque payload", async () => {
const connectionId = 0x10203070;
const stray = defineDocumentType({
type: "cultmesh.stray_note",
schemaId: "cultmesh.stray_note.v0",
schema: z.object({ noteId: z.string() }),
name: "noteId",
codec: codecs.slots<{ noteId: string }>({ 0: ["noteId", codecs.string] }, "Tests.StrayNote"),
});
const strayBinding = defineCultNetDocumentBinding({ definition: stray });
const known = defineCultNetDocumentBinding({ definition: plainNote });
const errors: Error[] = [];
const puts: CultMeshRudpDocumentPut[] = [];
const server = CultMesh.createRudpDocumentServer("cultmesh-ts-unregistered-server", connectionId, {
documents: new CultNetDocumentRegistry([known]),
bindHost: "127.0.0.1", bindPort: 0, resendDelayMs: 25, resendPollMs: 5,
onError: (error) => errors.push(error),
onDocumentPutRaw: async (document) => { puts.push(document); },
});
let peer: CultNetPeer | undefined;
try {
await server.start();
peer = await CultMesh.createRudpPeer("cultmesh-ts-unregistered-client", connectionId, `rudp://127.0.0.1:${server.bind.port}`, {
resendDelayMs: 25, resendPollMs: 5, connectTimeoutMs: 1_000,
});
peer.send(new CultNetDocumentRegistry([strayBinding]).createRawDocumentPutMessage(strayBinding, "unregistered-1", "k", { noteId: "k" }));
const startedAt = Date.now();
while (puts.length === 0 && Date.now() - startedAt < 2_000) await new Promise((resolve) => setTimeout(resolve, 5));
assert.deepEqual(errors, []);
assert.equal(puts.length, 1);
// Not read as plain msgpack: the bytes are the codec's slot record, handed over opaque, and the definition that reads
// them is the handler's to hold.
const put = puts[0]!;
assert.ok(!put.decoded, "the registry holds no definition for this schema");
const payload = put.payload;
assert.equal(Buffer.from(documentPayloadBytes(payload)).toString("hex"), "91a16b", "a one-slot codec record, not a map");
assert.deepEqual(Buffer.from(documentPayloadBytes(payload)), Buffer.from(documentPayloadBytes(encodeDocumentPayload(stray, { noteId: "k" }))));
assert.deepEqual(decodeDocumentPayload(stray, payload), { noteId: "k" });
} finally {
peer?.close();
server.close();
}
});
// A payload nested past the limit: MAX_PAYLOAD_DEPTH + 1 one-element arrays around a nil.
const pastLimitPayload = () => documentPayloadFromBytes(Uint8Array.from([...new Array(MAX_PAYLOAD_DEPTH + 1).fill(0x91), 0xc0]));
test("a payload past the nesting limit is refused at the network doors: a RUDP put and a single-file read", async () => {
const connectionId = 0x10203080;
const plainBinding = defineCultNetDocumentBinding({ definition: plainNote });
const registry = new CultNetDocumentRegistry([plainBinding]);
const errors: unknown[] = [];
const puts: CultMeshRudpDocumentPut[] = [];
const server = CultMesh.createRudpDocumentServer("cultmesh-ts-depth-server", connectionId, {
documents: registry,
bindHost: "127.0.0.1", bindPort: 0, resendDelayMs: 25, resendPollMs: 5,
onError: (error) => errors.push(error),
onDocumentPutRaw: async (document) => { puts.push(document); },
});
let peer: CultNetPeer | undefined;
try {
await server.start();
peer = await CultMesh.createRudpPeer("cultmesh-ts-depth-client", connectionId, `rudp://127.0.0.1:${server.bind.port}`, {
resendDelayMs: 25, resendPollMs: 5, connectTimeoutMs: 1_000,
});
peer.send(registry.createRawDocumentPutMessageFromEnvelope("deep-1", {
key: "k", type: plainNote.type, schemaId: plainNote.schemaId, payload: pastLimitPayload(), storedAt: new Date().toISOString(),
}));
const startedAt = Date.now();
while (errors.length === 0 && Date.now() - startedAt < 2_000) await new Promise((resolve) => setTimeout(resolve, 5));
assert.equal(errors.length, 1);
assert.ok(errors[0] instanceof CultCachePayloadDepthError, "the put is refused by the payload owner");
assert.deepEqual(puts, [], "the handler never saw the put");
// The server is still serving: an ordinary put goes through.
peer.send(registry.createRawDocumentPutMessage(plainBinding, "ok-1", "k2", { noteId: "k2", body: "fine" }));
while (puts.length === 0 && Date.now() - startedAt < 4_000) await new Promise((resolve) => setTimeout(resolve, 5));
assert.equal(puts.length, 1);
} finally {
peer?.close();
server.close();
}
const dir = await mkdtemp(join(tmpdir(), "cultmesh-ts-depth-store-"));
const path = join(dir, "deep.ccmp");
await new SingleFileMessagePackBackingStore(path).push({
key: "k", type: plainNote.type, schemaId: plainNote.schemaId, payload: pastLimitPayload(), storedAt: new Date().toISOString(),
});
await assert.rejects(() => cultMeshDocumentFromSingleFile(path, plainNote).latest(), CultCachePayloadDepthError);
});