/** * 단계별 지식 스코프 라우터 — 도메인 분류(휴리스틱)와 폴더 조합 규칙 검증. * 소형 모델 정밀 검색의 핵심 계약: 확신 없으면 'general'(전체 두뇌)로 보수적 폴백. */ import { classifyKnowledgeDomain, combineDomainFolders, buildDomainRoutingHint } from '../src/retrieval/domainRouter'; // vscode 는 jest.config.js 의 moduleNameMapper(tests/mocks/vscode.js)가 전역 mock 으로 처리한다. describe('classifyKnowledgeDomain', () => { it('코드 펜스가 있으면 coding 확정', () => { expect(classifyKnowledgeDomain('이 코드 왜 안 돼?\n```js\nconsole.log(x)\n```')).toBe('coding'); }); it('코딩 신호: 에러/파일 확장자/언어명', () => { expect(classifyKnowledgeDomain('TypeError: cannot read properties of undefined 에러가 나요')).toBe('coding'); expect(classifyKnowledgeDomain('sidebarProvider.ts 파일의 함수를 리팩토링해줘')).toBe('coding'); expect(classifyKnowledgeDomain('python으로 API endpoint 구현해줘')).toBe('coding'); }); it('수학 신호: 연산식/백분율/수학 용어', () => { expect(classifyKnowledgeDomain('1234 * 5678 계산해줘')).toBe('math'); expect(classifyKnowledgeDomain('원금 500만원에 연 3.5% 복리 이자면 10년 뒤 얼마야?')).toBe('math'); expect(classifyKnowledgeDomain('주사위 두 개 던져서 합이 7일 확률은?')).toBe('math'); }); it('팩트 신호: 질문사/정의 요청 (다른 도메인 신호가 없을 때만)', () => { expect(classifyKnowledgeDomain('세종대왕이 누구야?')).toBe('facts'); expect(classifyKnowledgeDomain('OLED와 QLED의 차이가 뭐야?')).toBe('facts'); }); it('신호가 없거나 애매하면 general (전체 두뇌 폴백)', () => { expect(classifyKnowledgeDomain('오늘 하루 어땠어?')).toBe('general'); expect(classifyKnowledgeDomain('')).toBe('general'); expect(classifyKnowledgeDomain('고마워!')).toBe('general'); }); it('코딩+팩트 혼합 신호는 전문 도메인(coding)이 이긴다', () => { expect(classifyKnowledgeDomain('git rebase가 뭐야? 설명해줘')).toBe('coding'); }); }); describe('combineDomainFolders — General+Specialty 조합 규칙', () => { const cfg = { general: 'General', math: 'Specialty_Math', coding: '10_Wiki/Topic_Programming', facts: '' }; it('도메인 분류 성공 + Specialty 설정됨 → [General, Specialty]', () => { expect(combineDomainFolders('coding', cfg)).toEqual(['General', '10_Wiki/Topic_Programming']); expect(combineDomainFolders('math', cfg)).toEqual(['General', 'Specialty_Math']); }); it('Specialty 미설정 도메인은 스코프 미적용 (General만으로 좁히지 않음)', () => { expect(combineDomainFolders('facts', cfg)).toEqual([]); }); it('general 도메인은 항상 스코프 미적용 (레거시 전체 검색)', () => { expect(combineDomainFolders('general', cfg)).toEqual([]); }); it('General 미설정이면 Specialty 단독으로 스코프', () => { expect(combineDomainFolders('math', { ...cfg, general: '' })).toEqual(['Specialty_Math']); }); }); describe('buildDomainRoutingHint — 분류 1회로 도구 유도까지', () => { it('math → calculate 위임 지시', () => { expect(buildDomainRoutingHint('math')).toContain(''); expect(buildDomainRoutingHint('math')).toContain('암산'); }); it('coding → run_code 실행 확인 + edit_file 수정 루프 지시', () => { const hint = buildDomainRoutingHint('coding'); expect(hint).toContain(''); }); it('facts → 근거 없으면 확인 불가 / fetch_url', () => { const hint = buildDomainRoutingHint('facts'); expect(hint).toContain('확인 불가'); expect(hint).toContain(''); }); it('general → 힌트 없음 (기존 동작 무간섭)', () => { expect(buildDomainRoutingHint('general')).toBe(''); }); });