e2e: integration harness (69/69 green) + fix /agent/ws missing bearer auth (BUG-1)

This commit is contained in:
Raphael Westphal
2026-08-18 14:13:15 +02:00
parent 6ba718730e
commit ecb91e941c
17 changed files with 1129 additions and 2 deletions
+77
View File
@@ -0,0 +1,77 @@
// scenarios/resilience.mjs — daemon crash (SIGKILL of the go process group)
// and reboot on the SAME sqlite file: every persisted event survives, sessions
// come back listed offline, and seq continues past the persisted high-water.
import { agentClient, agentEvent, agentHello, rest, sessionSnapshot, sid, waitUntil } from "../lib.mjs";
export async function run(ctx) {
const { r, base, token, agentUrl, state } = ctx;
const id = sid("e2e-res");
const marker = `persist-me-${sid("m")}`;
const snapshot = sessionSnapshot(id);
const agent = await agentClient(agentUrl, token);
const w1 = await agentHello(agent, snapshot);
r.check("pre-restart handshake", w1?.type === "welcome");
agentEvent(agent, id, 1, "message_end", {
message: { role: "user", id: "mr1", text: marker, thinking: null, toolCalls: [], toolCallId: null },
});
agentEvent(agent, id, 2, "tool_execution_start", { toolCallId: "tc9", toolName: "bash", args: { command: "true" } });
agentEvent(agent, id, 3, "agent_settled", {});
const flushed = await waitUntil(
async () => (await rest(base, token, `/api/sessions/${id}/events?after=0`)).json?.length === 3,
);
r.check("events flushed to sqlite before crash", flushed);
agent.close();
let restarted = true;
let restartDetail = "";
try {
await ctx.restartDaemon();
} catch (err) {
restarted = false;
restartDetail = String(err?.message ?? err);
}
r.check("daemon restarted on same DB after SIGKILL", restarted, restartDetail);
const sessions = (await rest(base, token, "/api/sessions")).json ?? [];
const row = sessions.find((s) => s.id === id);
r.check(
"persisted session listed with online=false after restart",
row !== undefined && row.online === false,
JSON.stringify(row ?? null),
);
r.check(
"pre-restart sessions from other scenarios also listed offline",
sessions.some((s) => s.id === state.lifecycle?.id && s.online === false) &&
sessions.every((s) => s.online === false),
JSON.stringify(sessions.map((s) => [s.id, s.online])),
);
const events = (await rest(base, token, `/api/sessions/${id}/events?after=0`)).json ?? [];
r.check(
"all 3 events survive the crash",
JSON.stringify(events.map((e) => e.seq)) === "[1,2,3]",
JSON.stringify(events.map((e) => e.seq)),
);
r.check(
"event payload intact (message text marker)",
events[0]?.message?.text === marker,
JSON.stringify(events[0] ?? null),
);
const tail = (await rest(base, token, `/api/sessions/${id}/events?after=2`)).json ?? [];
r.check("after=2 still only seq 3 after restart", JSON.stringify(tail.map((e) => e.seq)) === "[3]");
// Reconnect after restart: the persisted high-water must be honoured.
const agent2 = await agentClient(agentUrl, token);
const w2 = await agentHello(agent2, snapshot);
r.check("post-restart welcome lastSeq=3", w2?.lastSeq === 3, `got ${w2?.lastSeq}`);
agentEvent(agent2, id, 4, "message_end", {
message: { role: "user", id: "mr2", text: "after restart", thinking: null, toolCalls: [], toolCallId: null },
});
const continued = await waitUntil(
async () => (await rest(base, token, `/api/sessions/${id}/events?after=3`)).json?.length === 1,
);
r.check("seq continues past persisted high-water after restart", continued);
agent2.close();
}