test/claude-transport-lifecycle.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("node:fs");
var os = require("node:os");
var path = require("node:path");
var home = fs.mkdtempSync(path.join(os.tmpdir(), "clay-transport-"));
process.env.HOME = home;
process.env.CLAY_HOME = path.join(home, ".clay");
var attach = require("../lib/yoke/claude-worker-startup").attachWorkerStartup;
var kit = require("../lib/yoke/adapters/claude").contractTestKit;
test.after(function() { fs.rmSync(home, { recursive: true, force: true }); });
function startup() {
var timeout;
var cancelled = 0;
var killed = 0;
var worker = { kill: function() { killed++; } };
attach(worker, { setTimeout: function(fn, ms) { assert.equal(ms, 180000); timeout = fn; return 1; }, clearTimeout: function() { cancelled++; } });
return { worker: worker, timeout: function() { timeout(); }, cancelled: function() { return cancelled; }, killed: function() { return killed; } };
}
test("worker startup settles on readiness and cancels its deadline", async function() {
var fixture = startup();
fixture.worker._readyResolve();
await fixture.worker.readyPromise;
assert.equal(fixture.cancelled(), 1);
assert.equal(fixture.worker._readyReject, null);
});
test("worker startup exit/error rejects rather than leaving createQuery pending", async function() {
var fixture = startup();
fixture.worker._readyReject(new Error("Worker exited before ready"));
await assert.rejects(fixture.worker.readyPromise, /exited before ready/);
assert.equal(fixture.cancelled(), 1);
});
test("silent startup has a bounded deadline and terminates its worker", async function() {
var fixture = startup();
fixture.timeout();
await assert.rejects(fixture.worker.readyPromise, /startup deadline/);
assert.equal(fixture.killed(), 1);
});
test("in-process recovery waits for SDK termination, not close or cancellation request", async function() {
var finish;
var controller = new AbortController();
var raw = { close: function() {}, [Symbol.asyncIterator]: function() { return { next: function() { return new Promise(function(resolve) { finish = resolve; }); } }; } };
var handle = kit.createQueryHandle(raw, kit.createMessageQueue(), controller);
var next = handle[Symbol.asyncIterator]().next();
var settled = false;
var stopped = handle.stopAndWait().then(function() { settled = true; });
handle.close();
await Promise.resolve();
assert.equal(controller.signal.aborted, true);
assert.equal(settled, false);
finish({ done: true });
await next;
await stopped;
assert.equal(settled, true);
});
test("worker recovery waits for termination acknowledgement, ignoring local iterator closure", async function() {
var receive;
var resolveExit;
var timer;
var worker = { process: { killed: false, exitCode: null }, send: function() { return true; }, onMessage: function(fn) { receive = fn; }, exitPromise: new Promise(function(resolve) { resolveExit = resolve; }) };
var handle = kit.createWorkerQueryHandle(worker);
var settled = false;
var stopped = handle.stopAndWait().then(function() { settled = true; });
timer = worker._abortTimeout;
handle.close();
await Promise.resolve();
assert.equal(settled, false);
receive({ type: "query_error", error: "socket lost", _fallback: true });
await Promise.resolve();
assert.equal(settled, false);
resolveExit();
await stopped;
clearTimeout(timer);
assert.equal(settled, true);
});
test("worker query completion acknowledges safe stop without waiting for reusable process exit", async function() {
var receive;
var worker = { process: { killed: false, exitCode: null }, send: function() { return true; }, onMessage: function(fn) { receive = fn; }, exitPromise: new Promise(function() {}) };
var handle = kit.createWorkerQueryHandle(worker);
var stopped = handle.stopAndWait();
receive({ type: "query_done" });
await stopped;
assert.equal(worker._abortTimeout, null, "completed abort must not kill a reused worker later");
});
test("in-process rejected SDK iteration also confirms execution termination", async function() {
var fail;
var raw = { [Symbol.asyncIterator]: function() { return { next: function() { return new Promise(function(resolve, reject) { fail = reject; }); } }; } };
var handle = kit.createQueryHandle(raw, kit.createMessageQueue(), new AbortController());
var next = handle[Symbol.asyncIterator]().next();
var rejected = assert.rejects(next, /SDK stopped/);
var stopped = handle.stopAndWait();
fail(new Error("SDK stopped"));
await rejected;
await stopped;
});