From 4b668002aa6bd5f4ee3a094fd8240508dff10e90 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 30 Sep 2026 14:15:49 +0200 Subject: [PATCH] cultmesh-ts: pin each served-bound limit with a fixture that can tell the rules apart The queue-bound puts carry a source runtime id, which the registry does not store, so sizing the record as received instead of as served is caught. The test states the served record's shape itself rather than asking the registry, so a registry that served a different record is caught too. A two-document snapshot over maxPayloadBytes proves a reply that cannot be sent reaches onError. Co-Authored-By: Claude Opus 5.5 --- packages/cultmesh-ts/test/cultmesh.test.ts | 35 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/cultmesh-ts/test/cultmesh.test.ts b/packages/cultmesh-ts/test/cultmesh.test.ts index f7c4c6c1..5d79dacc 100644 --- a/packages/cultmesh-ts/test/cultmesh.test.ts +++ b/packages/cultmesh-ts/test/cultmesh.test.ts @@ -3241,7 +3241,12 @@ type ServedBoundRig = { const servedBoundRegistry = () => new CultNetDocumentRegistry([defineCultNetDocumentBinding({ definition: noteDocument })]); -function notePut(messageId: string, recordKey: string, bodyLength: number): CultNetDocumentPutRawMessage { +function notePut( + messageId: string, + recordKey: string, + bodyLength: number, + sourceRuntimeId?: string, +): CultNetDocumentPutRawMessage { return { schemaVersion: "cultnet.document_put_raw.v0", messageId, @@ -3251,13 +3256,23 @@ function notePut(messageId: string, recordKey: string, bodyLength: number): Cult storedAt: "2026-09-30T00:00:00.000Z", payloadEncoding: "messagepack", payload: encode({ noteId: recordKey, body: "x".repeat(bodyLength) }), + ...(sourceRuntimeId ? { sourceRuntimeId } : {}), }, }; } -/** The encoded size of the reply that would serve this put's document alone. */ +/** + * The encoded size of the reply that would serve this put's document alone, + * under the shortest message id: the record as a snapshot serves it, which + * keeps no source fields. + */ function servedAloneBytes(put: CultNetDocumentPutRawMessage): number { - return encode(servedBoundRegistry().createRawSnapshotResponseForPut(put, "0")).byteLength; + const { schemaId, recordKey, storedAt, payloadEncoding, payload } = put.document; + return encode({ + schemaVersion: "cultnet.snapshot_response_raw.v0", + messageId: "0", + documents: [{ schemaId, recordKey, storedAt, payloadEncoding, payload }], + }).byteLength; } /** The body length whose served-alone reply is exactly `bytes`, with a guard. */ @@ -3372,7 +3387,8 @@ test("CultMesh TS RUDP document server refuses a put whose reply would overflow const atBound = bodyLengthForReplyBytes("note:at", 8 * 1024); const overBound = bodyLengthForReplyBytes("note:ov", 8 * 1024 + 1); await withServedBoundRig(0x10203061, { maxFragmentBytes: 1024, maxPendingReliablePackets: 8 }, async (rig) => { - rig.peer.send(notePut("put-over", "note:ov", overBound)); + // The puts carry a source the registry does not store, so only the served record is the right size. + rig.peer.send(notePut("put-over", "note:ov", overBound, "cultmesh-ts-served-bound-client")); const refusal = await waitForError(rig, "the over-bound put"); assert.ok(refusal instanceof CultMeshRudpUnservableDocumentError, refusal.message); assert.equal(refusal.messageId, "put-over"); @@ -3384,7 +3400,7 @@ test("CultMesh TS RUDP document server refuses a put whose reply would overflow assert.match(refusal.message, /8193 bytes in 9 fragments/); assert.deepEqual(rig.admitted, []); - rig.peer.send(notePut("put-at", "note:at", atBound)); + rig.peer.send(notePut("put-at", "note:at", atBound, "cultmesh-ts-served-bound-client")); await waitForAdmission(rig, "note:at"); assert.deepEqual(servedBodies(await snapshotUnderShortestId(rig, "note:at")), [["note:at", atBound]]); assert.equal(rig.errors.length, 1); @@ -3418,6 +3434,15 @@ test("CultMesh TS RUDP document server honours maxPayloadBytes for replies, puts assert.ok(!(rig.errors[1] instanceof CultMeshRudpUnservableDocumentError)); assert.match(rig.errors[1]!.message, /maxPayloadBytes is 4000/); assert.deepEqual(rig.admitted, ["note:at"]); + + // Two servable documents make a reply over the limit: it is not sent, and onError hears why. + rig.peer.send(notePut("put-second", "note:a2", atBound)); + await waitForAdmission(rig, "note:a2"); + rig.peer.sendSnapshotRequest({ schemaVersion: "cultnet.snapshot_request.v0", messageId: "0", recordKeys: ["note:at", "note:a2"] }); + const replyStartedAt = Date.now(); + while (rig.errors.length < 3 && Date.now() - replyStartedAt < 3_000) await delay(5); + assert.equal(rig.errors.length, 3); + assert.match(rig.errors[2]!.message, /reply cultnet\.snapshot_response_raw\.v0 is \d+ bytes; maxPayloadBytes is 4000/); }); });