diff --git a/src/cron/service/timer.regression.test.ts b/src/cron/service/timer.regression.test.ts index c52535ccd17..11b31d5df19 100644 --- a/src/cron/service/timer.regression.test.ts +++ b/src/cron/service/timer.regression.test.ts @@ -49,6 +49,19 @@ function requireTimestamp(value: number | undefined, label: string): number { return value; } +function requireRecord(value: unknown): Record { + expect(value).toBeTruthy(); + expect(typeof value).toBe("object"); + expect(Array.isArray(value)).toBe(false); + return value as Record; +} + +function firstMockArg(mock: unknown): unknown { + const calls = (mock as { mock: { calls: readonly (readonly unknown[])[] } }).mock.calls; + expect(calls[0]).toBeTruthy(); + return calls[0]?.[0]; +} + describe("cron service timer regressions", () => { it("caps timer delay to 60s for far-future schedules", async () => { const timeoutSpy = vi.spyOn(globalThis, "setTimeout"); @@ -101,7 +114,7 @@ describe("cron service timer regressions", () => { await onTimer(state); expect(timeoutSpy).toHaveBeenCalled(); - expect(state.timer).toEqual(expect.anything()); + expect(state.timer).toBeTruthy(); const delays = timeoutSpy.mock.calls .map(([, delay]) => delay) .filter((d): d is number => typeof d === "number"); @@ -1124,24 +1137,26 @@ describe("cron service timer regressions", () => { await onTimer(state); expect(state.store?.jobs).toStrictEqual([]); - expect(log.warn).not.toHaveBeenCalledWith( - expect.anything(), - "cron: applyOutcomeToStoredJob — job not found after forceReload, result discarded", - ); + expect( + log.warn.mock.calls.some( + ([, message]) => + message === + "cron: applyOutcomeToStoredJob — job not found after forceReload, result discarded", + ), + ).toBe(false); expect(log.info).toHaveBeenCalledWith( { jobId: selfRemovingJob.id }, "cron: finalized successful run after job was removed during execution", ); - expect(events).toContainEqual( - expect.objectContaining({ - jobId: selfRemovingJob.id, - action: "finished", - status: "ok", - summary: `finished ${selfRemovingJob.id}`, - delivered: true, - deliveryStatus: "delivered", - }), + const event = events.find( + (candidate) => candidate.jobId === selfRemovingJob.id && candidate.action === "finished", ); + expect(event).toBeTruthy(); + expect(event?.action).toBe("finished"); + expect(event?.status).toBe("ok"); + expect(event?.summary).toBe(`finished ${selfRemovingJob.id}`); + expect(event?.delivered).toBe(true); + expect(event?.deliveryStatus).toBe("delivered"); }); it("keeps missing-job discard semantics for failed isolated outcomes", async () => { @@ -1335,15 +1350,15 @@ describe("cron service timer regressions", () => { await timerPromise; expect(abortObserved).toBe(true); - expect(cleanupTimedOutAgentRun).toHaveBeenCalledWith({ - job: expect.objectContaining({ id: "timeout-cleanup-stuck-run" }), - timeoutMs: 1_000, - execution: { - jobId: "timeout-cleanup-stuck-run", - agentId: "main", - sessionId: "cron-run-session", - sessionKey: "agent:main:cron:timeout-cleanup-stuck-run:run:cron-run-session", - }, + expect(cleanupTimedOutAgentRun).toHaveBeenCalledTimes(1); + const cleanupArgs = requireRecord(firstMockArg(cleanupTimedOutAgentRun)); + expect(requireRecord(cleanupArgs.job).id).toBe("timeout-cleanup-stuck-run"); + expect(cleanupArgs.timeoutMs).toBe(1_000); + expect(cleanupArgs.execution).toEqual({ + jobId: "timeout-cleanup-stuck-run", + agentId: "main", + sessionId: "cron-run-session", + sessionKey: "agent:main:cron:timeout-cleanup-stuck-run:run:cron-run-session", }); const job = state.store?.jobs.find((entry) => entry.id === "timeout-cleanup-stuck-run"); expect(job?.state.lastStatus).toBe("error"); @@ -1404,11 +1419,11 @@ describe("cron service timer regressions", () => { expect(abortObserved).toBe(true); expect(job.state.lastStatus).toBe("error"); expect(job.state.lastError).toContain("setup timed out before runner start"); - expect(cleanupTimedOutAgentRun).toHaveBeenCalledWith({ - job: expect.objectContaining({ id: "isolated-setup-timeout-74803" }), - timeoutMs: 120_000, - execution: undefined, - }); + expect(cleanupTimedOutAgentRun).toHaveBeenCalledTimes(1); + const cleanupArgs = requireRecord(firstMockArg(cleanupTimedOutAgentRun)); + expect(requireRecord(cleanupArgs.job).id).toBe("isolated-setup-timeout-74803"); + expect(cleanupArgs.timeoutMs).toBe(120_000); + expect(cleanupArgs.execution).toBeUndefined(); } finally { vi.useRealTimers(); } @@ -1490,14 +1505,13 @@ describe("cron service timer regressions", () => { expect(job.state.lastStatus).toBe("error"); expect(job.state.lastError).toContain("stalled before first model call"); expect(job.state.lastError).toContain("context-engine"); - expect(cleanupTimedOutAgentRun).toHaveBeenCalledWith({ - job: expect.objectContaining({ id: "isolated-pre-model-timeout-74803" }), - timeoutMs: 1_200_000, - execution: expect.objectContaining({ - jobId: "isolated-pre-model-timeout-74803", - phase: "context_engine", - }), - }); + expect(cleanupTimedOutAgentRun).toHaveBeenCalledTimes(1); + const cleanupArgs = requireRecord(firstMockArg(cleanupTimedOutAgentRun)); + expect(requireRecord(cleanupArgs.job).id).toBe("isolated-pre-model-timeout-74803"); + expect(cleanupArgs.timeoutMs).toBe(1_200_000); + const execution = requireRecord(cleanupArgs.execution); + expect(execution.jobId).toBe("isolated-pre-model-timeout-74803"); + expect(execution.phase).toBe("context_engine"); } finally { vi.useRealTimers(); } @@ -1741,10 +1755,16 @@ describe("cron service timer regressions", () => { endedAt, }); - expect(job.state.lastDiagnostics).toMatchObject({ - summary: "exec stderr tail", - entries: [{ source: "exec", severity: "error", message: "exec stderr tail", exitCode: 1 }], - }); + expect(job.state.lastDiagnostics?.summary).toBe("exec stderr tail"); + expect(job.state.lastDiagnostics?.entries).toEqual([ + { + ts: startedAt, + source: "exec", + severity: "error", + message: "exec stderr tail", + exitCode: 1, + }, + ]); expect(job.state.lastDiagnosticSummary).toBe("exec stderr tail"); expect(log.warn).toHaveBeenCalledWith( {