Initial Commit
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import fc from "fast-check";
|
||||
|
||||
import {
|
||||
BITSET_HEADER_SIZE,
|
||||
chunkIdsToRawBitset,
|
||||
decodeBitset as decodeFramed,
|
||||
encodeBitset as encodeFramed,
|
||||
rawBitsetToChunkIds,
|
||||
} from "../src/codec/bitset.js";
|
||||
import { DMP_CODES, DmpError } from "../src/error-codes.js";
|
||||
import {
|
||||
decodeBitsetToChunkIds as decodeBitset,
|
||||
encodeBitsetFromChunkIds as encodeBitset,
|
||||
SELECT_FORM_BITSET_THRESHOLD,
|
||||
selectForm,
|
||||
selectWhole,
|
||||
} from "../src/select/form.js";
|
||||
|
||||
describe("@suaveplan/drive-mesh-protocol — selectForm", () => {
|
||||
it("rejects empty needed list with EMPTY_REQUEST", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
expect(() => selectForm([])).toThrow(DmpError);
|
||||
try {
|
||||
selectForm([]);
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(DmpError);
|
||||
expect((err as DmpError).code).toBe(DMP_CODES.EMPTY_REQUEST);
|
||||
}
|
||||
});
|
||||
|
||||
it("returns kind:'specific' for ≤ threshold counts", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const result = selectForm([0, 5, 17]);
|
||||
expect(result.kind).toBe("specific");
|
||||
if (result.kind === "specific") {
|
||||
expect(result.chunkIds).toEqual([0, 5, 17]);
|
||||
}
|
||||
});
|
||||
|
||||
it("returns kind:'specific' at exactly threshold", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const ids = Array.from(
|
||||
{ length: SELECT_FORM_BITSET_THRESHOLD },
|
||||
(_, i) => i
|
||||
);
|
||||
const result = selectForm(ids);
|
||||
expect(result.kind).toBe("specific");
|
||||
});
|
||||
|
||||
it("returns kind:'bitset' for > threshold counts", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const ids = Array.from(
|
||||
{ length: SELECT_FORM_BITSET_THRESHOLD + 1 },
|
||||
(_, i) => i
|
||||
);
|
||||
const result = selectForm(ids);
|
||||
expect(result.kind).toBe("bitset");
|
||||
if (result.kind === "bitset") {
|
||||
expect(result.bytes).toBeInstanceOf(Uint8Array);
|
||||
expect(result.bytes.length).toBeGreaterThan(BITSET_HEADER_SIZE);
|
||||
}
|
||||
});
|
||||
|
||||
it("honors a caller-supplied threshold override", { timeout: 5000 }, () => {
|
||||
const result = selectForm([0, 1, 2], { threshold: 2 });
|
||||
expect(result.kind).toBe("bitset");
|
||||
});
|
||||
|
||||
it("threshold is exposed as the documented 64", { timeout: 5000 }, () => {
|
||||
expect(SELECT_FORM_BITSET_THRESHOLD).toBe(64);
|
||||
});
|
||||
});
|
||||
|
||||
describe("@suaveplan/drive-mesh-protocol — selectWhole", () => {
|
||||
it("returns the canonical whole form", { timeout: 5000 }, () => {
|
||||
expect(selectWhole()).toEqual({ kind: "whole" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("@suaveplan/drive-mesh-protocol — chunkIdsToRawBitset / rawBitsetToChunkIds", () => {
|
||||
it("encodes a single bit", { timeout: 5000 }, () => {
|
||||
const raw = chunkIdsToRawBitset([0], 1);
|
||||
expect(raw.length).toBe(1);
|
||||
expect(raw[0]).toBe(0b00000001);
|
||||
});
|
||||
|
||||
it("encodes bits across byte boundaries", { timeout: 5000 }, () => {
|
||||
const raw = chunkIdsToRawBitset([0, 7, 8, 15], 16);
|
||||
expect(raw.length).toBe(2);
|
||||
expect(raw[0]).toBe(0b10000001);
|
||||
expect(raw[1]).toBe(0b10000001);
|
||||
});
|
||||
|
||||
it("rejects negative chunk ids", { timeout: 5000 }, () => {
|
||||
expect(() => chunkIdsToRawBitset([-1], 8)).toThrow(DmpError);
|
||||
});
|
||||
|
||||
it("rejects ids past bitLength", { timeout: 5000 }, () => {
|
||||
expect(() => chunkIdsToRawBitset([10], 8)).toThrow(DmpError);
|
||||
});
|
||||
|
||||
it("decodes returns indexes in ascending order", { timeout: 5000 }, () => {
|
||||
const raw = chunkIdsToRawBitset([10, 3, 17, 0], 18);
|
||||
expect(rawBitsetToChunkIds(raw, 18)).toEqual([0, 3, 10, 17]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("@suaveplan/drive-mesh-protocol — encodeBitset / decodeBitset (framed)", () => {
|
||||
it("framed bytes carry the magic prefix", { timeout: 5000 }, () => {
|
||||
const bytes = encodeBitset([0, 5, 17]);
|
||||
expect(bytes[0]).toBe(0x44); // 'D'
|
||||
expect(bytes[1]).toBe(0x4d); // 'M'
|
||||
expect(bytes[2]).toBe(0x50); // 'P'
|
||||
expect(bytes[3]).toBe(0x42); // 'B'
|
||||
});
|
||||
|
||||
it("decode honours the embedded bit-length", { timeout: 5000 }, () => {
|
||||
const framed = encodeFramed(chunkIdsToRawBitset([0, 5, 17], 18), 18);
|
||||
const round = decodeFramed(framed);
|
||||
expect(round.bitLength).toBe(18);
|
||||
});
|
||||
|
||||
it("roundtrip property: decode(encode(x)) preserves the sorted set", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc
|
||||
.array(fc.integer({ min: 0, max: 1023 }), {
|
||||
minLength: 1,
|
||||
maxLength: 64,
|
||||
})
|
||||
.map((arr) => Array.from(new Set(arr))),
|
||||
(input) => {
|
||||
const sorted = [...input].sort((a, b) => a - b);
|
||||
const framed = encodeBitset(sorted);
|
||||
const recovered = decodeBitset(framed);
|
||||
expect(recovered).toEqual(sorted);
|
||||
}
|
||||
),
|
||||
{ numRuns: 50 }
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user