fix(db): jobs.update writes only patched columns; late progress cannot resurrect a finished job

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-30 03:01:05 +01:00
1 parent 4636273d55
commit f061e36092
2 files changed
+39 -6

No files matched your search

+25
View File
@@ -80,6 +80,7 @@ forEachDb("jobs lifecycle", async (db) => {
const id = await j.insert("import", { url: "x" });
const got = await j.get(id);
expect(got).toMatchObject({ kind: "import", status: "queued", progress: 0, payload: { url: "x" }, result: null, error: null, message: null });
await j.claimNext(["import"]);
await j.update(id, { progress: 0.5, message: "half" });
await j.update(id, { status: "done", result: { ok: 1 } });
expect(await j.get(id)).toMatchObject({ status: "done", progress: 0.5, message: "half", result: { ok: 1 } });
@@ -123,3 +124,27 @@ forEachDb("two concurrent claimNext calls never claim the same job", async (db)
const res = await Promise.all([solo.claimNext(["k"]), solo.claimNext(["k"])]);
expect(res.filter((x) => x !== null).map((x) => x!.id)).toEqual([only]);
});
forEachDb("late progress update does not resurrect a done job", async (db) => {
const j = jobsRepo(db);
const id = await j.insert("k", {});
await j.claimNext(["k"]);
await j.update(id, { status: "done", progress: 1, result: { ok: true } });
await j.update(id, { progress: 0.4, message: "stale" });
expect(await j.get(id)).toMatchObject({ status: "done", progress: 1, message: null, result: { ok: true } });
const f = await j.insert("k", {});
await j.update(f, { status: "failed", error: "boom" });
await j.update(f, { progress: 0.9 });
expect(await j.get(f)).toMatchObject({ status: "failed", error: "boom", progress: 0 });
});
forEachDb("update only touches patched columns", async (db) => {
const j = jobsRepo(db);
const id = await j.insert("k", { a: 1 });
await j.claimNext(["k"]);
await j.update(id, { progress: 0.5, message: "working" });
await j.update(id, { message: "still" });
expect(await j.get(id)).toMatchObject({ status: "running", progress: 0.5, message: "still", result: null, error: null, payload: { a: 1 } });
await j.update(id, { result: { r: 1 } });
expect(await j.get(id)).toMatchObject({ status: "running", progress: 0.5, message: "still", result: { r: 1 } });
});