From 7d64b11045ebbe63fe69ff6c70594d5ee29c2944 Mon Sep 17 00:00:00 2001 From: claude code agent 227 Date: Fri, 26 Jun 2026 18:05:45 +0300 Subject: [PATCH] test: cover future-deadline (re-armed) branch in temporary-note cleanup guard The deletion guard skips a note when its re-read deadline is still in the future (user disarmed-then-re-armed in the race window between the batch SELECT and the per-row re-read). The default stub returns an epoch deadline (always < now), so the existing race tests never exercised the `new Date(temporaryExpiresAt) >= now` branch; a regression dropping it or inverting the comparison would pass unnoticed. Add a test that re-reads a fresh future deadline and asserts removePage is not called. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../temporary-note-cleanup.service.spec.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/server/src/core/page/services/spec/temporary-note-cleanup.service.spec.ts b/apps/server/src/core/page/services/spec/temporary-note-cleanup.service.spec.ts index 66ac3586..54fd49aa 100644 --- a/apps/server/src/core/page/services/spec/temporary-note-cleanup.service.spec.ts +++ b/apps/server/src/core/page/services/spec/temporary-note-cleanup.service.spec.ts @@ -123,6 +123,26 @@ describe('TemporaryNoteCleanupService.sweepExpiredTemporaryNotes', () => { expect(pageRepo.removePage).not.toHaveBeenCalled(); }); + it('does NOT trash a note re-armed to a future deadline in the race window', async () => { + // The batch SELECT saw the note as expired, but before its turn in the loop + // the user disarmed it and re-armed it to a fresh, still-future deadline + // (temporary_expires_at -> now + 1h). The deadline re-read must catch that + // the note is no longer expired and skip the delete so the keep wins. + const expired = [{ id: 'p1', creatorId: 'u1', workspaceId: 'w1' }]; + const { builder, reReadFirst } = makeDbStub(expired); + reReadFirst.mockResolvedValueOnce({ + temporaryExpiresAt: new Date(Date.now() + 60 * 60 * 1000), + deletedAt: null, + }); + const pageRepo = { removePage: jest.fn() } as any; + const service = new TemporaryNoteCleanupService(builder, pageRepo); + + await service.sweepExpiredTemporaryNotes(); + + expect(reReadFirst).toHaveBeenCalledTimes(1); + expect(pageRepo.removePage).not.toHaveBeenCalled(); + }); + it('does nothing when no notes are expired', async () => { const { builder } = makeDbStub([]); const pageRepo = { removePage: jest.fn() } as any;