import test from "node:test"; import assert from "node:assert/strict"; import { mkdtemp, writeFile, symlink, rm } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { CandidateMetadataBudget, readCandidateMetadata } from "../src/execution-world/metadata.js"; import { loadCheckedInstallationCandidate } from "../src/execution-world/candidate.js"; test( "a real checked application can stage its replacement within the default metadata budget", { skip: !process.env.QX_CHECKED_CANDIDATE, }, async () => { const root = process.env.QX_CHECKED_CANDIDATE!; const budget = new CandidateMetadataBudget(); const first = await loadCheckedInstallationCandidate(root, {}, { budget, owner: "reader" }); try { const retained = budget.snapshot().bytes; const second = await loadCheckedInstallationCandidate(root, {}, { budget, owner: "reader" }); try { assert.equal(budget.snapshot().bytes, retained * 2); assert.deepEqual(second.reference, first.reference, "Compaction preserves exact checked identity"); assert.equal(second.graphExecution.kernel, second.kernel, "Graph representation shares the validated program"); assert.equal(second.graphExecution.queries, second.queries); // A deliberately tighter pool admits the serving installation but must // reject replacement parsing without leaking a candidate or tree lease. const tight = new CandidateMetadataBudget({ bytes: retained + 1, ownerBytes: retained + 1, candidates: 2, }); const blocker = tight.acquire("reader"); blocker.charge(retained); try { await assert.rejects( loadCheckedInstallationCandidate(root, {}, { budget: tight, owner: "reader" }), /CANDIDATE_METADATA_LIMIT/, ); assert.equal(tight.snapshot().bytes, retained); assert.equal(tight.snapshot().candidates, 1); } finally { blocker.release(); } const other = budget.acquire("other"); try { assert.throws(() => other.charge(budget.limits.bytes), /CANDIDATE_METADATA_LIMIT/); assert.equal(budget.snapshot().bytes, retained * 2, "Rejected admission must not leak charges"); } finally { other.release(); } } finally { second.metadata.release(); } assert.equal(budget.snapshot().bytes, retained, "The serving candidate survives failed or completed staging"); } finally { first.metadata.release(); } assert.equal(budget.snapshot().bytes, 0); assert.equal(budget.snapshot().candidates, 0); }, ); test("concurrent candidate reservations enforce aggregate and owner limits and release exactly once", () => { const budget = new CandidateMetadataBudget({ bytes: 1000, ownerBytes: 600, candidates: 3 }); const first = budget.acquire("a"), second = budget.acquire("a"), third = budget.acquire("b"); assert.throws(() => budget.acquire("c"), /CANDIDATE_METADATA_LIMIT/); first.charge(400); second.charge(200); third.charge(400); assert.throws(() => second.charge(1), /CANDIDATE_METADATA_LIMIT/); assert.throws(() => third.charge(1), /CANDIDATE_METADATA_LIMIT/); assert.equal(budget.snapshot().bytes, 1000); second.release(); second.release(); assert.equal(budget.snapshot().bytes, 800); assert.throws(() => second.charge(1), /CANDIDATE_METADATA_CLOSED/); first.release(); third.release(); assert.deepEqual(budget.snapshot(), { bytes: 0, candidates: 0, owners: 0, limits: budget.limits }); }); test("metadata files reserve parse/index space before allocation and refuse oversized or linked inputs", async () => { const root = await mkdtemp(join(tmpdir(), "qx-metadata-budget-")); const budget = new CandidateMetadataBudget({ bytes: 450, ownerBytes: 450, candidates: 2 }); const scope = budget.acquire("workspace"); try { const file = join(root, "metadata.json"); await writeFile(file, '{"ok":true}'); const input = await readCandidateMetadata(file, scope, 100); assert.equal(new TextDecoder().decode(input.bytes), '{"ok":true}'); const withInput = scope.bytes; input.releaseInput(); input.releaseInput(); const first = scope.bytes; assert.equal(withInput - first, (Buffer.byteLength('{"ok":true}') + 1) * 3); assert.ok(first > 12 * 3, "Decoded tree and derived metadata are reserved before parsing"); await assert.rejects(readCandidateMetadata(file, scope, 100), /CANDIDATE_METADATA_LIMIT/); assert.equal(scope.bytes, first, "Rejected tree allocation releases its transient input buffer"); await assert.rejects(readCandidateMetadata(file, scope, 5), /CANDIDATE_METADATA_FILE_LIMIT/); await symlink(file, join(root, "linked")); await assert.rejects(readCandidateMetadata(join(root, "linked"), scope, 100), { code: "ELOOP" }); await assert.rejects(readCandidateMetadata(root, scope, 100), /CANDIDATE_METADATA_FILE_LIMIT/); assert.equal(scope.bytes, first); input.releaseTree(); input.releaseTree(); assert.equal(scope.bytes, 0, "Discarded validation trees can release independently of the candidate"); } finally { scope.release(); await rm(root, { recursive: true, force: true }); } assert.equal(budget.snapshot().bytes, 0); }); test("JSON structure accounting is independent of indentation and covers strings, values and dense nodes", async () => { const root = await mkdtemp(join(tmpdir(), "qx-metadata-shape-")); const budget = new CandidateMetadataBudget(); try { for (const value of [{}, [null, true, false, 1, -3.5e8], { 'a"b': '雪\\"', nodes: [{}, {}, {}] }]) { const measured = []; for (const spaces of [0, 4]) { const file = join(root, "metadata.json"), text = JSON.stringify(value, null, spaces); await writeFile(file, text); const scope = budget.acquire("test"); try { const input = await readCandidateMetadata(file, scope, 10000); assert.deepEqual(JSON.parse(new TextDecoder().decode(input.bytes)), value); input.releaseInput(); measured.push(scope.bytes); } finally { scope.release(); } } assert.equal(measured[0], measured[1], "Whitespace changes input storage, not the allocated JSON shape"); assert.ok(measured[0] >= 128); } } finally { await rm(root, { recursive: true, force: true }); } assert.equal(budget.snapshot().bytes, 0); }); test("transient leases release exactly once, including after their parent closes", () => { const budget = new CandidateMetadataBudget({ bytes: 1000, ownerBytes: 600, candidates: 3 }); const scope = budget.acquire("a"); scope.charge(200); const release = scope.reserve(300); assert.throws(() => scope.reserve(101), /CANDIDATE_METADATA_LIMIT/); release(); release(); assert.equal(scope.bytes, 200); const afterClose = scope.reserve(100); scope.release(); afterClose(); assert.equal(scope.bytes, 0); assert.equal(budget.snapshot().bytes, 0); });