71, 'sleep_condition' => '睡眠一般', 'stool_condition' => '', 'current_medications' => null, 'appetite' => '口干;大便未记录;否认腹痛', 'family_history' => '无', 'allergy_history_text' => '否认药物过敏史', 'tongue' => '本次舌淡红苔少', 'tongue_text' => '', 'pulse' => '本次脉细弱', 'pulse_text' => null, 'tongue_images' => [], 'status' => 0, 'show_card' => false]; $submission = AiRequiredSource::submission(['diagnoses' => [$row]]); $present = sourceStateSection($submission, 'present_illness')[0]; sourceStateExpect($present['sleep_condition'] === '睡眠一般', 'Actual observation is verbatim'); sourceStateExpect($present['stool_condition'] === '', 'Missing scalar does not become 无'); sourceStateExpect($present['current_medications'] === null, 'Unknown medication is not an assertion of no medication'); sourceStateExpect($present['appetite'] === $row['appetite'], 'Mixed positive, unknown and denial text stays whole'); sourceStateExpect($present['weight_change'] === null, 'Absent field remains null'); $other = sourceStateSection($submission, 'other_history')[0]; sourceStateExpect($other['family_history'] === '无', 'An actual raw source string is never rewritten'); sourceStateExpect($other['allergy_history_text'] === '否认药物过敏史', 'Explicit denial alias survives'); $exam = sourceStateSection($submission, 'diagnosis_info')[0]; sourceStateExpect($exam['tongue_images'] === [], 'Empty attachment array never becomes an invented reference'); $basic = sourceStateSection($submission, 'basic_info')[0]; sourceStateExpect($basic['status'] === 0 && $basic['show_card'] === false, 'Nonclinical zero and false retain type'); $guard = TcmEvidenceGuard::assess($row, $submission); sourceStateExpect(!$guard['restricted'], 'Empty aliases do not erase complete raw tongue and pulse'); sourceStateExpect($guard['current_tongue'] === $row['tongue'] && $guard['current_pulse'] === $row['pulse'], 'Actual source exams are unchanged'); sourceStateExpect(str_contains(json_encode($guard['facts'], JSON_UNESCAPED_UNICODE), '口干;大便未记录;否认腹痛'), 'Source-only evidence retains mixed source text'); $enrich = (new ReflectionClass(PatientAiReportLogic::class))->getMethod('enrichDiagnosisRows'); $binary = ['trauma_history', 'surgery_history', 'allergy_history', 'family_history', 'pregnancy_history']; $coded = $enrich->invoke(null, [array_fill_keys($binary, 0), array_fill_keys($binary, 1), ['family_history' => 0, 'family_history_text' => '本次询问明确否认家族史'], ['allergy_history' => '否认药物过敏史']]); foreach ($binary as $field) { sourceStateExpect($coded[0][$field] === 0 && $coded[0][$field . '_text'] === '未记录', 'Default zero is not confirmed negative'); sourceStateExpect($coded[1][$field] === 1 && $coded[1][$field . '_text'] === '有', 'Positive legacy flag is retained'); } sourceStateExpect($coded[2]['family_history_text'] === '本次询问明确否认家族史', 'Enrichment never replaces recorded interview text'); sourceStateExpect($coded[3]['allergy_history'] === '否认药物过敏史', 'Raw textual denial remains unchanged'); foreach ([null, '', 'unknown', false, 'invalid'] as $value) { $result = $enrich->invoke(null, [['gender' => $value]]); sourceStateExpect($result[0]['gender_text'] === '未知', 'Blank or invalid gender is not cast to female'); } $gender = (new ReflectionClass(DiagnosisAiLogic::class))->getMethod('genderText'); foreach ([0 => '女', 1 => '男'] as $value => $label) { foreach ([$value, (string) $value] as $encoded) { $result = $enrich->invoke(null, [['gender' => $encoded]]); sourceStateExpect($result[0]['gender_text'] === $label && $gender->invoke(null, $encoded) === $label, 'Demographics agree with defined source codes'); } } $safe = (new ReflectionClass(PatientAiReportLogic::class))->getMethod('sanitizeSnapshotForUpstream') ->invoke(null, $submission); $safeExam = sourceStateSection($safe, 'diagnosis_info')[0]; sourceStateExpect($safeExam['tongue_images']['attachment_count'] === 0, 'Sanitization counts empty attachments correctly'); $safePresent = sourceStateSection($safe, 'present_illness')[0]; sourceStateExpect($safePresent['current_medications'] === null && $safePresent['stool_condition'] === '', 'Transport projection keeps field absence distinct from denial'); echo "AI source state preservation: {$checks} assertions passed (pure, no network/database).\n";