feat(v0211): ftsHelpers + NoteRepository.search + reviewAggregate
- ftsHelpers: sanitizeFtsQuery (FTS5 special char escape) + computeCutoff (period → KST 자정) - search: notes_fts MATCH + status filter + rank order + sanitize + 빈 query → [] - reviewAggregate: period 별 totalCount/recentNotes(50)/tagCounts(DESC)/dueProgress(passed/pending)
This commit is contained in:
72
tests/unit/NoteRepository.reviewAggregate.test.ts
Normal file
72
tests/unit/NoteRepository.reviewAggregate.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import Database from 'better-sqlite3';
|
||||
import { runMigrations } from '../../src/main/db/migrations/index.js';
|
||||
import { NoteRepository } from '../../src/main/repository/NoteRepository.js';
|
||||
|
||||
describe('NoteRepository.reviewAggregate', () => {
|
||||
let db: Database.Database;
|
||||
let repo: NoteRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
db = new Database(':memory:');
|
||||
db.pragma('foreign_keys = ON');
|
||||
runMigrations(db);
|
||||
repo = new NoteRepository(db);
|
||||
});
|
||||
|
||||
afterEach(() => { db.close(); });
|
||||
|
||||
it('daily — 오늘 KST 자정 이후 노트만 카운트', () => {
|
||||
const now = new Date('2026-05-10T05:00:00Z'); // KST 14:00
|
||||
db.prepare(`INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at, status)
|
||||
VALUES (?, ?, 'done', ?, ?, 'active')`).run('today', '오늘 메모', '2026-05-10T00:30:00Z', '2026-05-10T00:30:00Z');
|
||||
db.prepare(`INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at, status)
|
||||
VALUES (?, ?, 'done', ?, ?, 'active')`).run('yesterday', '어제 메모', '2026-05-09T10:00:00Z', '2026-05-09T10:00:00Z');
|
||||
const r = repo.reviewAggregate('daily', now);
|
||||
expect(r.totalCount).toBe(1);
|
||||
expect(r.recentNotes).toHaveLength(1);
|
||||
expect(r.recentNotes[0]!.id).toBe('today');
|
||||
});
|
||||
|
||||
it('weekly — 7일 전 KST 자정 이후', () => {
|
||||
const now = new Date('2026-05-10T05:00:00Z');
|
||||
db.prepare(`INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at, status)
|
||||
VALUES (?, ?, 'done', ?, ?, 'active')`).run('5dago', '5일 전', '2026-05-05T00:00:00Z', '2026-05-05T00:00:00Z');
|
||||
db.prepare(`INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at, status)
|
||||
VALUES (?, ?, 'done', ?, ?, 'active')`).run('10dago', '10일 전', '2026-04-30T00:00:00Z', '2026-04-30T00:00:00Z');
|
||||
const r = repo.reviewAggregate('weekly', now);
|
||||
expect(r.totalCount).toBe(1);
|
||||
});
|
||||
|
||||
it('trashed 제외', () => {
|
||||
const now = new Date('2026-05-10T05:00:00Z');
|
||||
const a = repo.create({ rawText: '활성' });
|
||||
const b = repo.create({ rawText: '버린' });
|
||||
repo.setStatus(b.id, 'trashed', null);
|
||||
const r = repo.reviewAggregate('monthly', now);
|
||||
expect(r.recentNotes.map((n) => n.id)).toContain(a.id);
|
||||
expect(r.recentNotes.map((n) => n.id)).not.toContain(b.id);
|
||||
});
|
||||
|
||||
it('tagCounts — period 안 노트의 태그만 DESC', () => {
|
||||
const now = new Date('2026-05-10T05:00:00Z');
|
||||
const a = repo.create({ rawText: 'a' });
|
||||
const b = repo.create({ rawText: 'b' });
|
||||
repo.updateAiResult(a.id, { title: 't', summary: 's', tags: ['x', 'y'], provider: 'p' });
|
||||
repo.updateAiResult(b.id, { title: 't', summary: 's', tags: ['x'], provider: 'p' });
|
||||
const r = repo.reviewAggregate('monthly', now);
|
||||
expect(r.tagCounts[0]).toEqual({ tag: 'x', count: 2 });
|
||||
expect(r.tagCounts[1]).toEqual({ tag: 'y', count: 1 });
|
||||
});
|
||||
|
||||
it('dueProgress — passed / pending KST today 기준', () => {
|
||||
const now = new Date('2026-05-10T05:00:00Z');
|
||||
const a = repo.create({ rawText: 'a' });
|
||||
const b = repo.create({ rawText: 'b' });
|
||||
repo.create({ rawText: 'c' }); // due 없음 → 카운트 X
|
||||
repo.setDueDate(a.id, '2026-05-01'); // passed
|
||||
repo.setDueDate(b.id, '2026-05-15'); // pending
|
||||
const r = repo.reviewAggregate('monthly', now);
|
||||
expect(r.dueProgress).toEqual({ total: 2, passed: 1, pending: 1 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user