fix(retry): review round 1 — minor/nit 4건 일괄 (#2 v0.2.3)

m1 — NoteRepository.test.ts 에 retryAllFailed OR IGNORE race-safe 회귀
가드 1 case 추가. failed 노트인데 pending_jobs row 가 이미 존재하는
비정상 race 상태 시뮬레이션 → INSERT OR IGNORE 라 duplicate 안 됨,
기존 attempts/next_run_at 보존.

m2 — store.retryAllFailed 의 r.count 무시 의도 주석 1줄.
단일 process (Electron) 환경 + 모든 ai_status='failed' 가 retry 대상이라
사용자 시점 카운트는 0 reset 가 정확.

n1 — AiWorker unreachableBackoffStep increment 명료화.
Math.min(..., length-1) → 명시적 if 가드 (step < length-1) 로 cap 도달 시
no-op 의도 가시화. 동작 동일.

n2 — AiWorker.processJob 의 max 의미 주석 1줄. unreachable/timeout 분기는
attempt -= 1 로 인덱스 stay 라 max 무관 — future maintainer 위해 명시.

n3 (FailedBanner inline style) 은 v0.2.4 backlog (banner theme cleanup).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
altair823
2026-05-02 03:47:08 +09:00
parent 95bbe9cd22
commit 8f56814186
3 changed files with 24 additions and 1 deletions

View File

@@ -628,6 +628,21 @@ describe('NoteRepository — failed retry helpers', () => {
expect(repo.retryAllFailed('2026-05-01T12:00:00.000Z')).toEqual({ ids: [] });
});
it('retryAllFailed — pending_jobs 이미 존재 시 OR IGNORE (race 안전)', () => {
// failed 노트인데 pending_jobs row 가 이미 존재하는 비정상 race 상태 시뮬레이션.
// attempts=2, next_run_at=과거 — retryAllFailed 가 INSERT OR IGNORE 라 그대로 보존되어야.
const id = makeFailed('a');
db.prepare(`INSERT INTO pending_jobs (note_id, attempts, next_run_at) VALUES (?, 2, ?)`)
.run(id, '2026-04-30T00:00:00.000Z');
const r = repo.retryAllFailed('2026-05-01T12:00:00.000Z');
expect(r.ids).toEqual([id]);
const jobs = repo.getAllPendingJobs().filter((j) => j.noteId === id);
expect(jobs).toHaveLength(1); // duplicate 안 됨
// OR IGNORE 라 기존 row 보존 — attempts=2, nextRunAt 그대로
expect(jobs[0]!.attempts).toBe(2);
expect(jobs[0]!.nextRunAt).toBe('2026-04-30T00:00:00.000Z');
});
it('setNextRunAt — attempts 변경 없이 next_run_at + last_error 갱신', () => {
const { id } = repo.create({ rawText: 'x' });
repo.incrementJobAttempt(id, '2026-05-01T11:00:00.000Z', 'first error');