89 lines
2.8 KiB
TypeScript
89 lines
2.8 KiB
TypeScript
/**
|
|
* DMP-08 — Advertisement bitset length invariant.
|
|
*
|
|
* In `ChunkAdvertisePayload`, the bit-length of `presence` MUST equal
|
|
* `totalChunks`; padding bits in the final byte MUST be zero. The codec
|
|
* rejects mismatched bitset wires with either
|
|
* `DMP.BITSET_LENGTH_MISMATCH` or `DMP.BITSET_PADDING_NONZERO`.
|
|
*/
|
|
|
|
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import {
|
|
BITSET_MAGIC,
|
|
chunkIdsToRawBitset,
|
|
DMP_CODES,
|
|
DmpError,
|
|
decodeAdvertisePayload,
|
|
encodeAdvertisePayload,
|
|
encodeFramedBitset,
|
|
} from "../src/index.js";
|
|
|
|
const FILE_ID = "b".repeat(64);
|
|
|
|
function makeAdvertise(presence: Uint8Array, totalChunks: number) {
|
|
return encodeAdvertisePayload({
|
|
fileId: FILE_ID,
|
|
presence,
|
|
totalChunks,
|
|
generation: 1,
|
|
timestamp: 0,
|
|
});
|
|
}
|
|
|
|
function makeFramedBitset(ids: number[], bitLength: number) {
|
|
return encodeFramedBitset(chunkIdsToRawBitset(ids, bitLength), bitLength);
|
|
}
|
|
|
|
describe("DMP-08 — bitset length invariant", () => {
|
|
it("advertisement with presence.length ≠ totalChunks rejected", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const presence = makeFramedBitset([0, 5], 16);
|
|
try {
|
|
makeAdvertise(presence, 32);
|
|
throw new Error("expected mismatch rejection");
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(DmpError);
|
|
expect((err as DmpError).code).toBe(
|
|
DMP_CODES.BITSET_LENGTH_MISMATCH
|
|
);
|
|
}
|
|
});
|
|
|
|
it("valid advertisement (bitLength == totalChunks) round-trips", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const presence = makeFramedBitset([0, 5, 7], 8);
|
|
const wire = makeAdvertise(presence, 8);
|
|
const decoded = decodeAdvertisePayload(wire);
|
|
expect(decoded.totalChunks).toBe(8);
|
|
expect(decoded.presence).toEqual(presence);
|
|
});
|
|
|
|
it("non-zero padding bits rejected", { timeout: 5000 }, () => {
|
|
// Hand-build a framed bitset with bitLength=10 (so high bits 10..15
|
|
// must be zero) but flip bit 14 to violate padding zeroing.
|
|
const raw = new Uint8Array([0x01, 0xff]);
|
|
// Build the wire envelope by hand.
|
|
const wire = new Uint8Array(12 + raw.length);
|
|
wire.set(BITSET_MAGIC, 0);
|
|
const view = new DataView(
|
|
wire.buffer,
|
|
wire.byteOffset,
|
|
wire.byteLength
|
|
);
|
|
view.setUint32(4, 10, false);
|
|
view.setUint32(8, raw.length, false);
|
|
wire.set(raw, 12);
|
|
try {
|
|
makeAdvertise(wire, 10);
|
|
throw new Error("expected padding-nonzero rejection");
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(DmpError);
|
|
expect((err as DmpError).code).toBe(
|
|
DMP_CODES.BITSET_PADDING_NONZERO
|
|
);
|
|
}
|
|
});
|
|
});
|