





















@@ -0,0 +1,115 @@
1+import fs from "node:fs/promises";
2+import path from "node:path";
3+import { describe, expect, it, vi } from "vitest";
4+import { setupCronServiceSuite } from "../service.test-harness.js";
5+import { assertSupportedJobSpec, findJobOrThrow } from "./jobs.js";
6+import { createCronServiceState } from "./state.js";
7+import { ensureLoaded } from "./store.js";
8+9+const { logger, makeStorePath } = setupCronServiceSuite({
10+prefix: "cron-service-store-missing-session-target-",
11+});
12+13+const STORE_TEST_NOW = Date.parse("2026-03-23T12:00:00.000Z");
14+15+async function writeSingleJobStore(storePath: string, job: Record<string, unknown>) {
16+await fs.mkdir(path.dirname(storePath), { recursive: true });
17+await fs.writeFile(storePath, JSON.stringify({ version: 1, jobs: [job] }, null, 2), "utf8");
18+}
19+20+function createStoreTestState(storePath: string) {
21+return createCronServiceState({
22+ storePath,
23+cronEnabled: true,
24+log: logger,
25+nowMs: () => STORE_TEST_NOW,
26+enqueueSystemEvent: vi.fn(),
27+requestHeartbeatNow: vi.fn(),
28+runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const })),
29+});
30+}
31+32+describe("cron service store load: missing sessionTarget", () => {
33+it('defaults missing sessionTarget to "main" for systemEvent payloads', async () => {
34+const { storePath } = await makeStorePath();
35+36+await writeSingleJobStore(storePath, {
37+id: "missing-session-target-system-event",
38+name: "missing session target system event",
39+enabled: true,
40+createdAtMs: STORE_TEST_NOW - 60_000,
41+updatedAtMs: STORE_TEST_NOW - 60_000,
42+schedule: { kind: "every", everyMs: 60_000 },
43+wakeMode: "now",
44+payload: { kind: "systemEvent", text: "tick" },
45+state: {},
46+});
47+48+const state = createStoreTestState(storePath);
49+await ensureLoaded(state);
50+51+const job = findJobOrThrow(state, "missing-session-target-system-event");
52+expect(job.sessionTarget).toBe("main");
53+expect(() => assertSupportedJobSpec(job)).not.toThrow();
54+});
55+56+it('defaults missing sessionTarget to "isolated" for agentTurn payloads', async () => {
57+const { storePath } = await makeStorePath();
58+59+await writeSingleJobStore(storePath, {
60+id: "missing-session-target-agent-turn",
61+name: "missing session target agent turn",
62+enabled: true,
63+createdAtMs: STORE_TEST_NOW - 60_000,
64+updatedAtMs: STORE_TEST_NOW - 60_000,
65+schedule: { kind: "every", everyMs: 60_000 },
66+wakeMode: "now",
67+payload: { kind: "agentTurn", message: "ping" },
68+state: {},
69+});
70+71+const state = createStoreTestState(storePath);
72+await ensureLoaded(state);
73+74+const job = findJobOrThrow(state, "missing-session-target-agent-turn");
75+expect(job.sessionTarget).toBe("isolated");
76+expect(() => assertSupportedJobSpec(job)).not.toThrow();
77+});
78+79+it("assertSupportedJobSpec throws a clear error when sessionTarget is missing", () => {
80+const bogus = {
81+payload: { kind: "agentTurn" as const, message: "ping" },
82+} as unknown as Parameters<typeof assertSupportedJobSpec>[0];
83+expect(() => assertSupportedJobSpec(bogus)).toThrow(/missing sessionTarget/);
84+});
85+86+it("warns once per jobId across repeated forceReload cycles", async () => {
87+const { storePath } = await makeStorePath();
88+89+await writeSingleJobStore(storePath, {
90+id: "log-dedupe-target",
91+name: "log dedupe target",
92+enabled: true,
93+createdAtMs: STORE_TEST_NOW - 60_000,
94+updatedAtMs: STORE_TEST_NOW - 60_000,
95+schedule: { kind: "every", everyMs: 60_000 },
96+wakeMode: "now",
97+payload: { kind: "agentTurn", message: "ping" },
98+state: {},
99+});
100+101+const warnSpy = vi.spyOn(logger, "warn");
102+const state = createStoreTestState(storePath);
103+104+await ensureLoaded(state);
105+await ensureLoaded(state, { forceReload: true });
106+await ensureLoaded(state, { forceReload: true });
107+108+const missingSessionTargetWarns = warnSpy.mock.calls.filter((call) => {
109+const msg = typeof call[1] === "string" ? call[1] : "";
110+return msg.includes("missing sessionTarget");
111+});
112+expect(missingSessionTargetWarns).toHaveLength(1);
113+warnSpy.mockRestore();
114+});
115+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。