154 lines
5.3 KiB
TypeScript
154 lines
5.3 KiB
TypeScript
// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-19-p1-agentrun-incremental-cursor.
|
|
// Responsibility: Independent backend unit tests for AgentRun projection cursor planning.
|
|
|
|
import assert from "node:assert/strict";
|
|
import { test } from "bun:test";
|
|
|
|
import { buildAgentRunProjectionEventsFetchPlan, buildWorkbenchProjectionStateUpdate } from "./workbench-projection-cursor.ts";
|
|
|
|
test("AgentRun projection fetch plan uses durable cursor without scanning long trace", () => {
|
|
const poisonTrace = {};
|
|
Object.defineProperty(poisonTrace, "events", {
|
|
get() {
|
|
throw new Error("trace events must not be read to compute AgentRun afterSeq");
|
|
}
|
|
});
|
|
const poisonResult = {};
|
|
Object.defineProperty(poisonResult, "events", {
|
|
get() {
|
|
throw new Error("result events must not be read to compute AgentRun afterSeq");
|
|
}
|
|
});
|
|
|
|
const plan = buildAgentRunProjectionEventsFetchPlan({
|
|
projectionState: {
|
|
traceId: "trc_cursor_o1",
|
|
sourceRunId: "run_cursor_o1",
|
|
sourceCommandId: "cmd_cursor_o1",
|
|
lastSourceSeq: 3700,
|
|
lastProjectedSeq: 129
|
|
},
|
|
agentRun: {
|
|
runId: "run_cursor_o1",
|
|
commandId: "cmd_cursor_o1",
|
|
lastSeq: 12
|
|
},
|
|
trace: poisonTrace,
|
|
result: poisonResult,
|
|
pageLimit: 500
|
|
});
|
|
|
|
assert.equal(plan.runId, "run_cursor_o1");
|
|
assert.equal(plan.commandId, "cmd_cursor_o1");
|
|
assert.equal(plan.afterSeq, 3700);
|
|
assert.equal(plan.limit, 500);
|
|
assert.equal(plan.cursorSource, "projection-state");
|
|
});
|
|
|
|
test("AgentRun projection state advances from incremental events response", () => {
|
|
const state = buildWorkbenchProjectionStateUpdate({
|
|
currentState: {
|
|
traceId: "trc_cursor_update",
|
|
sourceRunId: "run_cursor_update",
|
|
sourceCommandId: "cmd_cursor_update",
|
|
lastSourceSeq: 3700,
|
|
lastProjectedSeq: 3700,
|
|
sourceLatestSeq: 3700,
|
|
projectionStatus: "projecting",
|
|
resultSyncState: "not_started",
|
|
createdAt: "2026-06-19T12:00:00.000Z"
|
|
},
|
|
agentRun: {
|
|
runId: "run_cursor_update",
|
|
commandId: "cmd_cursor_update",
|
|
lastSeq: 3700
|
|
},
|
|
eventsResponse: {
|
|
traceLastSeq: 3720,
|
|
maxSeq: 3721,
|
|
rawEventCount: 20
|
|
},
|
|
now: () => "2026-06-19T12:01:00.000Z"
|
|
});
|
|
|
|
assert.equal(state.lastSourceSeq, 3720);
|
|
assert.equal(state.lastAgentRunSeq, 3720);
|
|
assert.equal(state.lastProjectedSeq, 3720);
|
|
assert.equal(state.sourceLatestSeq, 3721);
|
|
assert.equal(state.projectionStatus, "projecting");
|
|
assert.equal(state.resultSyncState, "not_started");
|
|
assert.equal(state.createdAt, "2026-06-19T12:00:00.000Z");
|
|
assert.equal(state.updatedAt, "2026-06-19T12:01:00.000Z");
|
|
});
|
|
|
|
test("AgentRun projection state preserves retry params for background resume", () => {
|
|
const state = buildWorkbenchProjectionStateUpdate({
|
|
currentState: {
|
|
traceId: "trc_cursor_retry_params",
|
|
sourceRunId: "run_cursor_retry_params",
|
|
sourceCommandId: "cmd_cursor_retry_params",
|
|
retryParams: {
|
|
message: "preserve this prompt for fresh-session retry",
|
|
sessionId: "ses_cursor_retry",
|
|
providerProfile: "dsflash-go",
|
|
valuesPrinted: false
|
|
},
|
|
createdAt: "2026-06-19T12:00:00.000Z"
|
|
},
|
|
agentRun: {
|
|
runId: "run_cursor_retry_params",
|
|
commandId: "cmd_cursor_retry_params",
|
|
lastSeq: 3
|
|
},
|
|
now: () => "2026-06-19T12:01:00.000Z"
|
|
});
|
|
|
|
assert.equal(state.retryParams.message, "preserve this prompt for fresh-session retry");
|
|
assert.equal(state.retryParams.prompt, "preserve this prompt for fresh-session retry");
|
|
assert.equal(state.retryParams.sessionId, "ses_cursor_retry");
|
|
assert.equal(state.retryParams.providerProfile, "dsflash-go");
|
|
assert.equal(state.retryParams.valuesPrinted, false);
|
|
});
|
|
|
|
test("AgentRun projection state resets source cursor when fresh retry switches run", () => {
|
|
const state = buildWorkbenchProjectionStateUpdate({
|
|
currentState: {
|
|
traceId: "trc_cursor_fresh_retry",
|
|
sourceRunId: "run_cursor_failed",
|
|
sourceCommandId: "cmd_cursor_failed",
|
|
lastSourceSeq: 3700,
|
|
lastAgentRunSeq: 3700,
|
|
lastProjectedSeq: 3700,
|
|
sourceLatestSeq: 3700,
|
|
projectionStatus: "terminal",
|
|
resultSyncState: "pending",
|
|
retryParams: {
|
|
message: "retry on a fresh AgentRun session",
|
|
sessionId: "ses_cursor_fresh_retry",
|
|
providerProfile: "dsflash-go",
|
|
valuesPrinted: false
|
|
},
|
|
createdAt: "2026-06-19T12:00:00.000Z"
|
|
},
|
|
agentRun: {
|
|
runId: "run_cursor_fresh_retry",
|
|
commandId: "cmd_cursor_fresh_retry",
|
|
lastSeq: 0
|
|
},
|
|
projectionStatus: "projecting",
|
|
resultSyncState: null,
|
|
now: () => "2026-06-19T12:02:00.000Z"
|
|
});
|
|
|
|
assert.equal(state.sourceRunId, "run_cursor_fresh_retry");
|
|
assert.equal(state.sourceCommandId, "cmd_cursor_fresh_retry");
|
|
assert.equal(state.lastSourceSeq, 0);
|
|
assert.equal(state.lastAgentRunSeq, 0);
|
|
assert.equal(state.lastProjectedSeq, 0);
|
|
assert.equal(state.sourceLatestSeq, 0);
|
|
assert.equal(state.projectionStatus, "projecting");
|
|
assert.equal(state.retryParams.message, "retry on a fresh AgentRun session");
|
|
const plan = buildAgentRunProjectionEventsFetchPlan({ projectionState: state, agentRun: { runId: "run_cursor_fresh_retry", commandId: "cmd_cursor_fresh_retry", lastSeq: 0 } });
|
|
assert.equal(plan.afterSeq, 0);
|
|
});
|