mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
test: pair cron task state env
This commit is contained in:
@@ -6,7 +6,7 @@ import { runOpenClawStateWriteTransaction } from "../../state/openclaw-state-db.
|
|||||||
import * as detachedTaskRuntime from "../../tasks/detached-task-runtime.js";
|
import * as detachedTaskRuntime from "../../tasks/detached-task-runtime.js";
|
||||||
import { findTaskByRunId, resetTaskRegistryForTests } from "../../tasks/task-registry.js";
|
import { findTaskByRunId, resetTaskRegistryForTests } from "../../tasks/task-registry.js";
|
||||||
import { formatTaskStatusDetail } from "../../tasks/task-status.js";
|
import { formatTaskStatusDetail } from "../../tasks/task-status.js";
|
||||||
import { captureEnv } from "../../test-utils/env.js";
|
import { withEnvAsync } from "../../test-utils/env.js";
|
||||||
import { setupCronServiceSuite, writeCronStoreSnapshot } from "../service.test-harness.js";
|
import { setupCronServiceSuite, writeCronStoreSnapshot } from "../service.test-harness.js";
|
||||||
import { loadCronJobsStoreWithConfigJobs, loadCronStore } from "../store.js";
|
import { loadCronJobsStoreWithConfigJobs, loadCronStore } from "../store.js";
|
||||||
import type { CronJob } from "../types.js";
|
import type { CronJob } from "../types.js";
|
||||||
@@ -18,15 +18,17 @@ const { logger, makeStorePath } = setupCronServiceSuite({
|
|||||||
prefix: "cron-service-ops-seam",
|
prefix: "cron-service-ops-seam",
|
||||||
});
|
});
|
||||||
|
|
||||||
function withStateDirForStorePath(storePath: string) {
|
async function withStateDirForStorePath<T>(
|
||||||
|
storePath: string,
|
||||||
|
runWithStateDir: () => Promise<T>,
|
||||||
|
): Promise<T> {
|
||||||
const stateRoot = path.dirname(path.dirname(storePath));
|
const stateRoot = path.dirname(path.dirname(storePath));
|
||||||
const envSnapshot = captureEnv(["OPENCLAW_STATE_DIR"]);
|
|
||||||
process.env.OPENCLAW_STATE_DIR = stateRoot;
|
|
||||||
resetTaskRegistryForTests();
|
resetTaskRegistryForTests();
|
||||||
return () => {
|
try {
|
||||||
envSnapshot.restore();
|
return await withEnvAsync({ OPENCLAW_STATE_DIR: stateRoot }, runWithStateDir);
|
||||||
|
} finally {
|
||||||
resetTaskRegistryForTests();
|
resetTaskRegistryForTests();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTimedOutIsolatedCronState(params: { storePath: string; now: number }) {
|
function createTimedOutIsolatedCronState(params: { storePath: string; now: number }) {
|
||||||
@@ -413,9 +415,8 @@ describe("cron service ops seam coverage", () => {
|
|||||||
it("keeps manual acknowledgement IDs separate from recoverable task run IDs", async () => {
|
it("keeps manual acknowledgement IDs separate from recoverable task run IDs", async () => {
|
||||||
const { storePath } = await makeStorePath();
|
const { storePath } = await makeStorePath();
|
||||||
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
||||||
const restoreStateDir = withStateDirForStorePath(storePath);
|
|
||||||
|
|
||||||
try {
|
await withStateDirForStorePath(storePath, async () => {
|
||||||
await writeDueIsolatedJobSnapshot(storePath, now);
|
await writeDueIsolatedJobSnapshot(storePath, now);
|
||||||
|
|
||||||
const state = createOkIsolatedCronState({ storePath, now, summary: "done" });
|
const state = createOkIsolatedCronState({ storePath, now, summary: "done" });
|
||||||
@@ -436,17 +437,14 @@ describe("cron service ops seam coverage", () => {
|
|||||||
progressSummary: "Running cron job.",
|
progressSummary: "Running cron job.",
|
||||||
});
|
});
|
||||||
expect(findTaskByRunId(manualRunId)).toBeUndefined();
|
expect(findTaskByRunId(manualRunId)).toBeUndefined();
|
||||||
} finally {
|
});
|
||||||
restoreStateDir();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("records timed out manual runs as timed_out in the shared task registry", async () => {
|
it("records timed out manual runs as timed_out in the shared task registry", async () => {
|
||||||
const { storePath } = await makeStorePath();
|
const { storePath } = await makeStorePath();
|
||||||
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
||||||
const restoreStateDir = withStateDirForStorePath(storePath);
|
|
||||||
|
|
||||||
try {
|
await withStateDirForStorePath(storePath, async () => {
|
||||||
await writeDueIsolatedJobSnapshot(storePath, now);
|
await writeDueIsolatedJobSnapshot(storePath, now);
|
||||||
|
|
||||||
const state = createTimedOutIsolatedCronState({
|
const state = createTimedOutIsolatedCronState({
|
||||||
@@ -462,9 +460,7 @@ describe("cron service ops seam coverage", () => {
|
|||||||
status: "timed_out",
|
status: "timed_out",
|
||||||
sourceId: "isolated-timeout",
|
sourceId: "isolated-timeout",
|
||||||
});
|
});
|
||||||
} finally {
|
});
|
||||||
restoreStateDir();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps manual cron runs progressing when task ledger creation fails", async () => {
|
it("keeps manual cron runs progressing when task ledger creation fails", async () => {
|
||||||
@@ -495,9 +491,8 @@ describe("cron service ops seam coverage", () => {
|
|||||||
it("keeps manual cron cleanup progressing when task ledger updates fail", async () => {
|
it("keeps manual cron cleanup progressing when task ledger updates fail", async () => {
|
||||||
const { storePath } = await makeStorePath();
|
const { storePath } = await makeStorePath();
|
||||||
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
||||||
const restoreStateDir = withStateDirForStorePath(storePath);
|
|
||||||
|
|
||||||
try {
|
await withStateDirForStorePath(storePath, async () => {
|
||||||
await writeDueIsolatedJobSnapshot(storePath, now);
|
await writeDueIsolatedJobSnapshot(storePath, now);
|
||||||
|
|
||||||
const updateTaskRecordSpy = vi
|
const updateTaskRecordSpy = vi
|
||||||
@@ -516,9 +511,7 @@ describe("cron service ops seam coverage", () => {
|
|||||||
} finally {
|
} finally {
|
||||||
updateTaskRecordSpy.mockRestore();
|
updateTaskRecordSpy.mockRestore();
|
||||||
}
|
}
|
||||||
} finally {
|
});
|
||||||
restoreStateDir();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("non-schedule edit preserves nextRunAtMs (#63499)", async () => {
|
it("non-schedule edit preserves nextRunAtMs (#63499)", async () => {
|
||||||
@@ -586,9 +579,8 @@ describe("cron service ops seam coverage", () => {
|
|||||||
it("records startup catch-up timeouts as timed_out in the shared task registry", async () => {
|
it("records startup catch-up timeouts as timed_out in the shared task registry", async () => {
|
||||||
const { storePath } = await makeStorePath();
|
const { storePath } = await makeStorePath();
|
||||||
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
||||||
const restoreStateDir = withStateDirForStorePath(storePath);
|
|
||||||
|
|
||||||
try {
|
await withStateDirForStorePath(storePath, async () => {
|
||||||
await writeCronStoreSnapshot({
|
await writeCronStoreSnapshot({
|
||||||
storePath,
|
storePath,
|
||||||
jobs: [createMissedIsolatedJob(now)],
|
jobs: [createMissedIsolatedJob(now)],
|
||||||
@@ -608,17 +600,14 @@ describe("cron service ops seam coverage", () => {
|
|||||||
sourceId: "startup-timeout",
|
sourceId: "startup-timeout",
|
||||||
progressSummary: "Running cron job.",
|
progressSummary: "Running cron job.",
|
||||||
});
|
});
|
||||||
} finally {
|
});
|
||||||
restoreStateDir();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("seeds active manual cron task progress for status surfaces", async () => {
|
it("seeds active manual cron task progress for status surfaces", async () => {
|
||||||
const { storePath } = await makeStorePath();
|
const { storePath } = await makeStorePath();
|
||||||
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
const now = Date.parse("2026-03-23T12:00:00.000Z");
|
||||||
const restoreStateDir = withStateDirForStorePath(storePath);
|
|
||||||
|
|
||||||
try {
|
await withStateDirForStorePath(storePath, async () => {
|
||||||
await writeDueIsolatedJobSnapshot(storePath, now);
|
await writeDueIsolatedJobSnapshot(storePath, now);
|
||||||
let resolveRun: ((value: { status: "ok"; summary: string }) => void) | undefined;
|
let resolveRun: ((value: { status: "ok"; summary: string }) => void) | undefined;
|
||||||
const state = createCronServiceState({
|
const state = createCronServiceState({
|
||||||
@@ -651,8 +640,6 @@ describe("cron service ops seam coverage", () => {
|
|||||||
|
|
||||||
resolveRun?.({ status: "ok", summary: "done" });
|
resolveRun?.({ status: "ok", summary: "done" });
|
||||||
await manualRun;
|
await manualRun;
|
||||||
} finally {
|
});
|
||||||
restoreStateDir();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user