Files
rea/experiments/atomic-codemode/native-byte-batch.test.mjs

222 lines
6.6 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { createHash } from "node:crypto";
import {
NativeByteBatchReads,
parseNativeByteBatch,
} from "./native-byte-batch.mjs";
import { FactFailure } from "./fact-error.mjs";
const a = { address: "0x1000", length: 2 },
b = { address: "0x2000", length: 2 };
const result = (request) => ({
binary: "/input",
kind: "data",
start: Number(BigInt(request.address)),
start_hex: request.address,
end: Number(BigInt(request.address) + 2n),
end_hex: "0x" + (BigInt(request.address) + 2n).toString(16),
count: 1,
bytes: 2,
hex: "0041",
truncated: false,
notes: [],
rows: [
{
address: Number(BigInt(request.address)),
address_hex: request.address,
size: 2,
bytes: "0041",
},
],
});
const report = (requests) => ({
schema_version: 1,
binary: "/input",
window_count: requests.length,
program_loads: 1,
output_bytes: 4096,
windows: requests.map((request, index) => ({
source_row: index,
request_address_hex: request.address,
requested_bytes: request.length,
ok: true,
result: result(request),
})),
});
const captured = (doc) => {
const text = typeof doc === "string" ? doc : JSON.stringify(doc);
return {
text,
capture: {
input: "/input",
output_bytes: 4096,
source: {
evidence_id: "ev_test",
sha256: createHash("sha256").update(text).digest("hex"),
bytes: Buffer.byteLength(text),
capture_complete: true,
},
},
};
};
const decode = (doc, requests = [a, b]) => {
const { text, capture } = captured(doc);
return parseNativeByteBatch(text, capture, requests);
};
test("one report keeps exact windows, shared source identity and per-window partial facts", () => {
const doc = report([a, { ...b, length: 3 }]),
rows = decode(doc, [a, { ...b, length: 3 }]);
assert.equal(rows[0].source.evidence_id, rows[1].source.evidence_id);
assert.deepEqual(
rows.map((r) => r.source_row),
[0, 1],
);
assert.equal(rows[0].coverage.status, "complete-for-requested-range");
assert.equal(rows[1].coverage.status, "partial");
assert.equal(rows[1].coverage.requested_bytes, 3);
assert.equal(rows[1].returned_bytes, 2);
assert.equal(rows[1].coverage.runtime_memory, "not-observed");
assert.equal(rows[1].coverage.original_file_mapping, "unknown");
});
test("native window errors and malformed individual data preserve valid sibling facts and Evidence", () => {
for (const mutate of [
(row) => {
row.ok = false;
delete row.result;
row.error = { code: "window_unavailable", message: "unmapped address" };
},
(row) => {
row.result.hex = "0000";
},
(row) => {
row.result.rows[0].address = 0x1000;
},
(row) => {
row.source_row = 0;
},
(row) => {
row.request_address_hex = a.address;
},
]) {
const doc = report([a, b]);
mutate(doc.windows[1]);
const rows = decode(doc);
assert(rows[0].ok);
assert.equal(rows[1].ok, false);
assert.equal(rows[1].error.details.evidence_id, "ev_test");
assert.equal(rows[1].error.details.source_row, 1);
assert.equal(rows[1].error.details.source_retained, true);
}
});
test("wrong envelope identity, count, format or rounded integer cannot publish invented empty success", () => {
for (const patch of [
{ binary: "/other" },
{ window_count: 1 },
{ program_loads: 2 },
{ output_bytes: 4095 },
{ schema_version: 2 },
{ windows: [] },
])
assert.throws(
() => decode({ ...report([a, b]), ...patch }),
(e) =>
e.code === "invalid_provider_output" &&
e.details.evidence_id === "ev_test",
);
const raw = JSON.stringify(report([a, b])).replace(
'"window_count":2',
'"window_count":2.00000000000000001',
);
assert.throws(() => decode(raw), { code: "invalid_provider_output" });
});
function owner() {
const captures = [],
sources = new Map();
let rejected;
const reads = new NativeByteBatchReads(
async (name, args) => {
if (rejected) {
const error = rejected;
rejected = undefined;
throw error;
}
const argv = args("/input", 4096);
assert.equal(argv[0], "read");
assert.equal(argv[1], "/input");
assert.equal(argv[2], "--atomic-windows");
assert.deepEqual(argv.slice(4), [
"--json",
"--atomic-output-bytes",
"4096",
]);
const requests = argv[3].split(",").map((part) => {
const [address, length] = part.split(":");
return { address, length: Number(length) };
});
const { text, capture } = captured(report(requests));
capture.source.evidence_id = "ev_" + (captures.length + 1);
captures.push({ name, requests });
sources.set(capture.source.evidence_id, text);
return capture;
},
(id) => sources.get(id),
);
return {
reads,
captures,
reject(error) {
rejected = error;
},
};
}
test("reordering, duplicate windows, mixed cached/new batches and caller mutation reuse exact facts", async () => {
const { reads, captures } = owner();
const first = await reads.read({ windows: [a, a, b] });
assert.equal(captures.length, 1);
assert.equal(captures[0].requests.length, 2);
assert.deepEqual(
first.results.map((r) => r.source_row),
[0, 0, 1],
);
first.results[0].hex = "ffff";
const reverse = await reads.read({ windows: [b, a] });
assert.equal(captures.length, 1);
assert.equal(reverse.results[1].hex, "0041");
const c = { address: "0x3000", length: 2 };
const mixed = await reads.read({ windows: [b, c, a] });
assert.equal(captures.length, 2);
assert.deepEqual(captures[1].requests, [c]);
assert.equal(
mixed.results[0].source.evidence_id,
first.results[2].source.evidence_id,
);
assert.notEqual(
mixed.results[1].source.evidence_id,
mixed.results[0].source.evidence_id,
);
reads.clear();
await reads.read({ windows: [a] });
assert.equal(captures.length, 3);
});
test("global capture failure retains cached successes and leaves transient failures retryable", async () => {
const { reads, captures, reject } = owner();
await reads.read({ windows: [a] });
reject(
new FactFailure("cancelled", "stopped", {
evidence_id: "ev_prefix",
source_retained: true,
source_complete: false,
}),
);
const partial = await reads.read({ windows: [a, b] });
assert(partial.results[0].ok);
assert.equal(partial.results[1].error.code, "cancelled");
assert.equal(partial.results[1].error.details.evidence_id, "ev_prefix");
const retry = await reads.read({ windows: [b] });
assert(retry.results[0].ok);
assert.equal(captures.length, 2);
});