import { Test, TestingModule } from '@nestjs/testing'; import { ConfigService } from '@nestjs/config'; import { DietAnalysisService } from './diet-analysis.service'; import { DietRecordsService } from '../../diet-records/diet-records.service'; describe('DietAnalysisService - Text Analysis', () => { let service: DietAnalysisService; let mockDietRecordsService: Partial; let mockConfigService: Partial; beforeEach(async () => { // Mock services mockDietRecordsService = { addDietRecord: jest.fn().mockResolvedValue({}), getDietHistory: jest.fn().mockResolvedValue({ total: 0, records: [] }), getRecentNutritionSummary: jest.fn().mockResolvedValue({ recordCount: 0, totalCalories: 0, totalProtein: 0, totalCarbohydrates: 0, totalFat: 0, totalFiber: 0 }) }; mockConfigService = { get: jest.fn().mockImplementation((key: string) => { switch (key) { case 'DASHSCOPE_API_KEY': return 'test-api-key'; case 'DASHSCOPE_BASE_URL': return 'https://test-api.com'; case 'DASHSCOPE_VISION_MODEL': return 'test-model'; default: return undefined; } }) }; const module: TestingModule = await Test.createTestingModule({ providers: [ DietAnalysisService, { provide: DietRecordsService, useValue: mockDietRecordsService }, { provide: ConfigService, useValue: mockConfigService }, ], }).compile(); service = module.get(DietAnalysisService); }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('buildTextDietAnalysisPrompt', () => { it('should build a proper prompt for text analysis', () => { // 通过反射访问私有方法进行测试 const prompt = (service as any).buildTextDietAnalysisPrompt('breakfast'); expect(prompt).toContain('作为专业营养分析师'); expect(prompt).toContain('breakfast'); expect(prompt).toContain('shouldRecord'); expect(prompt).toContain('confidence'); expect(prompt).toContain('extractedData'); expect(prompt).toContain('analysisText'); }); }); describe('Text diet analysis scenarios', () => { const testCases = [ { description: '应该识别简单的早餐描述', input: '今天早餐吃了一碗燕麦粥', expectedFood: '燕麦粥', shouldRecord: true }, { description: '应该识别午餐描述', input: '午餐点了一份鸡胸肉沙拉', expectedFood: '鸡胸肉沙拉', shouldRecord: true }, { description: '应该识别零食描述', input: '刚吃了两个苹果当零食', expectedFood: '苹果', shouldRecord: true }, { description: '不应该记录模糊的描述', input: '今天吃得不错', shouldRecord: false } ]; testCases.forEach(testCase => { it(testCase.description, () => { // 这里我们主要测试prompt构建逻辑 // 实际的AI调用需要真实的API密钥,在单元测试中我们跳过 const prompt = (service as any).buildTextDietAnalysisPrompt('breakfast'); expect(prompt).toBeDefined(); expect(typeof prompt).toBe('string'); expect(prompt.length).toBeGreaterThan(100); }); }); }); describe('processDietRecord', () => { it('should handle text-based diet records without image URL', async () => { const mockAnalysisResult = { shouldRecord: true, confidence: 85, extractedData: { foodName: '燕麦粥', mealType: 'breakfast' as any, portionDescription: '1碗', estimatedCalories: 200, proteinGrams: 8, carbohydrateGrams: 35, fatGrams: 3, fiberGrams: 4, nutritionDetails: { mainIngredients: ['燕麦'], cookingMethod: '煮制', foodCategories: ['主食'] } }, analysisText: '识别到燕麦粥' }; const result = await service.processDietRecord('test-user-id', mockAnalysisResult); expect(result).toBeDefined(); expect(result?.foodName).toBe('燕麦粥'); expect(result?.source).toBe('manual'); // 文本记录应该是manual源 expect(result?.imageUrl).toBeUndefined(); expect(mockDietRecordsService.addDietRecord).toHaveBeenCalledWith('test-user-id', expect.objectContaining({ foodName: '燕麦粥', source: 'manual' })); }); it('should handle image-based diet records with image URL', async () => { const mockAnalysisResult = { shouldRecord: true, confidence: 90, extractedData: { foodName: '鸡胸肉沙拉', mealType: 'lunch' as any, portionDescription: '1份', estimatedCalories: 300, proteinGrams: 25, carbohydrateGrams: 10, fatGrams: 15, fiberGrams: 5, nutritionDetails: { mainIngredients: ['鸡胸肉', '生菜'], cookingMethod: '生食', foodCategories: ['蛋白质', '蔬菜'] } }, analysisText: '识别到鸡胸肉沙拉' }; const result = await service.processDietRecord('test-user-id', mockAnalysisResult, 'https://example.com/image.jpg'); expect(result).toBeDefined(); expect(result?.foodName).toBe('鸡胸肉沙拉'); expect(result?.source).toBe('vision'); // 有图片URL应该是vision源 expect(result?.imageUrl).toBe('https://example.com/image.jpg'); }); }); });