Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
65 lines
3.1 KiB
JavaScript
65 lines
3.1 KiB
JavaScript
const test = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const { installRepairGate } = require("../memory/repair.js");
|
|
const { gateText } = require("../core/locale.js");
|
|
|
|
function gate({ answers, fail = false, resume = false }) {
|
|
let tool;
|
|
const calls = [], widgets = [];
|
|
const repair = { repair_id: "receipt", choice: null, status: "proposed", can_apply: true,
|
|
saved: false, indexed: false, options: [{ id: "fix", content: { detail: "Correction" } }] };
|
|
installRepairGate({ registerTool: def => { tool = def; } }, {
|
|
languageFor: () => "en",
|
|
gateText,
|
|
workflow: { activate() {}, phase: () => ({ id: "F4" }) },
|
|
memory: { execute: (_ctx, args) => {
|
|
calls.push(args);
|
|
if (args[0] === "repair-apply") {
|
|
const choice = args[args.indexOf("--choice") + 1];
|
|
if (choice === "reject") Object.assign(repair, { choice, status: "rejected" });
|
|
else Object.assign(repair, { choice, saved: true, indexed: !fail,
|
|
status: fail ? "pending_activation" : "active" });
|
|
fail = false;
|
|
}
|
|
return JSON.stringify(repair);
|
|
} },
|
|
waitForReviewer: async (_ctx, descriptor) => {
|
|
widgets.push(descriptor);
|
|
assert.ok(answers.length, "gate must not ask an unbounded extra question");
|
|
return answers.shift();
|
|
},
|
|
toTextResult: text => text,
|
|
});
|
|
return { calls, widgets, run: () => tool.execute("id", {
|
|
session: "s", ...(resume ? { repair_id: "receipt" } : { proposal: { reason: "conflict", options: [] } }),
|
|
}, null, null, {}) };
|
|
}
|
|
|
|
test("human choice applies once and the next widget reports activation", async () => {
|
|
const g = gate({ answers: [{ choices: ["fix"] }, { choices: ["continue"] }] });
|
|
assert.equal(JSON.parse(await g.run()).indexed, true);
|
|
assert.deepEqual(g.calls.map(args => args[0]), ["repair-prepare", "repair-apply"]);
|
|
assert.equal(g.widgets[0].repair.saved, false);
|
|
assert.equal(g.widgets[1].repair.status, "active");
|
|
});
|
|
test("rejection asks for reformulation without a normal workflow decision", async () => {
|
|
const g = gate({ answers: [{ choices: ["reject"] }] });
|
|
assert.match(await g.run(), /rejected as inadequate/);
|
|
assert.equal(g.calls.length, 2);
|
|
});
|
|
test("retry keeps the receipt and selected choice, then reports true activation", async () => {
|
|
const g = gate({ resume: true, fail: true,
|
|
answers: [{ choices: ["fix"] }, { choices: ["fix"] }, { choices: ["continue"] }] });
|
|
assert.equal(JSON.parse(await g.run()).indexed, true);
|
|
assert.equal(g.widgets[1].repair.status, "pending_activation");
|
|
assert.equal(g.calls[0][0], "repair-show");
|
|
assert.deepEqual(g.calls[1], g.calls[2]);
|
|
});
|
|
test("free text and forged choices never apply a correction", async () => {
|
|
for (const response of [{ control: "freetext", text: "Explain the conflict" }, { choices: ["forged"] }]) {
|
|
const g = gate({ answers: [response] });
|
|
await g.run();
|
|
assert.equal(g.calls.length, 1);
|
|
}
|
|
});
|