mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:00:43 +00:00
Reject array-shaped cron state sidecars
This commit is contained in:
@@ -296,6 +296,44 @@ describe("cron store", () => {
|
||||
expect(stateFile.jobs["job-1"].state.nextRunAtMs).toBe(legacy.jobs[0].createdAtMs + 60_000);
|
||||
});
|
||||
|
||||
it("ignores array-shaped state sidecars when migrating legacy inline state", async () => {
|
||||
const store = await makeStorePath();
|
||||
const statePath = store.storePath.replace(/\.json$/, "-state.json");
|
||||
// Numeric-looking IDs catch accidental array indexing in invalid sidecars.
|
||||
const legacy = makeStore("0", true);
|
||||
legacy.jobs[0].state = {
|
||||
lastRunAtMs: legacy.jobs[0].createdAtMs + 30_000,
|
||||
nextRunAtMs: legacy.jobs[0].createdAtMs + 60_000,
|
||||
};
|
||||
const staleSidecar = {
|
||||
...legacy,
|
||||
jobs: [
|
||||
{
|
||||
...legacy.jobs[0],
|
||||
updatedAtMs: legacy.jobs[0].updatedAtMs + 10_000,
|
||||
state: {
|
||||
nextRunAtMs: legacy.jobs[0].createdAtMs + 120_000,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await fs.mkdir(path.dirname(store.storePath), { recursive: true });
|
||||
await fs.writeFile(store.storePath, JSON.stringify(legacy, null, 2), "utf-8");
|
||||
await fs.writeFile(statePath, JSON.stringify(staleSidecar, null, 2), "utf-8");
|
||||
|
||||
const loaded = await loadCronStore(store.storePath);
|
||||
await saveCronStore(store.storePath, loaded);
|
||||
|
||||
const stateFile = JSON.parse(await fs.readFile(statePath, "utf-8"));
|
||||
|
||||
expect(loaded.jobs[0]?.updatedAtMs).toBe(legacy.jobs[0].updatedAtMs);
|
||||
expect(loaded.jobs[0]?.state.nextRunAtMs).toBe(legacy.jobs[0].createdAtMs + 60_000);
|
||||
expect(Array.isArray(stateFile.jobs)).toBe(false);
|
||||
expect(stateFile.jobs["0"].updatedAtMs).toBe(legacy.jobs[0].updatedAtMs);
|
||||
expect(stateFile.jobs["0"].state.nextRunAtMs).toBe(legacy.jobs[0].createdAtMs + 60_000);
|
||||
});
|
||||
|
||||
it("treats a corrupt state sidecar as absent", async () => {
|
||||
const store = await makeStorePath();
|
||||
const payload = makeStore("job-1", true);
|
||||
|
||||
@@ -99,7 +99,12 @@ async function loadStateFile(statePath: string): Promise<CronStateFile | null> {
|
||||
return null;
|
||||
}
|
||||
const record = parsed as Record<string, unknown>;
|
||||
if (record.version !== 1 || typeof record.jobs !== "object" || record.jobs === null) {
|
||||
if (
|
||||
record.version !== 1 ||
|
||||
typeof record.jobs !== "object" ||
|
||||
record.jobs === null ||
|
||||
Array.isArray(record.jobs)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return { version: 1, jobs: record.jobs as Record<string, CronStateFileEntry> };
|
||||
|
||||
Reference in New Issue
Block a user