/** * Regression: concurrent capturePart writes must not clobber sibling slots. * * capturePartV2 used to `$set` the entire `media.damagedParts` array from a * stale read. Parallel uploads (common while Fanavaran attachment submit keeps * the HTTP request open) made the last writer win — Fanavaran still saw each * file on disk and could return errors, while Mongo was missing captures. * * Required strategy: per-index `$set` (`media.damagedParts.N`), matching * `media.carAngles.`. */ describe("capture-part media.damagedParts write strategies", () => { type Row = { path?: string; fileName?: string; name?: string }; type Claim = { media: { damagedParts: Row[] } }; const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); /** Legacy (buggy) strategy: replace the entire array from a stale read. */ async function writeWholeArray( store: { claim: Claim }, index: number, capture: Row, readDelayMs: number, ) { const snapshot = structuredClone(store.claim); await sleep(readDelayMs); const next = snapshot.media.damagedParts.map((row) => ({ ...row })); while (next.length <= index) next.push({}); next[index] = { ...next[index], ...capture }; store.claim = { ...store.claim, media: { ...store.claim.media, damagedParts: next }, }; } /** Required strategy: set only the target index (Mongo $set media.damagedParts.N). */ async function writeSingleIndex( store: { claim: Claim }, index: number, capture: Row, readDelayMs: number, ) { await sleep(readDelayMs); const next = store.claim.media.damagedParts.map((row) => ({ ...row })); while (next.length <= index) next.push({}); next[index] = { ...next[index], ...capture }; store.claim.media.damagedParts[index] = next[index]; } it("documents that whole-array replace loses a concurrent capture", async () => { const store: { claim: Claim } = { claim: { media: { damagedParts: [{ name: "hood" }, { name: "front_bumper" }], }, }, }; await Promise.all([ writeWholeArray( store, 0, { path: "files/claim-captures/hood.jpg", fileName: "hood.jpg" }, 30, ), writeWholeArray( store, 1, { path: "files/claim-captures/bumper.jpg", fileName: "bumper.jpg", }, 10, ), ]); expect(store.claim.media.damagedParts.map((r) => r.path).filter(Boolean)) .toHaveLength(1); }); it("keeps both concurrent captures with per-index writes", async () => { const store: { claim: Claim } = { claim: { media: { damagedParts: [{ name: "hood" }, { name: "front_bumper" }], }, }, }; await Promise.all([ writeSingleIndex( store, 0, { path: "files/claim-captures/hood.jpg", fileName: "hood.jpg" }, 30, ), writeSingleIndex( store, 1, { path: "files/claim-captures/bumper.jpg", fileName: "bumper.jpg", }, 10, ), ]); expect(store.claim.media.damagedParts.map((r) => r.path)).toEqual([ "files/claim-captures/hood.jpg", "files/claim-captures/bumper.jpg", ]); }); });