test/loop-legacy-owner-boundary.test.js
Rama main · réplica de lectura del repositorio original.
var test = require("node:test");
var assert = require("node:assert/strict");
var fs = require("fs");
var os = require("os");
var path = require("path");
var execFileSync = require("node:child_process").execFileSync;
var attachLoop = require("../lib/project-loop").attachLoop;
function fixture(t, options) {
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "clay-legacy-owner-boundary-"));
execFileSync("git", ["init", "-q"], { cwd: dir });
execFileSync("git", ["-c", "user.name=Clay Test", "-c", "user.email=test@example.invalid", "commit", "--allow-empty", "-qm", "chore: initialize test fixture"], { cwd: dir });
var loopId = "legacy-owner-loop"; var loopDir = path.join(dir, ".claude", "loops", loopId);
fs.mkdirSync(loopDir, { recursive: true }); fs.writeFileSync(path.join(loopDir, "PROMPT.md"), "Implement safely.\n"); fs.writeFileSync(path.join(loopDir, "JUDGE.md"), "Verify safely.\n");
var sessions = new Map(); var nextId = 1; var calls = []; var events = []; var allowed = true; var linuxAvailable = true;
var sm = {
sessions: sessions, setResolveLoopInfo: function () {}, saveSessionFile: function () {}, broadcastSessionList: function () {}, appendToSessionFile: function () {},
createSession: function (input) { var session = { localId: nextId++, ownerId: input.ownerId || null, history: [] }; sessions.set(session.localId, session); return session; },
};
var loop = attachLoop({
cwd: dir, slug: "legacy-boundary", sm: sm, loopRegistryPath: path.join(dir, "registry.jsonl"), loopStatePath: path.join(dir, "loop-state.json"),
sdk: { startQuery: function (session, prompt, images, linuxUser) {
calls.push({ role: session.loop.role, ownerId: session.ownerId, linuxUser: linuxUser });
if (session.loop.role === "coder" && options.revokeAfterCoder) { allowed = false; session.history.push({ type: "delta", text: "coder complete" }); session.onQueryComplete(session); }
return Promise.resolve();
} },
send: function (message) { events.push(message); }, sendTo: function () {}, sendToSession: function () {}, getHubSchedules: function () { return []; },
getLinuxUserForSession: function () { return linuxAvailable ? "alice-linux" : null; }, onProcessingChanged: function () {}, hydrateImageRefs: function (value) { return value; },
isMultiUser: function () { return true; }, requiresLinuxUser: function () { return true; }, authorizeScheduledRun: function () { return allowed; },
});
loop.stopTimer();
var record = loop.loopRegistry.register({ id: loopId, name: "Legacy owner", task: "Run", cron: "0 9 * * *", ownerId: "alice", maxIterations: 2 });
loop.loopState.loopId = record.id; loop.loopState.loopFilesId = record.id; loop.loopState.wizardData = { loopMode: "judge", name: record.name };
if (options.missingLinuxAtStart) linuxAvailable = false;
t.after(function () { loop.stopTimer(); fs.rmSync(dir, { recursive: true, force: true }); });
return { loop: loop, calls: calls, events: events };
}
test("legacy coder and judge starts recheck live owner authorization and OS identity", function (t) {
var revoked = fixture(t, { revokeAfterCoder: true }); revoked.loop.startLoop({ maxIterations: 2, name: "Legacy owner" });
assert.deepEqual(revoked.calls.map(function (call) { return call.role; }), ["coder"]); assert.equal(revoked.calls[0].ownerId, "alice"); assert.equal(revoked.calls[0].linuxUser, "alice-linux");
assert.equal(revoked.loop.loopState.active, false); assert.equal(revoked.events.some(function (event) { return event.type === "loop_error" && /no longer authorized/.test(event.text); }), true);
var missingIdentity = fixture(t, { missingLinuxAtStart: true }); missingIdentity.loop.startLoop({ maxIterations: 2, name: "Legacy owner" });
assert.equal(missingIdentity.calls.length, 0); assert.equal(missingIdentity.loop.loopState.active, false);
assert.equal(missingIdentity.events.some(function (event) { return event.type === "loop_error" && /valid OS identity/.test(event.text); }), true);
});