test: reproduce workbench live backfill race
This commit is contained in:
@@ -33,6 +33,8 @@ interface ScenarioState {
|
||||
terminalScript: boolean;
|
||||
terminalFailureScript: boolean;
|
||||
staleTraceId: string | null;
|
||||
liveBackfillTraceId: string | null;
|
||||
liveBackfillReadyAtMs: number | null;
|
||||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
@@ -280,7 +282,9 @@ function createScenarioState(scenarioId: string): ScenarioState {
|
||||
chatDelayMs: id === "submit-authority-race" ? 1_000 : 0,
|
||||
terminalScript: id === "event-replay" || id === "running-to-terminal" || id === "stale-submit-restore",
|
||||
terminalFailureScript: id === "stale-submit-restore",
|
||||
staleTraceId
|
||||
staleTraceId,
|
||||
liveBackfillTraceId: null,
|
||||
liveBackfillReadyAtMs: null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -400,7 +404,9 @@ function acceptChatTurn(body: JsonRecord): JsonRecord {
|
||||
const session = sessionById(sessionId) ?? createManualSession({ sessionId });
|
||||
if (!state.sessions.includes(session)) state.sessions.unshift(session);
|
||||
const threadId = typeof body.threadId === "string" && body.threadId.trim() ? body.threadId : null;
|
||||
const traceId = typeof body.traceId === "string" && body.traceId.trim() ? body.traceId : `trc_${Date.now().toString(16)}`;
|
||||
const traceId = state.scenarioId === "live-rest-backfill-without-terminal-sse"
|
||||
? "trc_live_backfill"
|
||||
: typeof body.traceId === "string" && body.traceId.trim() ? body.traceId : `trc_${Date.now().toString(16)}`;
|
||||
const message = typeof body.message === "string" ? body.message : "";
|
||||
const now = new Date().toISOString();
|
||||
session.status = "running";
|
||||
@@ -413,10 +419,47 @@ function acceptChatTurn(body: JsonRecord): JsonRecord {
|
||||
{ id: `msg_${traceId}_user`, role: "user", title: "用户", text: message, status: "sent", createdAt: now, sessionId, threadId, traceId },
|
||||
{ id: `msg_${traceId}_agent`, role: "agent", title: "Code Agent", text: "", status: "running", createdAt: now, sessionId, threadId, traceId, runnerTrace: { traceId, status: "running", sessionId, threadId, events: [], eventCount: 0, fullTraceLoaded: false, hasMore: false } }
|
||||
];
|
||||
state.traces[traceId] = { traceId, status: "running", sessionId, threadId, events: [], eventCount: 0, fullTraceLoaded: false, hasMore: false };
|
||||
if (state.scenarioId === "live-rest-backfill-without-terminal-sse") {
|
||||
state.liveBackfillTraceId = traceId;
|
||||
state.liveBackfillReadyAtMs = Date.now() + 2_000;
|
||||
state.traces[traceId] = liveBackfillEarlyTrace(sessionId, threadId, traceId);
|
||||
} else {
|
||||
state.traces[traceId] = { traceId, status: "running", sessionId, threadId, events: [], eventCount: 0, fullTraceLoaded: false, hasMore: false };
|
||||
}
|
||||
return { status: "running", accepted: true, running: true, terminal: false, traceId, sessionId, threadId, turnId: traceId };
|
||||
}
|
||||
|
||||
function liveBackfillEarlyTrace(sessionId: string, threadId: string | null, traceId: string): JsonRecord {
|
||||
const createdAt = new Date().toISOString();
|
||||
return {
|
||||
traceId,
|
||||
status: "running",
|
||||
sessionId,
|
||||
threadId,
|
||||
events: [
|
||||
{ seq: 1, sourceSeq: 1, createdAt, label: "agentrun:request:accepted", type: "backend_status", status: "running" },
|
||||
{ seq: 2, sourceSeq: 2, createdAt, label: "agentrun:run:created", type: "backend_status", status: "running" },
|
||||
{ seq: 3, sourceSeq: 3, createdAt, label: "agentrun:command:created", type: "backend_status", status: "running" },
|
||||
{ seq: 4, sourceSeq: 4, createdAt, label: "agentrun:runner-job:created", type: "backend_status", status: "running" }
|
||||
],
|
||||
eventCount: 4,
|
||||
fullTraceLoaded: true,
|
||||
hasMore: false
|
||||
};
|
||||
}
|
||||
|
||||
function liveBackfillCompletedTrace(sessionId: string, threadId: string | null, traceId: string): JsonRecord {
|
||||
const createdAt = new Date().toISOString();
|
||||
const finalText = "fake AgentRun completed after REST backfill without terminal SSE.";
|
||||
const events = [
|
||||
...(liveBackfillEarlyTrace(sessionId, threadId, traceId).events as JsonRecord[]),
|
||||
{ seq: 5, sourceSeq: 21, createdAt, label: "agentrun:assistant:message", type: "assistant_message", status: "completed", message: finalText },
|
||||
{ seq: 6, sourceSeq: 22, createdAt, label: "item/commandExecution:completed", type: "commandExecution", status: "completed", stdout: "OK\n" },
|
||||
{ seq: 7, sourceSeq: 23, createdAt, label: "agentrun:backend:turn/completed", type: "backend_status", status: "completed", terminal: true }
|
||||
];
|
||||
return { traceId, status: "completed", sessionId, threadId, events, eventCount: events.length, fullTraceLoaded: true, hasMore: false, finalResponse: { text: finalText, status: "completed" } };
|
||||
}
|
||||
|
||||
function sessionSummary(session: SessionRecord): SessionRecord {
|
||||
const { messages: _messages, hidden: _hidden, ...rest } = session;
|
||||
return rest as SessionRecord;
|
||||
@@ -464,6 +507,13 @@ function sessionPayload(sessionId: string): JsonRecord {
|
||||
}
|
||||
|
||||
function turnPayload(traceId: string): JsonRecord {
|
||||
if (state.scenarioId === "live-rest-backfill-without-terminal-sse" && traceId === state.liveBackfillTraceId) {
|
||||
const session = state.sessions.find((item) => item.lastTraceId === traceId) ?? state.sessions[0];
|
||||
const sessionId = session?.sessionId ?? state.selectedSessionId;
|
||||
const threadId = typeof session?.threadId === "string" ? session.threadId : null;
|
||||
if (state.liveBackfillReadyAtMs && Date.now() >= state.liveBackfillReadyAtMs) return liveBackfillCompletedTrace(sessionId, threadId, traceId);
|
||||
return liveBackfillEarlyTrace(sessionId, threadId, traceId);
|
||||
}
|
||||
const trace = state.traces[traceId] ?? { traceId, status: "unknown", events: [] };
|
||||
if (state.scenarioId === "terminal-turn-stale-session-active" && traceId === "trc_completed") return { ...trace, traceId, status: "active", running: true, terminal: false, trace: { status: "completed", eventCount: trace.eventCount ?? 0 } };
|
||||
const status = String(trace.status ?? "unknown");
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { expect, gotoWorkbench, saveScreenshot, test } from "../fixtures/test";
|
||||
import { selectors, sessionTab } from "../fixtures/selectors";
|
||||
|
||||
test.describe("live REST backfill without terminal SSE", () => {
|
||||
test.use({ scenarioId: "live-rest-backfill-without-terminal-sse" });
|
||||
|
||||
test("submitted turn catches completed AgentRun projection without switching sessions", async ({ page }, testInfo) => {
|
||||
await gotoWorkbench(page, "/workbench/sessions/ses_completed");
|
||||
|
||||
await page.locator(selectors.commandInput).fill("p0 1189 live backfill race: reply OK and run one command");
|
||||
await expect(page.locator(selectors.commandSend)).toBeEnabled();
|
||||
await page.locator(selectors.commandSend).click();
|
||||
|
||||
await expect(page.locator(sessionTab("ses_completed"))).toHaveAttribute("data-active", "true");
|
||||
await expect(page.locator(`${selectors.messageCard}[data-role="agent"][data-status="running"]`).last()).toBeVisible();
|
||||
await saveScreenshot(page, testInfo, "live-backfill-submitted");
|
||||
|
||||
await expect.poll(async () => {
|
||||
const response = await page.request.get("/v1/workbench/turns/trc_live_backfill");
|
||||
return response.ok() ? ((await response.json()).turn?.status as string | undefined) : undefined;
|
||||
}).toBe("completed");
|
||||
|
||||
await saveScreenshot(page, testInfo, "live-backfill-canonical-completed");
|
||||
await expect(page.locator(sessionTab("ses_completed"))).toHaveAttribute("data-active", "true");
|
||||
const backfilledFinal = page.locator(`${selectors.messageCard}[data-role="agent"]`).filter({ hasText: "fake AgentRun completed after REST backfill" });
|
||||
await expect(backfilledFinal).toHaveAttribute("data-status", "completed");
|
||||
await expect(page.locator(`${selectors.traceTimeline}[data-status="completed"]`).last()).toBeVisible();
|
||||
await expect(page.locator(selectors.commandSend)).toHaveAttribute("data-action", "turn");
|
||||
await saveScreenshot(page, testInfo, "live-backfill-ui-completed");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user