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:
57
tests/unit/NoteRepository.search.test.ts
Normal file
57
tests/unit/NoteRepository.search.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
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.search — FTS5', () => {
|
||||
let db: Database.Database;
|
||||
let repo: NoteRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
db = new Database(':memory:');
|
||||
db.pragma('foreign_keys = ON');
|
||||
runMigrations(db);
|
||||
repo = new NoteRepository(db);
|
||||
const a = repo.create({ rawText: '오늘 월요일 회의 정리' });
|
||||
repo.updateAiResult(a.id, { title: '회의록', summary: '월요일', tags: ['기획', '회의'], provider: 'p' });
|
||||
const b = repo.create({ rawText: '결재 요청 본문' });
|
||||
repo.updateAiResult(b.id, { title: '결재', summary: '요청서', tags: ['결재'], provider: 'p' });
|
||||
const c = repo.create({ rawText: '버려진 메모' });
|
||||
repo.setStatus(c.id, 'trashed', null);
|
||||
});
|
||||
|
||||
afterEach(() => { db.close(); });
|
||||
|
||||
it('빈 query → 빈 배열', () => {
|
||||
expect(repo.search('')).toEqual([]);
|
||||
expect(repo.search(' ')).toEqual([]);
|
||||
});
|
||||
|
||||
it('keyword 매칭 → hydrated Note', () => {
|
||||
const r = repo.search('월요일');
|
||||
expect(r.length).toBeGreaterThan(0);
|
||||
const titles = r.map((n) => n.aiTitle);
|
||||
expect(titles).toContain('회의록');
|
||||
});
|
||||
|
||||
it('multi-token implicit AND', () => {
|
||||
const r1 = repo.search('회의 월요일');
|
||||
expect(r1.length).toBeGreaterThan(0);
|
||||
const r2 = repo.search('회의 결재'); // 동시 매칭 노트 없음
|
||||
expect(r2).toEqual([]);
|
||||
});
|
||||
|
||||
it('default 는 trashed 제외', () => {
|
||||
const r = repo.search('버려진');
|
||||
expect(r).toEqual([]);
|
||||
});
|
||||
|
||||
it('status filter 명시 시 해당 status 만', () => {
|
||||
const r = repo.search('버려진', { status: 'trashed' });
|
||||
expect(r.length).toBe(1);
|
||||
});
|
||||
|
||||
it('FTS5 special char 안전 처리', () => {
|
||||
expect(() => repo.search('"회의*" (월요일):')).not.toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user