import { interviewerNode } from './interviewer.node'; import { AIMessage } from '@langchain/core/messages'; function baseState(overrides: any = {}) { return { questions: [{ id: 'q1', questionText: 'What is JS?', keyPoints: ['point1'], dimension: 'llm' }], currentQuestionIndex: 0, shouldFollowUp: false, language: 'en', ...overrides, } as any; } describe('interviewerNode', () => { describe('empty questions handling', () => { it('should return apology message when questions array is empty', async () => { const state = baseState({ questions: [] }); const result = await interviewerNode(state); expect(result.messages).toBeDefined(); expect((result.messages as any)[0].content).toContain("sorry"); }); it('should return apology message when questions is undefined', async () => { const state = baseState({ questions: undefined }); const result = await interviewerNode(state); expect(result.messages).toBeDefined(); expect((result.messages as any)[0].content).toContain("sorry"); }); it('should return Chinese apology when language is zh', async () => { const state = baseState({ questions: [], language: 'zh' }); const result = await interviewerNode(state); expect((result.messages as any)[0].content).toContain('抱歉'); }); it('should return Japanese apology when language is ja', async () => { const state = baseState({ questions: [], language: 'ja' }); const result = await interviewerNode(state); expect((result.messages as any)[0].content).toContain('申し訳'); }); }); describe('question index range checks', () => { it('should return shouldFollowUp: false when currentQuestionIndex >= questions.length', async () => { const state = baseState({ currentQuestionIndex: 5, questions: [{ id: 'q1', questionText: 'Q', keyPoints: ['k'], dimension: 'llm' }] }); const result = await interviewerNode(state); expect(result.shouldFollowUp).toBe(false); }); }); describe('standard question presentation', () => { it('should present the current question', async () => { const result = await interviewerNode(baseState()); expect(result.messages).toBeDefined(); const msg = (result.messages as any)[0].content as string; expect(msg).toContain('Question 1'); expect(msg).toContain('What is JS?'); }); it('should include answer instruction', async () => { const result = await interviewerNode(baseState()); const msg = (result.messages as any)[0].content as string; expect(msg).toContain('answer'); }); it('should use Chinese labels when language is zh', async () => { const state = baseState({ language: 'zh' }); const result = await interviewerNode(state); const msg = (result.messages as any)[0].content as string; expect(msg).toContain('问题'); expect(msg).toContain('回答'); }); it('should use Japanese labels when language is ja', async () => { const state = baseState({ language: 'ja' }); const result = await interviewerNode(state); const msg = (result.messages as any)[0].content as string; expect(msg).toContain('質問'); expect(msg).toContain('回答'); }); }); describe('follow-up mode', () => { it('should use last feedbackHistory message content as follow-up prompt', async () => { const state = baseState({ shouldFollowUp: true, feedbackHistory: [new AIMessage('You need more details')], }); const result = await interviewerNode(state); const msg = (result.messages as any)[0].content as string; expect(msg).toContain('You need more details'); }); it('should reset shouldFollowUp to false after processing', async () => { const state = baseState({ shouldFollowUp: true, feedbackHistory: [new AIMessage('Feedback: More info needed')], }); const result = await interviewerNode(state); expect(result.shouldFollowUp).toBe(false); }); }); });