Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
/**
* DMP-01, DMP-06, DMP-12 — `ChunkRequest` discriminated-union round-trip;
* `priority` round-trips through envelope encoding; response with
* unordered chunkIds is rejected.
*/
import { describe, expect, it } from "@suaveplan/testing/runner";
import {
chunkIdsToRawBitset,
DMP_CODES,
DmpError,
decodeRequestPayload,
decodeResponsePayload,
encodeFramedBitset,
encodeRequestPayload,
encodeResponsePayload,
selectWhole,
} from "../src/index.js";
import type {
ChunkRequestPayload,
ChunkResponsePayload,
} from "../src/types/protocol.types.js";
const FILE_ID = "a".repeat(64);
function roundTrip(payload: ChunkRequestPayload): ChunkRequestPayload {
return decodeRequestPayload(encodeRequestPayload(payload));
}
describe("DMP-01 — ChunkRequest discriminated union", () => {
it("every ChunkRequest form roundtrips through encode/decode", {
timeout: 5000,
}, () => {
const whole = roundTrip({
fileId: FILE_ID,
request: selectWhole(),
priority: "normal",
});
expect(whole.request.kind).toBe("whole");
const specific = roundTrip({
fileId: FILE_ID,
request: { kind: "specific", chunkIds: [0, 5, 17] },
priority: "normal",
});
expect(specific.request.kind).toBe("specific");
if (specific.request.kind === "specific") {
expect(specific.request.chunkIds).toEqual([0, 5, 17]);
}
const framed = encodeFramedBitset(chunkIdsToRawBitset([3, 7], 16), 16);
const bitset = roundTrip({
fileId: FILE_ID,
request: { kind: "bitset", bytes: framed },
priority: "high",
});
expect(bitset.request.kind).toBe("bitset");
if (bitset.request.kind === "bitset") {
expect(bitset.request.bytes).toEqual(framed);
}
});
it("exhaustive switch over the union compiles to no never-narrowing", {
timeout: 5000,
}, () => {
const cover = (req: ChunkRequestPayload["request"]): string => {
switch (req.kind) {
case "whole":
return "whole";
case "specific":
return `specific:${req.chunkIds.length}`;
case "bitset":
return `bitset:${req.bytes.length}`;
}
};
expect(cover({ kind: "whole" })).toBe("whole");
});
});
describe("DMP-06 — priority round-trip", () => {
it("priority field round-trips through envelope encoding", {
timeout: 5000,
}, () => {
for (const priority of [
"critical",
"high",
"normal",
"low",
"bulk",
] as const) {
const round = roundTrip({
fileId: FILE_ID,
request: selectWhole(),
priority,
});
expect(round.priority).toBe(priority);
}
});
});
describe("DMP-12 — response chunks ordered ascending", () => {
it("response with unordered chunkIds rejected", { timeout: 5000 }, () => {
const presence = encodeFramedBitset(chunkIdsToRawBitset([0, 1], 8), 8);
const payload: ChunkResponsePayload = {
fileId: FILE_ID,
chunks: [
{ chunkId: 1, bytes: new Uint8Array([1]) },
{ chunkId: 0, bytes: new Uint8Array([2]) },
],
presence,
generation: 1,
};
try {
encodeResponsePayload(payload);
throw new Error("expected unordered rejection");
} catch (err) {
expect(err).toBeInstanceOf(DmpError);
expect((err as DmpError).code).toBe(DMP_CODES.UNORDERED_RESPONSE);
}
});
it("decoder also rejects unordered chunks (defence-in-depth)", {
timeout: 5000,
}, () => {
// Build an ordered envelope, then mutate the JSON to swap.
const presence = encodeFramedBitset(
chunkIdsToRawBitset([0, 1, 2], 8),
8
);
const ok: ChunkResponsePayload = {
fileId: FILE_ID,
chunks: [
{ chunkId: 0, bytes: new Uint8Array([10]) },
{ chunkId: 1, bytes: new Uint8Array([20]) },
],
presence,
generation: 5,
};
const wire = encodeResponsePayload(ok);
const text = new TextDecoder().decode(wire);
const obj = JSON.parse(text);
// Swap the chunkIds.
const tmp = obj.chunks[0].chunkId;
obj.chunks[0].chunkId = obj.chunks[1].chunkId;
obj.chunks[1].chunkId = tmp;
const tampered = new TextEncoder().encode(JSON.stringify(obj));
try {
decodeResponsePayload(tampered);
throw new Error("expected unordered rejection");
} catch (err) {
expect(err).toBeInstanceOf(DmpError);
expect((err as DmpError).code).toBe(DMP_CODES.UNORDERED_RESPONSE);
}
});
});