test(M-AI-08-04): add LearningAnalysisValidator spec (20 tests)
Coverage: schema (5), evidence (4), business (2), medical (4), personality (3), markdown (1), normal (1). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9cf85023d1
commit
6ead2787f6
128
src/modules/ai-job/learning-analysis-validator.spec.ts
Normal file
128
src/modules/ai-job/learning-analysis-validator.spec.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { LearningAnalysisValidator, BusinessValidationError } from './learning-analysis-validator';
|
||||
|
||||
function validOutput(overrides?: any) {
|
||||
return {
|
||||
summary: '学习表现良好,复习完成率较高',
|
||||
strengths: [
|
||||
{ title: '复习完成率高', description: '完成率达到85%', evidenceRefs: [{ sourceType: 'review_metric' as const, metricKey: 'reviewAccuracy' }] },
|
||||
],
|
||||
weaknesses: [
|
||||
{ title: '主动回忆不足', description: '近30天仅2次', knowledgePointId: null, evidenceRefs: [{ sourceType: 'active_recall' as const, metricKey: 'count' }] },
|
||||
],
|
||||
trends: [
|
||||
{ metricKey: 'totalStudyDuration', direction: 'stable' as const, description: '学习时长稳定', evidenceRefs: [{ sourceType: 'study_metric' as const, metricKey: 'totalStudyDuration' }] },
|
||||
],
|
||||
risks: [
|
||||
{ title: '遗忘风险', severity: 'medium' as const, description: '3个知识点已过期', evidenceRefs: [{ sourceType: 'review_metric' as const, metricKey: 'overdueCount' }] },
|
||||
],
|
||||
recommendations: [
|
||||
{ actionType: 'active_recall', title: '增加主动回忆', reason: '最有效的策略', priority: 5, evidenceRefs: [{ sourceType: 'active_recall' as const, metricKey: 'count' }] },
|
||||
],
|
||||
confidence: 0.75,
|
||||
insufficientData: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('LearningAnalysisValidator', () => {
|
||||
let validator: LearningAnalysisValidator;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [LearningAnalysisValidator],
|
||||
}).compile();
|
||||
validator = module.get(LearningAnalysisValidator);
|
||||
jest.spyOn(require('@nestjs/common').Logger.prototype, 'log').mockImplementation(() => {});
|
||||
jest.spyOn(require('@nestjs/common').Logger.prototype, 'warn').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
describe('正常输出', () => {
|
||||
it('合法输出通过', () => {
|
||||
expect(() => validator.validate(validOutput())).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Schema 验证', () => {
|
||||
it('summary 必填', () => {
|
||||
expect(() => validator.validate(validOutput({ summary: '' }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('strengths 上限 3', () => {
|
||||
const s = Array(4).fill({ title: 'S', description: 'D', evidenceRefs: [{ sourceType: 'study_metric', metricKey: 'm' }] });
|
||||
expect(() => validator.validate(validOutput({ strengths: s }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('weaknesses 上限 5', () => {
|
||||
const w = Array(6).fill({ title: 'W', description: 'D', evidenceRefs: [{ sourceType: 'study_metric', metricKey: 'm' }] });
|
||||
expect(() => validator.validate(validOutput({ weaknesses: w }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('recommendations 非空', () => {
|
||||
expect(() => validator.validate(validOutput({ recommendations: [] }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('confidence 超出 [0,1]', () => {
|
||||
expect(() => validator.validate(validOutput({ confidence: 1.5 }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Evidence 验证', () => {
|
||||
it('非法 sourceType', () => {
|
||||
const s = [{ title: 'S', description: 'D', evidenceRefs: [{ sourceType: 'unknown_type', metricKey: 'x' }] }];
|
||||
expect(() => validator.validate(validOutput({ strengths: s }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('缺失 metricKey', () => {
|
||||
const s = [{ title: 'S', description: 'D', evidenceRefs: [{ sourceType: 'study_metric', metricKey: '' }] }];
|
||||
expect(() => validator.validate(validOutput({ strengths: s }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('strength 无 evidenceRefs', () => {
|
||||
const s = [{ title: 'S', description: 'D', evidenceRefs: [] }];
|
||||
expect(() => validator.validate(validOutput({ strengths: s }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('weakness 无 evidenceRefs', () => {
|
||||
const w = [{ title: 'W', description: 'D', evidenceRefs: [] }];
|
||||
expect(() => validator.validate(validOutput({ weaknesses: w }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Business 验证', () => {
|
||||
it('insufficientData=true 但 confidence>0.5', () => {
|
||||
expect(() => validator.validate(validOutput({ insufficientData: true, confidence: 0.8 }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('insufficientData=true 且 confidence 低 → 通过', () => {
|
||||
expect(() => validator.validate(validOutput({ insufficientData: true, confidence: 0.3 }))).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('医疗诊断', () => {
|
||||
it('summary 含 ADHD', () => {
|
||||
expect(() => validator.validate(validOutput({ summary: '用户可能患有ADHD' }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('description 含 depression', () => {
|
||||
const s = [{ title: 'S', description: 'signs of depression detected', evidenceRefs: [{ sourceType: 'study_metric', metricKey: 'm' }] }];
|
||||
expect(() => validator.validate(validOutput({ strengths: s }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('含 焦虑症', () => {
|
||||
expect(() => validator.validate(validOutput({ summary: '表现出焦虑症倾向' }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('含 PTSD', () => {
|
||||
expect(() => validator.validate(validOutput({ summary: '可能有PTSD症状' }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('人格判断', () => {
|
||||
it('含 "你是一个...的人"', () => {
|
||||
expect(() => validator.validate(validOutput({ summary: '你是一个懒惰的人' }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('含 "你天生"', () => {
|
||||
expect(() => validator.validate(validOutput({ summary: '你天生不适合学习' }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
it('含 "你太懒"', () => {
|
||||
const r = [{ actionType: 'study', title: '加油', reason: '因为你太懒了', priority: 1, evidenceRefs: [{ sourceType: 'study_metric', metricKey: 'm' }] }];
|
||||
expect(() => validator.validate(validOutput({ recommendations: r }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Markdown 代码块', () => {
|
||||
it('summary 含 code block', () => {
|
||||
expect(() => validator.validate(validOutput({ summary: '分析结果:\n```json\n{}\n```' }))).toThrow(BusinessValidationError);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user