feat(db): migration v2 — due_date columns + pre-migration snapshot

ALTER TABLE notes adds due_date TEXT + due_date_edited_by_user INTEGER.
openDb takes <dbFile>.pre-v<N>.bak before running migrations
(F6-L1 follow-up #4 — preserves recoverable state if migration fails).
NoteRepository: updateAiResult accepts dueDate?, setDueDate +
edited-flag CASE WHEN guard mirroring title/summary pattern.
Note interface gains dueDate + dueDateEditedByUser fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
altair823
2026-04-26 11:05:44 +09:00
parent cfd34c352b
commit 0bb6c12bbb
8 changed files with 129 additions and 8 deletions

View File

@@ -126,4 +126,53 @@ describe('NoteRepository', () => {
expect(row.attempts).toBe(1);
expect(row.last_error).toBe('boom');
});
it('hydrate returns dueDate=null + dueDateEditedByUser=false on new note', () => {
const { id } = repo.create({ rawText: 'x' });
const note = repo.findById(id)!;
expect(note.dueDate).toBeNull();
expect(note.dueDateEditedByUser).toBe(false);
});
it('updateAiResult writes dueDate when edited flag is 0', () => {
const { id } = repo.create({ rawText: 'x' });
repo.updateAiResult(id, { title: 'AI 제목', summary: 'a\nb\nc', tags: [], provider: 'p', dueDate: '2026-05-01' });
const note = repo.findById(id)!;
expect(note.dueDate).toBe('2026-05-01');
expect(note.dueDateEditedByUser).toBe(false);
});
it('updateAiResult does NOT overwrite dueDate when edited flag is 1', () => {
const { id } = repo.create({ rawText: 'x' });
repo.updateAiResult(id, { title: 'AI', summary: 'a\nb\nc', tags: [], provider: 'p', dueDate: '2026-05-01' });
repo.setDueDate(id, '2026-05-15');
repo.updateAiResult(id, { title: 'AI 2', summary: 'd\ne\nf', tags: [], provider: 'p', dueDate: '2026-05-30' });
const note = repo.findById(id)!;
expect(note.dueDate).toBe('2026-05-15');
expect(note.dueDateEditedByUser).toBe(true);
});
it('setDueDate sets due_date and edited flag', () => {
const { id } = repo.create({ rawText: 'x' });
repo.setDueDate(id, '2026-06-01');
const note = repo.findById(id)!;
expect(note.dueDate).toBe('2026-06-01');
expect(note.dueDateEditedByUser).toBe(true);
});
it('setDueDate(null) clears due_date but keeps edited flag', () => {
const { id } = repo.create({ rawText: 'x' });
repo.setDueDate(id, '2026-06-01');
repo.setDueDate(id, null);
const note = repo.findById(id)!;
expect(note.dueDate).toBeNull();
expect(note.dueDateEditedByUser).toBe(true);
});
it('updateAiResult without dueDate field treats it as null', () => {
const { id } = repo.create({ rawText: 'x' });
repo.updateAiResult(id, { title: 'AI', summary: 'a\nb\nc', tags: [], provider: 'p' });
const note = repo.findById(id)!;
expect(note.dueDate).toBeNull();
});
});