import { buildSynthesisPrompt } from '../src/features/datacollect/prompts/synthesisPrompt'; // handlers.ts 는 vscode·bridge 의존이 커서 순수 함수만 골라 검증한다 // (vscode 는 전역 moduleNameMapper mock 이 처리 — 로컬 jest.mock 금지). import { parseBenchmarkArgs, BENCHMARK_MODE_PARTS } from '../src/features/datacollect/handlers'; const SCAN = { url: 'https://interiorai.com', meta: { title: 'InteriorAI', description: 'Fire your interior designer', lang: 'en' }, design: { colors: { palette: [{ value: 'rgb(10,10,10)', count: 40 }] }, typography: { primaryFont: 'Inter' } }, microcopy: { headline: 'Fire your interior designer', ctaSamples: ['Get started $29/mo'] }, structure: { h1: 'Fire your interior designer' }, sitemap: { totalPages: 3, crawlDepth: 1, ascii: '/', pages: [] }, }; describe('parseBenchmarkArgs — 모드 파싱', () => { test('기본 모드는 full (디자인+비즈니스)', () => { const r = parseBenchmarkArgs('https://a.com depth=2 pages=10 부동산 쪽 변형'); expect(r).toMatchObject({ url: 'https://a.com', depth: 2, pages: 10, mode: 'full', userContent: '부동산 쪽 변형' }); }); test('bare 키워드와 mode= 표기를 모두 받는다', () => { expect(parseBenchmarkArgs('https://a.com build').mode).toBe('build'); expect(parseBenchmarkArgs('https://a.com mode=biz').mode).toBe('biz'); expect(parseBenchmarkArgs('https://a.com mode:design').mode).toBe('design'); }); test('모드 키워드는 보조 설명에 섞이지 않는다', () => { const r = parseBenchmarkArgs('https://a.com biz 인테리어 시장 분석'); expect(r.mode).toBe('biz'); expect(r.userContent).toBe('인테리어 시장 분석'); }); test('URL 없으면 url 빈 문자열', () => { expect(parseBenchmarkArgs('design').url).toBe(''); }); }); describe('BENCHMARK_MODE_PARTS — 모드별 파트 구성', () => { test('full=1~4, design=1~3, biz=4만, build=1~5', () => { expect(BENCHMARK_MODE_PARTS.full).toEqual([1, 2, 3, 4]); expect(BENCHMARK_MODE_PARTS.design).toEqual([1, 2, 3]); expect(BENCHMARK_MODE_PARTS.biz).toEqual([4]); expect(BENCHMARK_MODE_PARTS.build).toEqual([1, 2, 3, 4, 5]); }); }); describe('buildSynthesisPrompt — 파트 4·5 템플릿', () => { test('파트 4는 비즈니스 렌즈 섹션(포지셔닝·가격·MVP·운영)을 요구한다', () => { const p = buildSynthesisPrompt(SCAN, '', 4); expect(p).toContain('10. 서비스 정의 & 포지셔닝'); expect(p).toContain('11. 가격 & 수익 구조'); expect(p).toContain('12. 기능 인벤토리'); expect(p).toContain('13. 운영 & 방향성 제안'); expect(p).toContain('MVP 컷'); expect(p).toContain('손익분기'); // 날조 금지 — 스캔에 없는 가격을 지어내지 않도록. expect(p).toContain('스캔 데이터 부족'); }); test('파트 4에서만 사용자 컨텍스트 결합 변형을 지시한다', () => { const p4 = buildSynthesisPrompt(SCAN, '부동산 중개용으로 변형', 4); expect(p4).toContain('사용자 컨텍스트 결합 변형'); expect(p4).toContain('부동산 중개용으로 변형'); const p3 = buildSynthesisPrompt(SCAN, '부동산 중개용으로 변형', 3); expect(p3).toContain('재해석·확장·전환하지 말 것'); // 원본 복원 원칙은 그대로 }); test('파트 5는 자기완결적 스텝 프롬프트와 실측 토큰 명시를 요구한다', () => { const p = buildSynthesisPrompt(SCAN, '', 5); expect(p).toContain('단계별 빌드 프롬프트'); expect(p).toContain('STEP 1'); expect(p).toContain('STEP 4'); expect(p).toContain('자기완결적'); expect(p).toContain('빌드 순서 요약'); }); test('파트 1~3 기존 템플릿은 변하지 않았다', () => { expect(buildSynthesisPrompt(SCAN, '', 1)).toContain('1. 시각적 정체성'); expect(buildSynthesisPrompt(SCAN, '', 2)).toContain('8. 페이지 템플릿 맵'); expect(buildSynthesisPrompt(SCAN, '', 3)).toContain('9. 원본 사이트 재구축 명세'); }); });