




















@@ -7,6 +7,8 @@ import {
77listDueCommitmentsForSession,
88listPendingCommitmentsForScope,
99loadCommitmentStore,
10+markCommitmentsAttempted,
11+markCommitmentsStatus,
1012saveCommitmentStore,
1113} from "./store.js";
1214import type { CommitmentRecord } from "./types.js";
@@ -232,4 +234,63 @@ describe("commitment store delivery selection", () => {
232234expect(expiredCommitments[0]?.id).toBe("cm_interview");
233235expect(expiredCommitments[0]?.status).toBe("expired");
234236});
237+238+it("serializes concurrent markCommitmentsStatus on disjoint ids without losing a write", async () => {
239+await useTempStateDir();
240+await saveCommitmentStore(undefined, {
241+version: 1,
242+commitments: [
243+commitment({ id: "cm_raceA", dedupeKey: "race-A" }),
244+commitment({ id: "cm_raceB", dedupeKey: "race-B" }),
245+],
246+});
247+248+await Promise.all([
249+markCommitmentsStatus({ ids: ["cm_raceA"], status: "dismissed", nowMs }),
250+markCommitmentsStatus({ ids: ["cm_raceB"], status: "dismissed", nowMs }),
251+]);
252+253+const after = await loadCommitmentStore();
254+const byId = Object.fromEntries(after.commitments.map((c) => [c.id, c.status]));
255+expect(byId.cm_raceA).toBe("dismissed");
256+expect(byId.cm_raceB).toBe("dismissed");
257+});
258+259+it("serializes concurrent markCommitmentsAttempted bumps without losing the counter", async () => {
260+await useTempStateDir();
261+await saveCommitmentStore(undefined, {
262+version: 1,
263+commitments: [commitment({ id: "cm_race_attempts", attempts: 0 })],
264+});
265+266+await Promise.all(
267+Array.from({ length: 5 }, () =>
268+markCommitmentsAttempted({ ids: ["cm_race_attempts"], nowMs }),
269+),
270+);
271+272+const after = await loadCommitmentStore();
273+expect(after.commitments[0]?.attempts).toBe(5);
274+});
275+276+it("serializes a markCommitmentsStatus dismiss against a concurrent attempted bump", async () => {
277+await useTempStateDir();
278+await saveCommitmentStore(undefined, {
279+version: 1,
280+commitments: [
281+commitment({ id: "cm_dismiss_target", dedupeKey: "dismiss-target" }),
282+commitment({ id: "cm_attempt_target", dedupeKey: "attempt-target", attempts: 2 }),
283+],
284+});
285+286+await Promise.all([
287+markCommitmentsStatus({ ids: ["cm_dismiss_target"], status: "dismissed", nowMs }),
288+markCommitmentsAttempted({ ids: ["cm_attempt_target"], nowMs }),
289+]);
290+291+const after = await loadCommitmentStore();
292+const byId = Object.fromEntries(after.commitments.map((c) => [c.id, c]));
293+expect(byId.cm_dismiss_target?.status).toBe("dismissed");
294+expect(byId.cm_attempt_target?.attempts).toBe(3);
295+});
235296});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。