llmAnthropic.test.ts
2,674 bytes
| 1 | import { describe, expect, it } from 'vitest' |
|---|---|
| 2 | import { initialCoverage, type Segment } from '../../shared/types' |
| 3 | import { buildInterviewSystemPrompt, buildInterviewUserMessage } from './llmAnthropic' |
| 4 | import type { InterviewContext } from './types' |
| 5 | |
| 6 | function segment(id: string, speaker: Segment['speaker'], text: string): Segment { |
| 7 | return { id, ts: new Date().toISOString(), speaker, text } |
| 8 | } |
| 9 | |
| 10 | describe('buildInterviewSystemPrompt', () => { |
| 11 | it('names the weakest category as the required target of the next question', () => { |
| 12 | const context: InterviewContext = { |
| 13 | summary: '', |
| 14 | segments: [], |
| 15 | coverage: { ...initialCoverage(), goal: 'clear' }, |
| 16 | } |
| 17 | const prompt = buildInterviewSystemPrompt(context) |
| 18 | expect(prompt).toContain('weakest category right now is "users"') |
| 19 | }) |
| 20 | |
| 21 | it('states every category is covered when nothing is weak', () => { |
| 22 | const allClear = Object.fromEntries( |
| 23 | Object.keys(initialCoverage()).map((c) => [c, 'clear']), |
| 24 | ) as ReturnType<typeof initialCoverage> |
| 25 | const context: InterviewContext = { summary: '', segments: [], coverage: allClear } |
| 26 | expect(buildInterviewSystemPrompt(context)).toContain('Every category is already "clear"') |
| 27 | }) |
| 28 | |
| 29 | it('instructs the exact-match "done" rule and the contradiction rule', () => { |
| 30 | const context: InterviewContext = { summary: '', segments: [], coverage: initialCoverage() } |
| 31 | const prompt = buildInterviewSystemPrompt(context) |
| 32 | expect(prompt).toContain('exactly "done"') |
| 33 | expect(prompt).toContain('contradiction') |
| 34 | }) |
| 35 | |
| 36 | it('instructs plain, jargon-free language for a non-technical user', () => { |
| 37 | const context: InterviewContext = { summary: '', segments: [], coverage: initialCoverage() } |
| 38 | const prompt = buildInterviewSystemPrompt(context) |
| 39 | expect(prompt).toContain('no jargon') |
| 40 | expect(prompt).toContain('may not be a developer') |
| 41 | }) |
| 42 | }) |
| 43 | |
| 44 | describe('buildInterviewUserMessage', () => { |
| 45 | it('includes the running summary and every recent segment with its id and speaker', () => { |
| 46 | const context: InterviewContext = { |
| 47 | summary: 'earlier: building a todo app', |
| 48 | segments: [segment('S1', 'user', 'it should sync across devices')], |
| 49 | coverage: initialCoverage(), |
| 50 | } |
| 51 | const message = buildInterviewUserMessage(context) |
| 52 | expect(message).toContain('earlier: building a todo app') |
| 53 | expect(message).toContain('[S1] user: it should sync across devices') |
| 54 | }) |
| 55 | |
| 56 | it('omits the summary section when there is no summary yet', () => { |
| 57 | const context: InterviewContext = { summary: '', segments: [], coverage: initialCoverage() } |
| 58 | expect(buildInterviewUserMessage(context)).not.toContain('Running summary') |
| 59 | }) |
| 60 | }) |
| 61 | |