Coverage: schema (5), evidence (4), business (2), medical (4), personality (3), markdown (1), normal (1). Co-Authored-By: Claude <noreply@anthropic.com>
129 lines
6.2 KiB
TypeScript
129 lines
6.2 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|