Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
import { afterEach, describe, expect, it, vi } from "vitest";
import { setupCronServiceSuite, writeCronStoreSnapshot } from "../../cron/service.test-harness.js";
import { createCronServiceState } from "../../cron/service/state.js";
import { onTimer } from "../../cron/service/timer.js";
import { loadCronStore } from "../../cron/store.js";
import type { CronJob } from "../../cron/types.js";
import * as detachedTaskRuntime from "../../tasks/detached-task-runtime.js";
import { resetTaskRegistryForTests } from "../../tasks/task-registry.js";
const { logger, makeStorePath } = setupCronServiceSuite({
prefix: "cron-service-timer-seam",
});
function createDueMainJob(params: { now: number; wakeMode: CronJob["wakeMode"] }): CronJob {
return {
id: "main-heartbeat-job",
name: "main heartbeat job",
enabled: true,
createdAtMs: params.now - 60_000,
updatedAtMs: params.now - 60_000,
schedule: { kind: "every", everyMs: 60_000, anchorMs: params.now - 60_000 },
sessionTarget: "main",
wakeMode: params.wakeMode,
payload: { kind: "systemEvent", text: "heartbeat seam tick" },
sessionKey: "agent:main:main",
state: { nextRunAtMs: params.now - 1 },
};
}
afterEach(() => {
resetTaskRegistryForTests();
});
describe("cron service timer seam coverage", () => {
it("persists the next schedule and hands off next-heartbeat main jobs", async () => {
const { storePath } = await makeStorePath();
const now = Date.parse("2026-03-23T12:00:00.000Z");
const enqueueSystemEvent = vi.fn();
const requestHeartbeatNow = vi.fn();
const timeoutSpy = vi.spyOn(globalThis, "setTimeout");
await writeCronStoreSnapshot({
storePath,
jobs: [createDueMainJob({ now, wakeMode: "next-heartbeat" })],
});
const state = createCronServiceState({
storePath,
cronEnabled: true,
log: logger,
nowMs: () => now,
enqueueSystemEvent,
requestHeartbeatNow,
runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const })),
});
await onTimer(state);
expect(enqueueSystemEvent).toHaveBeenCalledWith("heartbeat seam tick", {
agentId: undefined,
sessionKey: "agent:main:main",
contextKey: "cron:main-heartbeat-job",
});
expect(requestHeartbeatNow).toHaveBeenCalledWith({
reason: "cron:main-heartbeat-job",
agentId: undefined,
sessionKey: "agent:main:main",
heartbeat: { target: "last" },
});
const persisted = await loadCronStore(storePath);
const job = persisted.jobs[0];
expect(job).toBeDefined();
expect(job?.state.lastStatus).toBe("ok");
expect(job?.state.runningAtMs).toBeUndefined();
expect(job?.state.nextRunAtMs).toBe(now + 60_000);
const delays = timeoutSpy.mock.calls
.map(([, delay]) => delay)
.filter((delay): delay is number => typeof delay === "number");
expect(delays.some((delay) => delay > 0)).toBe(true);
timeoutSpy.mockRestore();
});
it("keeps scheduler progress when task ledger creation fails", async () => {
const { storePath } = await makeStorePath();
const now = Date.parse("2026-03-23T12:00:00.000Z");
const enqueueSystemEvent = vi.fn();
const requestHeartbeatNow = vi.fn();
await writeCronStoreSnapshot({
storePath,
jobs: [createDueMainJob({ now, wakeMode: "next-heartbeat" })],
});
const createTaskRecordSpy = vi
.spyOn(detachedTaskRuntime, "createRunningTaskRun")
.mockImplementation(() => {
throw new Error("disk full");
});
const state = createCronServiceState({
storePath,
cronEnabled: true,
log: logger,
nowMs: () => now,
enqueueSystemEvent,
requestHeartbeatNow,
runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const })),
});
await onTimer(state);
expect(logger.warn).toHaveBeenCalledWith(
expect.objectContaining({ jobId: "main-heartbeat-job" }),
"cron: failed to create task ledger record",
);
expect(enqueueSystemEvent).toHaveBeenCalledWith("heartbeat seam tick", {
agentId: undefined,
sessionKey: "agent:main:main",
contextKey: "cron:main-heartbeat-job",
});
createTaskRecordSpy.mockRestore();
});
});
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet am 2026-04-27)
¤
*© Formatika GbR, Deutschland
|
|