Screenshot guardrails: name every screenshot not COMPLETE on 409 ASSET_IN_POST_PROCESSING (submit, validate; stuck after 5 min in UPLOAD_COMPLETE), apple screenshots --repair-stuck replaces only those in place, part uploads without Expect: 100-continue (failed part deletes the reservation, one retry, never committed), storebound api --confirm <operationId> for agents' plain writes (never owner-only)

This commit is contained in:
CRTkafa 2026-10-21 12:40:26 +07:00
parent 164182012f
commit 00756e6908
19 changed files with 475 additions and 45 deletions

View file

@ -529,9 +529,12 @@ describe('crash midway and unclear answers', () => {
const md5 = (b: Uint8Array) => new Bun.CryptoHasher('md5').update(b).digest('hex');
s.asc.put({ type: 'appScreenshots', id: 'shot-a', attributes: { sourceFileChecksum: md5(png(1)), assetDeliveryState: { state: 'COMPLETE' }, fileName: '01.png' }, rels: { appScreenshotSet: set.id } });
s.asc.put({ type: 'appScreenshots', id: 'shot-x', attributes: { sourceFileChecksum: 'stale', assetDeliveryState: { state: 'COMPLETE' }, fileName: 'old.png' }, rels: { appScreenshotSet: set.id } });
s.failPart = n => n === 2; // the 2nd uploaded part (03.png) fails
s.failPart = n => n === 2 || n === 3; // 03.png fails, and its one retry too
const r1 = await capture(() => appleScreenshots(p.ctx(['--only', 'en-US']))).catch(e => ({ out: String(e), err: '', exit: 1 }));
expect(r1.out + r1.err).toContain('03.png part 0 -> 400');
expect(r1.out + r1.err).toContain('nothing committed');
// the failed reservations were deleted, not left AWAITING_UPLOAD
expect(s.asc.children(set.id, 'appScreenshots').filter(x => (x.attributes.assetDeliveryState as { state: string } | undefined)?.state !== 'COMPLETE')).toEqual([]);
expect(s.asc.children(set.id, 'appScreenshots').map(x => x.id)).toContain('shot-a');
s.failPart = undefined;
const r2 = await capture(() => appleScreenshots(p.ctx(['--only', 'en-US'])));