diff --git a/src/main/services/CaptureService.ts b/src/main/services/CaptureService.ts index e67ae3e..4dc03e2 100644 --- a/src/main/services/CaptureService.ts +++ b/src/main/services/CaptureService.ts @@ -88,9 +88,14 @@ export class CaptureService { async restoreNote(noteId: string): Promise { // 이미 active 인 노트는 telemetry emit skip — restore/trash ratio 오염 방지. - const note = this.repo.findById(noteId); - if (!note || note.deletedAt === null) return; - this.repo.restore(noteId); + const before = this.repo.findById(noteId); + if (!before || before.deletedAt === null) return; + // v0.2.6 #10 — production path: repo.restoreNote (ai_status reset + pending_jobs 재생성) + this.repo.restoreNote(noteId); + // v0.2.6 #10 — in-memory AiWorker queue 갱신: DB 갱신만으로는 다음 앱 실행 시까지 처리 X + if (before.aiStatus === 'failed' || before.aiStatus === 'pending') { + await this.deps.enqueue(noteId); + } if (this.deps.telemetry) { await this.deps.telemetry.emit({ kind: 'restore', payload: { noteId } }).catch(() => {}); } diff --git a/tests/unit/CaptureService.test.ts b/tests/unit/CaptureService.test.ts index 51e19e8..513f9fc 100644 --- a/tests/unit/CaptureService.test.ts +++ b/tests/unit/CaptureService.test.ts @@ -324,6 +324,52 @@ describe('CaptureService.trashExpiredBatch', () => { }); }); +describe('CaptureService.restoreNote — enqueue on failed/pending (#10 production path)', () => { + let db: Database.Database; + let repo: NoteRepository; + let store: MediaStore; + let tmp: string; + let enqueued: string[]; + let svc: CaptureService; + + beforeEach(() => { + db = new Database(':memory:'); + runMigrations(db); + repo = new NoteRepository(db); + tmp = mkdtempSync(join(tmpdir(), 'inkling-restore-')); + store = new MediaStore(tmp); + enqueued = []; + svc = new CaptureService(repo, store, { + enqueue: async (id) => { enqueued.push(id); }, + celebrate: () => {} + }); + }); + + it('restoreNote calls worker.enqueue when restoring failed note', async () => { + const { id } = repo.create({ rawText: 'x' }); + repo.markAiFailed(id, 'unreachable'); + repo.trash(id, new Date().toISOString()); + enqueued.length = 0; // reset + + await svc.restoreNote(id); + + expect(repo.findById(id)!.aiStatus).toBe('pending'); + expect(enqueued).toContain(id); + }); + + it('restoreNote does not enqueue done note', async () => { + const { id } = repo.create({ rawText: 'x' }); + repo.updateAiResult(id, { title: 't', summary: 'a\nb\nc', tags: ['x'], provider: 'p' }); + repo.trash(id, new Date().toISOString()); + enqueued.length = 0; // reset + + await svc.restoreNote(id); + + expect(repo.findById(id)!.aiStatus).toBe('done'); + expect(enqueued).not.toContain(id); + }); +}); + describe('CaptureService.retryAllFailed', () => { let db: Database.Database; let repo: NoteRepository;