e2e: integration harness (69/69 green) + fix /agent/ws missing bearer auth (BUG-1)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// scenarios/agent-lifecycle.mjs — full plugin lifecycle against the real
|
||||
// daemon: hello→welcome, persisted events, online flag, events after disconnect.
|
||||
|
||||
import { agentClient, agentEvent, agentHello, rest, sessionSnapshot, sid, waitUntil } from "../lib.mjs";
|
||||
|
||||
export async function run(ctx) {
|
||||
const { r, base, token, agentUrl } = ctx;
|
||||
const id = sid("e2e-life");
|
||||
const snapshot = sessionSnapshot(id);
|
||||
|
||||
const agent = await agentClient(agentUrl, token);
|
||||
const welcome = await agentHello(agent, snapshot);
|
||||
r.check("hello → welcome received", welcome !== null && welcome.type === "welcome");
|
||||
r.check("welcome echoes sessionId", welcome?.sessionId === id, JSON.stringify(welcome ?? null));
|
||||
r.check("welcome lastSeq 0 on fresh session", welcome?.lastSeq === 0, `got ${welcome?.lastSeq}`);
|
||||
r.check("welcome envelope v=1", welcome?.v === 1);
|
||||
|
||||
agentEvent(agent, id, 1, "message_end", {
|
||||
message: { role: "user", id: "m1", text: "hello from e2e", thinking: null, toolCalls: [], toolCallId: null },
|
||||
});
|
||||
agentEvent(agent, id, 2, "tool_execution_start", { toolCallId: "tc1", toolName: "bash", args: { command: "ls" } });
|
||||
agentEvent(agent, id, 3, "tool_execution_end", { toolCallId: "tc1", toolName: "bash", isError: false, resultPreview: "file1\nfile2" });
|
||||
agentEvent(agent, id, 4, "agent_end", { usage: { inputTokens: 10, outputTokens: 5, totalCost: 0.25 } });
|
||||
|
||||
const listUrl = "/api/sessions";
|
||||
const online = await waitUntil(async () => {
|
||||
const res = await rest(base, token, listUrl);
|
||||
return res.json?.find((s) => s.id === id)?.online === true;
|
||||
});
|
||||
r.check("GET /api/sessions shows online=true while agent connected", online);
|
||||
|
||||
const listed = (await rest(base, token, listUrl)).json.find((s) => s.id === id);
|
||||
r.check(
|
||||
"session snapshot fields exposed",
|
||||
listed?.cwd === "/work/e2e" &&
|
||||
listed?.model === "glm-5.3" &&
|
||||
listed?.provider === "zai-renaud" &&
|
||||
listed?.agent === false &&
|
||||
listed?.repo === null &&
|
||||
typeof listed?.startedAt === "number" &&
|
||||
typeof listed?.lastEventAt === "number",
|
||||
JSON.stringify(listed ?? null),
|
||||
);
|
||||
|
||||
agent.close();
|
||||
const offline = await waitUntil(async () => {
|
||||
const res = await rest(base, token, listUrl);
|
||||
return res.json?.find((s) => s.id === id)?.online === false;
|
||||
});
|
||||
r.check("online=false after agent disconnect", offline);
|
||||
|
||||
const eventsUrl = `/api/sessions/${id}/events?after=0`;
|
||||
const persisted = await waitUntil(
|
||||
async () => (await rest(base, token, eventsUrl)).json?.length === 4,
|
||||
);
|
||||
r.check("all 4 persisted events served after=0", persisted);
|
||||
const ev = (await rest(base, token, eventsUrl)).json ?? [];
|
||||
r.check(
|
||||
"event seqs ascending [1,2,3,4]",
|
||||
JSON.stringify(ev.map((e) => e.seq)) === "[1,2,3,4]",
|
||||
JSON.stringify(ev.map((e) => e.seq)),
|
||||
);
|
||||
r.check(
|
||||
"event types in order",
|
||||
JSON.stringify(ev.map((e) => e.type)) ===
|
||||
JSON.stringify(["message_end", "tool_execution_start", "tool_execution_end", "agent_end"]),
|
||||
JSON.stringify(ev.map((e) => e.type)),
|
||||
);
|
||||
r.check(
|
||||
"persisted frames re-serialized with envelope",
|
||||
ev[0]?.v === 1 && ev[0]?.sessionId === id && typeof ev[0]?.ts === "number" && ev[0]?.message?.text === "hello from e2e",
|
||||
JSON.stringify(ev[0] ?? null),
|
||||
);
|
||||
const tail = (await rest(base, token, `/api/sessions/${id}/events?after=2`)).json ?? [];
|
||||
r.check("events?after=2 returns only seq≥3", JSON.stringify(tail.map((e) => e.seq)) === "[3,4]", JSON.stringify(tail.map((e) => e.seq)));
|
||||
|
||||
ctx.state.lifecycle = { id, snapshot, lastSeq: 4 };
|
||||
}
|
||||
Reference in New Issue
Block a user