Files
rea/experiments/atomic-codemode/api.test.mjs
T

232 lines
8.0 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import Ajv2020 from "ajv/dist/2020.js";
import { methods, reference } from "./api.mjs";
import { AtomicSession } from "./session.mjs";
const factory = () => {
let sequence = 0;
const calls = [];
const create = async (path) => ({
context: { lease: `lease-${++sequence}`, selected_path: path },
close: async () => {
calls.push("close");
},
cancel: () => {
calls.push("cancel");
},
strings: async (input) => {
calls.push(input);
return { items: [], source: { evidence_id: "ev_real-owner" } };
},
});
return { create, calls };
};
test("path selectors agree in runtime and JSON Schema, without inferred target or string identities", () => {
const validate = new Ajv2020({ strict: false }).compile(
reference("path").methods[0].input_schema,
);
for (const [request, valid] of [
[{ from: 0, target: { kind: "exit" } }, true],
[
{
from: 0,
target: { kind: "block", id: 1 },
avoid: [2, 2],
offset: 1,
limit: 1,
},
true,
],
[{ from: "0", target: { kind: "exit" } }, false],
[{ from: 0, target: { kind: "block" } }, false],
[{ from: 0, target: { kind: "exit", id: 1 } }, false],
[{ from: 0, target: { kind: "exit" }, avoid: [-1] }, false],
[{ from: 0, target: { kind: "exit" }, limit: 129 }, false],
]) {
const input = { snapshot: "snap_test", ...request };
assert.equal(methods.path.input.safeParse(input).success, valid);
assert.equal(validate(input), valid, JSON.stringify(validate.errors));
}
});
test("relation identity and operand-only selectors agree in runtime and advertised schemas", () => {
const validate = new Ajv2020({ strict: false }).compile(
reference("relations").methods[0].input_schema,
);
for (const [request, valid] of [
[{ kind: "operands", seeds: ["0x1:0"], input_indices: [0, 155] }, true],
[{ kind: "operands", seeds: ["0x1:0"], input_indices: [] }, true],
[{ kind: "uses", seeds: ["0x1:0"] }, true],
[
{
kind: "cases",
seeds: ["0x1:0"],
case_values: ["0xffffffffffffffff"],
case_indices: [0, 155],
},
true,
],
[{ kind: "cases", seeds: [0] }, false],
[{ kind: "cases", seeds: ["0x1:0"], case_values: ["0X1"] }, false],
[{ kind: "incoming", seeds: [0] }, true],
[{ kind: "incoming", seeds: ["0"] }, false],
[{ kind: "operands", seeds: [0] }, false],
[{ kind: "uses", seeds: ["0x1:0"], input_indices: [0] }, false],
[{ kind: "operands", seeds: ["0x1:0"], input_indices: [-1] }, false],
]) {
const input = { snapshot: "snap_test", ...request };
assert.equal(methods.relations.input.safeParse(input).success, valid);
assert.equal(validate(input), valid, JSON.stringify(validate.errors));
}
});
test("named contracts reject extra keys and stale leases before provider admission", async () => {
const provider = factory();
const session = new AtomicSession(provider.create);
await session.open("first");
const lease = session.context.lease;
const invalid = await session.call("strings", { query: "x", guessed: true });
assert.equal(invalid.error.code, "invalid_input");
const wrongIdentity = await session.call("relations", {
snapshot: "snap_test",
kind: "incoming",
seeds: ["0"],
});
assert.equal(wrongIdentity.error.code, "invalid_input");
assert.equal(provider.calls.length, 0);
await session.open("second");
const stale = await session.call("strings", { query: "x" }, undefined, lease);
assert.equal(stale.error.code, "stale_context");
assert.deepEqual(provider.calls, ["close"]);
await session.close();
});
test("cancellation after a completed read retains observations under a cancelled outcome", async () => {
const controller = new AbortController();
const session = new AtomicSession(async () => ({
context: { lease: "late-cancel" },
close: async () => {},
strings: async () => {
controller.abort();
return {
source: { evidence_id: "ev_observed" },
items: [{ value: "retained" }],
};
},
}));
await session.open("target");
const result = await session.call(
"strings",
{ query: "x" },
controller.signal,
);
assert.equal(result.ok, false);
assert.equal(result.error.code, "cancelled");
assert.equal(
result.error.details.partial_observation.items[0].value,
"retained",
);
assert.equal(result.context.lease, "late-cancel");
await session.close();
});
test("close drains admitted reads and a failed cleanup retains ownership for retry", async () => {
let release;
let reads = 0;
let closes = 0;
const session = new AtomicSession(async () => ({
context: { lease: "owned" },
strings: () => {
reads++;
return new Promise((resolve) => {
release = resolve;
});
},
close: () => {
if (++closes === 1) throw new Error("cleanup failed");
},
cancel() {},
}));
await session.open("target");
const read = session.call("strings", { query: "x" });
await new Promise((resolve) => setImmediate(resolve));
const close = session.close();
assert.equal(closes, 0);
assert.equal(reads, 1);
release({ items: [] });
assert.equal((await read).ok, true);
await assert.rejects(close, /cleanup failed/u);
assert.equal(session.context.lease, "owned");
await session.close();
assert.equal(session.context, null);
});
for (const action of ["close", "replace"])
test(`failed open retains session file owners through ${action} and cleanup retry`, async () => {
let acquisitions = 0,
cleanupAttempts = 0,
releasable = false;
const primary = new Error(
"Engine inspection failed after acquiring a file",
);
const session = new AtomicSession(async (path, { fileResources }) => {
acquisitions++;
if (acquisitions === 1)
return fileResources.run(async () => {
fileResources.retain({
kind: "lease",
resource: "/selected/engine",
lease: {
release: async () => {
cleanupAttempts++;
if (!releasable) throw new Error("Descriptor remains open");
},
},
});
throw primary;
});
return {
context: { lease: "recovered", selected_path: path },
close: async () => {},
};
});
await assert.rejects(session.open("first"), (error) => error === primary);
assert.equal(session.context, null);
assert.equal(
(await session.call("strings", { query: "x" })).error.code,
"no_target",
);
const retry = () =>
action === "close" ? session.close() : session.open("second");
await assert.rejects(
retry(),
(error) =>
error.code === "cleanup_incomplete" &&
error.details.resources.length === 1 &&
error.details.resources[0] === "/selected/engine",
);
assert.equal(acquisitions, 1, "Unresolved owners block replacement");
assert.equal(cleanupAttempts, 1);
releasable = true;
assert.equal((await retry()).ok, true);
assert.equal(cleanupAttempts, 2);
if (action === "close") {
assert.equal(session.context, null);
assert.equal((await session.open("second")).ok, true);
}
assert.equal(acquisitions, 2);
assert.equal(session.context.lease, "recovered");
await session.close();
assert.equal(cleanupAttempts, 2, "Released owners are not retried");
});
test("cancellation before admission makes no provider query; advertised reference schemas compile", async () => {
const provider = factory();
const session = new AtomicSession(provider.create);
await session.open("target");
const stop = new AbortController();
stop.abort();
const cancelled = await session.call("strings", { query: "x" }, stop.signal);
assert.equal(cancelled.error.code, "cancelled");
assert.deepEqual(provider.calls, []);
const ajv = new Ajv2020({ strict: false });
for (const method of reference().methods) ajv.compile(method.input_schema);
await session.close();
});