Files
plates-server/src/ai-coach/services/diet-analysis.service.spec.ts

174 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<DietRecordsService>;
let mockConfigService: Partial<ConfigService>;
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>(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');
});
});
});