test(ci): settle cron wakes and refresh fast-lane fixtures

Worker-backed heartbeat preflight must settle before fixture assertions and runner retirement; otherwise unfinished notifications replay into later cases. Keep all routing, delivery, and timeout assertions. Use a pure command fixture for positive unit-fast routing now that status-overview-values uses theme spies.

Main CI failures reproduced: cron 5/15 and routing 3/16. Corrected files pass 31/31 cases (cron 13.86s wall, routing 6.13s). Focused lint, formatting, static guards, services-cron and four root-test type graphs, and independent P0-P2 review pass. No production changes.
This commit is contained in:
joshavant 2026-09-29 19:42:42 -05:00
parent ea096eaebd
commit e0ec544eb1
No known key found for this signature in database
GPG key ID: 4463B60B0DD49BC4
3 changed files with 43 additions and 20 deletions

View file

@ -8,7 +8,7 @@ import {
resolveHeartbeatRunPrompt,
} from "../../infra/heartbeat-runner-prompt.js";
import { startHeartbeatRunner } from "../../infra/heartbeat-runner-scheduler.js";
import { requestHeartbeat as requestHeartbeatWake } from "../../infra/heartbeat-wake.js";
import { requestHeartbeatAndWait } from "../../infra/heartbeat-wake.js";
import {
drainSystemEvents,
enqueueSystemEvent as queueSystemEvent,
@ -127,6 +127,7 @@ describe("startup run repair auto-disable", () => {
const sessionKey =
testCase.creatorSessionKey ?? resolveAgentMainSessionKey({ cfg, agentId: "main" });
const prompts: string[] = [];
const pendingWakes: Array<ReturnType<typeof requestHeartbeatAndWait>> = [];
const runOnce = vi.fn(async (options: HeartbeatRunOptions) => {
const preflight = await resolveHeartbeatPreflight({
cfg,
@ -165,12 +166,15 @@ describe("startup run repair auto-disable", () => {
sessionKey: options?.sessionKey ?? sessionKey,
contextKey: options?.contextKey,
}),
requestHeartbeat: (wake) =>
requestHeartbeatWake({
...wake,
sessionKey: wake.sessionKey ?? sessionKey,
coalesceMs: 0,
}),
requestHeartbeat: (wake) => {
pendingWakes.push(
requestHeartbeatAndWait({
...wake,
sessionKey: wake.sessionKey ?? sessionKey,
coalesceMs: 0,
}),
);
},
runIsolatedAgentJob: vi.fn(),
});
const job: CronJob = {
@ -200,6 +204,7 @@ describe("startup run repair auto-disable", () => {
expect(deferredNotifications).toHaveLength(1);
runPostPersistCronNotifications(state, structuredClone(deferredNotifications));
await vi.advanceTimersByTimeAsync(1);
await Promise.all(pendingWakes);
expect(runOnce).toHaveBeenCalledOnce();
expect(runOnce).toHaveBeenCalledWith(
@ -216,9 +221,15 @@ describe("startup run repair auto-disable", () => {
expect(prompts[0]).toContain("openclaw automations enable restart-auto-disable-notification");
expect(prompts[0]).toContain("Please relay this reminder to the user");
} finally {
runner.stop();
drainSystemEvents(sessionKey);
vi.useRealTimers();
try {
// Stopping the runner retains unfinished notifications for its successor.
await vi.advanceTimersByTimeAsync(1);
await Promise.all(pendingWakes);
} finally {
runner.stop();
drainSystemEvents(sessionKey);
vi.useRealTimers();
}
}
});

View file

@ -7,7 +7,7 @@ import {
resolveHeartbeatRunPrompt,
} from "../../infra/heartbeat-runner-prompt.js";
import { startHeartbeatRunner } from "../../infra/heartbeat-runner-scheduler.js";
import { requestHeartbeat as requestHeartbeatWake } from "../../infra/heartbeat-wake.js";
import { requestHeartbeatAndWait } from "../../infra/heartbeat-wake.js";
import {
drainSystemEvents,
enqueueSystemEvent as queueSystemEvent,
@ -60,6 +60,7 @@ describe("cron script immediate wake", () => {
};
const sessionKey = resolveAgentMainSessionKey({ cfg, agentId: "finn" });
const prompts: string[] = [];
const pendingWakes: Array<ReturnType<typeof requestHeartbeatAndWait>> = [];
const runOnce = vi.fn(async (options: HeartbeatRunOptions) => {
const prompt = resolveHeartbeatRunPrompt({
cfg,
@ -99,8 +100,11 @@ describe("cron script immediate wake", () => {
contextKey: options?.contextKey,
deliveryContext: options?.deliveryContext,
}),
requestHeartbeat: (wakeRequest) =>
requestHeartbeatWake({ ...wakeRequest, sessionKey, coalesceMs: 0 }),
requestHeartbeat: (wakeRequest) => {
pendingWakes.push(
requestHeartbeatAndWait({ ...wakeRequest, sessionKey, coalesceMs: 0 }),
);
},
runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const })),
runScriptJob: vi.fn(async () => ({
status: "ok" as const,
@ -128,6 +132,7 @@ describe("cron script immediate wake", () => {
]);
await vi.advanceTimersByTimeAsync(1);
await Promise.all(pendingWakes);
if (!immediate) {
expect(runOnce).not.toHaveBeenCalled();
@ -150,9 +155,15 @@ describe("cron script immediate wake", () => {
expect(prompts[0]).toContain(expectedText);
expect(prompts[0]).toContain("Please relay this reminder to the user");
} finally {
runner.stop();
drainSystemEvents(sessionKey);
vi.useRealTimers();
try {
// Stopping the runner retains unfinished notifications for its successor.
await vi.advanceTimersByTimeAsync(1);
await Promise.all(pendingWakes);
} finally {
runner.stop();
drainSystemEvents(sessionKey);
vi.useRealTimers();
}
}
},
);

View file

@ -426,7 +426,7 @@ describe("unit-fast vitest lane", () => {
"src/agents/agent-tools.deferred-followup-guidance.test.ts",
);
expect(testConfig.include).toContain("src/acp/runtime/registry.test.ts");
expect(testConfig.include).toContain("src/commands/status-overview-values.test.ts");
expect(testConfig.include).toContain("src/commands/text-format.test.ts");
expect(testConfig.include).toContain("src/plugins/config-policy.test.ts");
expect(testConfig.include).toContain("src/sessions/session-lifecycle-events.test.ts");
expect(testConfig.include).toContain("src/plugin-sdk/text-chunking.test.ts");
@ -443,7 +443,7 @@ describe("unit-fast vitest lane", () => {
const testConfig = requireTestConfig(config);
expect(testConfig.include).toContain("src/plugin-sdk/text-chunking.test.ts");
expect(testConfig.include).toContain("src/commands/status-overview-values.test.ts");
expect(testConfig.include).toContain("src/commands/text-format.test.ts");
});
it("keeps excluded stateful files out of directory-scoped CLI runs", () => {
@ -491,6 +491,7 @@ describe("unit-fast vitest lane", () => {
"src/agents/code-mode-quickjs.integration.test.ts",
"src/agents/prepared-model-runtime.scoped-refresh.test.ts",
"src/agents/provider-transport-fetch.headers.test.ts",
"src/commands/status-overview-values.test.ts",
]) {
expect(isUnitFastTestFile(file), file).toBe(false);
expect(resolveUnitFastTestIncludePattern(file), file).toBeNull();
@ -528,8 +529,8 @@ describe("unit-fast vitest lane", () => {
expect(resolveUnitFastTestIncludePattern("src/plugin-sdk/text-chunking.ts")).toBe(
"src/plugin-sdk/text-chunking.test.ts",
);
expect(resolveUnitFastTestIncludePattern("src/commands/status-overview-values.ts")).toBe(
"src/commands/status-overview-values.test.ts",
expect(resolveUnitFastTestIncludePattern("src/commands/text-format.ts")).toBe(
"src/commands/text-format.test.ts",
);
});