feat(notes): findPromotionCandidates — tag threshold default notebook 클러스터
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1256,6 +1256,75 @@ describe('NoteRepository.create with notebook', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('NoteRepository.findPromotionCandidates', () => {
|
||||
let db: Database.Database;
|
||||
let repo: NoteRepository;
|
||||
let defaultId: string;
|
||||
beforeEach(() => {
|
||||
db = new Database(':memory:');
|
||||
db.pragma('foreign_keys = ON');
|
||||
runMigrations(db);
|
||||
repo = new NoteRepository(db);
|
||||
defaultId = (db.prepare(`SELECT id FROM notebooks`).get() as { id: string }).id;
|
||||
});
|
||||
|
||||
function insertWithTag(rawText: string, tagName: string, notebookId?: string): string {
|
||||
const { id } = repo.create({ rawText, notebookId });
|
||||
repo.updateAiResult(id, { title: rawText, summary: 'a\nb\nc', tags: [tagName], provider: 'test', dueDate: null });
|
||||
return id;
|
||||
}
|
||||
|
||||
it('threshold 미만: 빈 결과', () => {
|
||||
insertWithTag('n1', 'mlx-ops');
|
||||
insertWithTag('n2', 'mlx-ops');
|
||||
expect(repo.findPromotionCandidates(defaultId)).toEqual([]);
|
||||
});
|
||||
|
||||
it('threshold 도달: tag 와 noteIds 반환', () => {
|
||||
const a = insertWithTag('n1', 'mlx-ops');
|
||||
const b = insertWithTag('n2', 'mlx-ops');
|
||||
const c = insertWithTag('n3', 'mlx-ops');
|
||||
const r = repo.findPromotionCandidates(defaultId);
|
||||
expect(r).toHaveLength(1);
|
||||
expect(r[0]!.tag).toBe('mlx-ops');
|
||||
expect(r[0]!.noteIds.sort()).toEqual([a, b, c].sort());
|
||||
});
|
||||
|
||||
it('default 가 아닌 notebook 의 노트는 제외', () => {
|
||||
db.prepare(`INSERT INTO notebooks(id,name,created_at,updated_at) VALUES('nb-x','회사','2099-01-01','2099-01-01')`).run();
|
||||
insertWithTag('n1', 'mlx-ops');
|
||||
insertWithTag('n2', 'mlx-ops');
|
||||
insertWithTag('n3', 'mlx-ops', 'nb-x');
|
||||
expect(repo.findPromotionCandidates(defaultId)).toEqual([]);
|
||||
});
|
||||
|
||||
it('completed 제외 — active 만', () => {
|
||||
insertWithTag('n1', 'mlx-ops');
|
||||
insertWithTag('n2', 'mlx-ops');
|
||||
const c = insertWithTag('n3', 'mlx-ops');
|
||||
repo.setStatus(c, 'completed', null);
|
||||
expect(repo.findPromotionCandidates(defaultId)).toEqual([]);
|
||||
});
|
||||
|
||||
it('threshold 인자로 cap 조절 가능', () => {
|
||||
insertWithTag('n1', 'mlx-ops');
|
||||
insertWithTag('n2', 'mlx-ops');
|
||||
expect(repo.findPromotionCandidates(defaultId, 2)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('여러 tag cluster 가 모두 반환', () => {
|
||||
insertWithTag('a1', 'mlx-ops');
|
||||
insertWithTag('a2', 'mlx-ops');
|
||||
insertWithTag('a3', 'mlx-ops');
|
||||
insertWithTag('b1', 'keycloak');
|
||||
insertWithTag('b2', 'keycloak');
|
||||
insertWithTag('b3', 'keycloak');
|
||||
const r = repo.findPromotionCandidates(defaultId);
|
||||
expect(r).toHaveLength(2);
|
||||
expect(r.map((c) => c.tag).sort()).toEqual(['keycloak', 'mlx-ops']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('NoteRepository.list / countByStatus with notebookId', () => {
|
||||
let db: Database.Database;
|
||||
let repo: NoteRepository;
|
||||
|
||||
Reference in New Issue
Block a user