From 78c10e88170d131aefc0333f3ca0c3f4d14b5b5e Mon Sep 17 00:00:00 2001 From: altair823 Date: Fri, 1 May 2026 21:00:09 +0900 Subject: [PATCH] feat(trash): AiWorker.processJob deletedAt guard (#4 v0.2.3) --- src/main/ai/AiWorker.ts | 2 +- tests/unit/AiWorker.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/ai/AiWorker.ts b/src/main/ai/AiWorker.ts index fe1df6e..2787edb 100644 --- a/src/main/ai/AiWorker.ts +++ b/src/main/ai/AiWorker.ts @@ -121,7 +121,7 @@ export class AiWorker { const startMs = this.now().getTime(); try { const note = this.repo.findById(job.noteId); - if (!note || note.aiStatus !== 'pending') return; + if (!note || note.deletedAt !== null || note.aiStatus !== 'pending') return; const nowDate = this.now(); const todayDate = todayKstAsDate(nowDate); const todayIso = todayKstAsIso(nowDate); diff --git a/tests/unit/AiWorker.test.ts b/tests/unit/AiWorker.test.ts index 7e3803f..b5fdbcf 100644 --- a/tests/unit/AiWorker.test.ts +++ b/tests/unit/AiWorker.test.ts @@ -278,3 +278,29 @@ describe('AiWorker telemetry emit', () => { expect(failed!.payload.reason).toBe('other'); }); }); + +describe('AiWorker — deletedAt guard (v0.2.3 #4)', () => { + let db: Database.Database; + let repo: NoteRepository; + + beforeEach(() => { + db = new Database(':memory:'); + runMigrations(db); + repo = new NoteRepository(db); + }); + + it('skips notes with deleted_at IS NOT NULL — provider.generate not called', async () => { + const { id } = repo.create({ rawText: 'x' }); + // 먼저 trash — pending_jobs cleanup 됨 + repo.trash(id, '2026-05-01T12:00:00.000Z'); + // 강제로 pending_jobs row 다시 삽입 (race 시뮬레이션 — AiWorker 가 이미 dequeue 한 상태 흉내) + db.prepare(`INSERT INTO pending_jobs (note_id, attempts, next_run_at) VALUES (?, 0, ?)`).run(id, '2026-05-01T12:00:00.000Z'); + const generate = vi.fn(); + const provider = makeProvider({ generate: generate as any }); + const w = new AiWorker(repo, provider, { backoffsMs: [0, 0, 0] }); + await w.loadFromDb(); + await w.drain(); + expect(generate).not.toHaveBeenCalled(); + expect(repo.findById(id)!.aiStatus).toBe('pending'); + }); +});