test(agents): dedupe agent and cron test scaffolds

This commit is contained in:
Peter Steinberger
2026-03-02 06:40:42 +00:00
parent 281494ae52
commit 7e29d604ba
38 changed files with 3114 additions and 4486 deletions

View File

@@ -84,6 +84,8 @@ vi.mock("./subagent-registry.store.js", () => ({
describe("subagent registry steer restarts", () => {
let mod: typeof import("./subagent-registry.js");
type RegisterSubagentRunInput = Parameters<typeof mod.registerSubagentRun>[0];
const MAIN_REQUESTER_SESSION_KEY = "agent:main:main";
const MAIN_REQUESTER_DISPLAY_KEY = "main";
beforeAll(async () => {
mod = await import("./subagent-registry.js");
@@ -135,23 +137,65 @@ describe("subagent registry steer restarts", () => {
task: string,
options: Partial<Pick<RegisterSubagentRunInput, "spawnMode">> = {},
): void => {
mod.registerSubagentRun({
registerRun({
runId,
childSessionKey,
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task,
expectsCompletionMessage: true,
requesterOrigin: {
channel: "discord",
to: "channel:123",
accountId: "work",
},
task,
cleanup: "keep",
expectsCompletionMessage: true,
...options,
});
};
const registerRun = (
params: {
runId: string;
childSessionKey: string;
task: string;
requesterSessionKey?: string;
requesterDisplayKey?: string;
} & Partial<
Pick<RegisterSubagentRunInput, "spawnMode" | "requesterOrigin" | "expectsCompletionMessage">
>,
): void => {
mod.registerSubagentRun({
runId: params.runId,
childSessionKey: params.childSessionKey,
requesterSessionKey: params.requesterSessionKey ?? MAIN_REQUESTER_SESSION_KEY,
requesterDisplayKey: params.requesterDisplayKey ?? MAIN_REQUESTER_DISPLAY_KEY,
requesterOrigin: params.requesterOrigin,
task: params.task,
cleanup: "keep",
spawnMode: params.spawnMode,
expectsCompletionMessage: params.expectsCompletionMessage,
});
};
const listMainRuns = () => mod.listSubagentRunsForRequester(MAIN_REQUESTER_SESSION_KEY);
const emitLifecycleEnd = (
runId: string,
data: {
startedAt?: number;
endedAt?: number;
aborted?: boolean;
error?: string;
} = {},
) => {
lifecycleHandler?.({
stream: "lifecycle",
runId,
data: {
phase: "end",
...data,
},
});
};
afterEach(async () => {
announceSpy.mockClear();
announceSpy.mockResolvedValue(true);
@@ -161,26 +205,19 @@ describe("subagent registry steer restarts", () => {
});
it("suppresses announce for interrupted runs and only announces the replacement run", async () => {
mod.registerSubagentRun({
registerRun({
runId: "run-old",
childSessionKey: "agent:main:subagent:steer",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "initial task",
cleanup: "keep",
});
const previous = mod.listSubagentRunsForRequester("agent:main:main")[0];
const previous = listMainRuns()[0];
expect(previous?.runId).toBe("run-old");
const marked = mod.markSubagentRunForSteerRestart("run-old");
expect(marked).toBe(true);
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-old",
data: { phase: "end" },
});
emitLifecycleEnd("run-old");
await flushAnnounce();
expect(announceSpy).not.toHaveBeenCalled();
@@ -193,15 +230,11 @@ describe("subagent registry steer restarts", () => {
});
expect(replaced).toBe(true);
const runs = mod.listSubagentRunsForRequester("agent:main:main");
const runs = listMainRuns();
expect(runs).toHaveLength(1);
expect(runs[0].runId).toBe("run-new");
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-new",
data: { phase: "end" },
});
emitLifecycleEnd("run-new");
await flushAnnounce();
expect(announceSpy).toHaveBeenCalledTimes(1);
@@ -228,11 +261,7 @@ describe("subagent registry steer restarts", () => {
"completion-mode task",
);
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-completion-delayed",
data: { phase: "end" },
});
emitLifecycleEnd("run-completion-delayed");
await flushAnnounce();
expect(runSubagentEndedHookMock).not.toHaveBeenCalled();
@@ -249,7 +278,7 @@ describe("subagent registry steer restarts", () => {
}),
expect.objectContaining({
runId: "run-completion-delayed",
requesterSessionKey: "agent:main:main",
requesterSessionKey: MAIN_REQUESTER_SESSION_KEY,
}),
);
});
@@ -265,11 +294,7 @@ describe("subagent registry steer restarts", () => {
{ spawnMode: "session" },
);
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-persistent-session",
data: { phase: "end" },
});
emitLifecycleEnd("run-persistent-session");
await flushAnnounce();
expect(runSubagentEndedHookMock).not.toHaveBeenCalled();
@@ -278,7 +303,7 @@ describe("subagent registry steer restarts", () => {
await flushAnnounce();
expect(runSubagentEndedHookMock).not.toHaveBeenCalled();
const run = mod.listSubagentRunsForRequester("agent:main:main")[0];
const run = listMainRuns()[0];
expect(run?.runId).toBe("run-persistent-session");
expect(run?.cleanupCompletedAt).toBeTypeOf("number");
expect(run?.endedHookEmittedAt).toBeUndefined();
@@ -286,16 +311,13 @@ describe("subagent registry steer restarts", () => {
});
it("clears announce retry state when replacing after steer restart", () => {
mod.registerSubagentRun({
registerRun({
runId: "run-retry-reset-old",
childSessionKey: "agent:main:subagent:retry-reset",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "retry reset",
cleanup: "keep",
});
const previous = mod.listSubagentRunsForRequester("agent:main:main")[0];
const previous = listMainRuns()[0];
expect(previous?.runId).toBe("run-retry-reset-old");
if (previous) {
previous.announceRetryCount = 2;
@@ -309,7 +331,7 @@ describe("subagent registry steer restarts", () => {
});
expect(replaced).toBe(true);
const runs = mod.listSubagentRunsForRequester("agent:main:main");
const runs = listMainRuns();
expect(runs).toHaveLength(1);
expect(runs[0].runId).toBe("run-retry-reset-new");
expect(runs[0].announceRetryCount).toBeUndefined();
@@ -317,16 +339,13 @@ describe("subagent registry steer restarts", () => {
});
it("clears terminal lifecycle state when replacing after steer restart", async () => {
mod.registerSubagentRun({
registerRun({
runId: "run-terminal-state-old",
childSessionKey: "agent:main:subagent:terminal-state",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "terminal state",
cleanup: "keep",
});
const previous = mod.listSubagentRunsForRequester("agent:main:main")[0];
const previous = listMainRuns()[0];
expect(previous?.runId).toBe("run-terminal-state-old");
if (previous) {
previous.endedHookEmittedAt = Date.now();
@@ -342,17 +361,13 @@ describe("subagent registry steer restarts", () => {
});
expect(replaced).toBe(true);
const runs = mod.listSubagentRunsForRequester("agent:main:main");
const runs = listMainRuns();
expect(runs).toHaveLength(1);
expect(runs[0].runId).toBe("run-terminal-state-new");
expect(runs[0].endedHookEmittedAt).toBeUndefined();
expect(runs[0].endedReason).toBeUndefined();
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-terminal-state-new",
data: { phase: "end" },
});
emitLifecycleEnd("run-terminal-state-new");
await flushAnnounce();
expect(runSubagentEndedHookMock).toHaveBeenCalledTimes(1);
@@ -367,22 +382,15 @@ describe("subagent registry steer restarts", () => {
});
it("restores announce for a finished run when steer replacement dispatch fails", async () => {
mod.registerSubagentRun({
registerRun({
runId: "run-failed-restart",
childSessionKey: "agent:main:subagent:failed-restart",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "initial task",
cleanup: "keep",
});
expect(mod.markSubagentRunForSteerRestart("run-failed-restart")).toBe(true);
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-failed-restart",
data: { phase: "end" },
});
emitLifecycleEnd("run-failed-restart");
await flushAnnounce();
expect(announceSpy).not.toHaveBeenCalled();
@@ -398,13 +406,10 @@ describe("subagent registry steer restarts", () => {
it("marks killed runs terminated and inactive", async () => {
const childSessionKey = "agent:main:subagent:killed";
mod.registerSubagentRun({
registerRun({
runId: "run-killed",
childSessionKey,
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "kill me",
cleanup: "keep",
});
expect(mod.isSubagentSessionRunActive(childSessionKey)).toBe(true);
@@ -415,7 +420,7 @@ describe("subagent registry steer restarts", () => {
expect(updated).toBe(1);
expect(mod.isSubagentSessionRunActive(childSessionKey)).toBe(false);
const run = mod.listSubagentRunsForRequester("agent:main:main")[0];
const run = listMainRuns()[0];
expect(run?.outcome).toEqual({ status: "error", error: "manual kill" });
expect(run?.cleanupHandled).toBe(true);
expect(typeof run?.cleanupCompletedAt).toBe("number");
@@ -434,7 +439,7 @@ describe("subagent registry steer restarts", () => {
{
runId: "run-killed",
childSessionKey,
requesterSessionKey: "agent:main:main",
requesterSessionKey: MAIN_REQUESTER_SESSION_KEY,
},
);
});
@@ -450,35 +455,23 @@ describe("subagent registry steer restarts", () => {
return true;
});
mod.registerSubagentRun({
registerRun({
runId: "run-parent",
childSessionKey: "agent:main:subagent:parent",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "parent task",
cleanup: "keep",
});
mod.registerSubagentRun({
registerRun({
runId: "run-child",
childSessionKey: "agent:main:subagent:parent:subagent:child",
requesterSessionKey: "agent:main:subagent:parent",
requesterDisplayKey: "parent",
task: "child task",
cleanup: "keep",
});
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-parent",
data: { phase: "end" },
});
emitLifecycleEnd("run-parent");
await flushAnnounce();
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-child",
data: { phase: "end" },
});
emitLifecycleEnd("run-child");
await flushAnnounce();
const childRunIds = announceSpy.mock.calls.map(
@@ -494,43 +487,33 @@ describe("subagent registry steer restarts", () => {
try {
announceSpy.mockResolvedValue(false);
mod.registerSubagentRun({
runId: "run-completion-retry",
childSessionKey: "agent:main:subagent:completion",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "completion retry",
cleanup: "keep",
expectsCompletionMessage: true,
});
registerCompletionModeRun(
"run-completion-retry",
"agent:main:subagent:completion",
"completion retry",
);
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-completion-retry",
data: { phase: "end" },
});
emitLifecycleEnd("run-completion-retry");
await vi.advanceTimersByTimeAsync(0);
expect(announceSpy).toHaveBeenCalledTimes(1);
expect(mod.listSubagentRunsForRequester("agent:main:main")[0]?.announceRetryCount).toBe(1);
expect(listMainRuns()[0]?.announceRetryCount).toBe(1);
await vi.advanceTimersByTimeAsync(999);
expect(announceSpy).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(1);
expect(announceSpy).toHaveBeenCalledTimes(2);
expect(mod.listSubagentRunsForRequester("agent:main:main")[0]?.announceRetryCount).toBe(2);
expect(listMainRuns()[0]?.announceRetryCount).toBe(2);
await vi.advanceTimersByTimeAsync(1_999);
expect(announceSpy).toHaveBeenCalledTimes(2);
await vi.advanceTimersByTimeAsync(1);
expect(announceSpy).toHaveBeenCalledTimes(3);
expect(mod.listSubagentRunsForRequester("agent:main:main")[0]?.announceRetryCount).toBe(3);
expect(listMainRuns()[0]?.announceRetryCount).toBe(3);
await vi.advanceTimersByTimeAsync(4_001);
expect(announceSpy).toHaveBeenCalledTimes(3);
expect(
mod.listSubagentRunsForRequester("agent:main:main")[0]?.cleanupCompletedAt,
).toBeTypeOf("number");
expect(listMainRuns()[0]?.cleanupCompletedAt).toBeTypeOf("number");
} finally {
vi.useRealTimers();
}
@@ -540,32 +523,22 @@ describe("subagent registry steer restarts", () => {
it("keeps completion cleanup pending while descendants are still active", async () => {
announceSpy.mockResolvedValue(false);
mod.registerSubagentRun({
runId: "run-parent-expiry",
childSessionKey: "agent:main:subagent:parent-expiry",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
task: "parent completion expiry",
cleanup: "keep",
expectsCompletionMessage: true,
});
mod.registerSubagentRun({
registerCompletionModeRun(
"run-parent-expiry",
"agent:main:subagent:parent-expiry",
"parent completion expiry",
);
registerRun({
runId: "run-child-active",
childSessionKey: "agent:main:subagent:parent-expiry:subagent:child-active",
requesterSessionKey: "agent:main:subagent:parent-expiry",
requesterDisplayKey: "parent-expiry",
task: "child still running",
cleanup: "keep",
});
lifecycleHandler?.({
stream: "lifecycle",
runId: "run-parent-expiry",
data: {
phase: "end",
startedAt: Date.now() - 7 * 60_000,
endedAt: Date.now() - 6 * 60_000,
},
emitLifecycleEnd("run-parent-expiry", {
startedAt: Date.now() - 7 * 60_000,
endedAt: Date.now() - 6 * 60_000,
});
await flushAnnounce();
@@ -576,7 +549,7 @@ describe("subagent registry steer restarts", () => {
});
expect(parentHookCall).toBeUndefined();
const parent = mod
.listSubagentRunsForRequester("agent:main:main")
.listSubagentRunsForRequester(MAIN_REQUESTER_SESSION_KEY)
.find((entry) => entry.runId === "run-parent-expiry");
expect(parent?.cleanupCompletedAt).toBeUndefined();
expect(parent?.cleanupHandled).toBe(false);