68 lines
4.0 KiB
PHP
68 lines
4.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
|
|
use app\common\service\prescriptionai\PrescriptionAiPolicy as Policy;
|
|
use app\common\service\prescriptionai\PrescriptionAiCipher as Cipher;
|
|
|
|
$checks = 0;
|
|
$expect = static function (bool $ok, string $message) use (&$checks): void {
|
|
if (!$ok) { throw new RuntimeException($message); }
|
|
$checks++;
|
|
};
|
|
$rx = ['id' => 1, 'diagnosis_id' => 10, 'patient_id' => 20, 'is_system_auto' => 0, 'void_status' => 0,
|
|
'age' => 50, 'prescription_type' => '饮片', 'herbs' => [['medicine_id' => 1, 'name' => '测试药', 'dosage' => 10, 'price' => 2]],
|
|
'aux_usage' => null, 'clinical_diagnosis' => 'fixture'];
|
|
$wire = $rx;
|
|
$wire['age'] = '50';
|
|
$wire['herbs'] = '[{"name":"测试药","medicine_id":"1","dosage":"10.00","price":999}]';
|
|
$wire['aux_usage'] = 'null';
|
|
$wire['audit_status'] = 1;
|
|
$wire['phone'] = '000000';
|
|
$expect(Policy::fingerprint($rx) === Policy::fingerprint($wire), 'JSON numbers, prices, audit and contact fields do not regenerate');
|
|
$changed = $rx;
|
|
$changed['herbs'][0]['dosage'] = 11;
|
|
$expect(Policy::fingerprint($rx) !== Policy::fingerprint($changed), 'dose change regenerates');
|
|
$changed = $rx;
|
|
$changed['herbs'][0]['processing'] = 'special fixture';
|
|
$expect(Policy::fingerprint($rx) !== Policy::fingerprint($changed), 'processing change regenerates');
|
|
foreach (['is_system_auto' => 1, 'void_status' => 1, 'delete_time' => 123, 'herbs' => '[]'] as $key => $value) {
|
|
$expect(!Policy::isManual(array_replace($rx, [$key => $value])), 'ineligible source: ' . $key);
|
|
}
|
|
$expect(Policy::aggregate(['success', 'retry_wait']) === 'running', 'retry remains active');
|
|
$expect(Policy::aggregate(['success', 'failed']) === 'partial', 'one result remains visible');
|
|
$expect(Policy::aggregate(['failed', 'failed']) === 'failed', 'both failures');
|
|
$expect(Policy::aggregate(['success', 'success']) === 'success', 'both results');
|
|
$expect(Policy::retryAt(3, 100, true, 3) === null && Policy::retryAt(1, 100, false, 3) === null, 'bounded and terminal failures');
|
|
$cipher = new Cipher(str_repeat('test-only-key-', 4));
|
|
$secret = ['report' => 'private synthetic fixture'];
|
|
$encrypted = $cipher->encrypt($secret, 'report:1');
|
|
$expect($encrypted !== $cipher->encrypt($secret, 'report:1'), 'fresh IV per encryption');
|
|
$expect($cipher->decrypt($encrypted, 'report:1') === $secret, 'authenticated round trip');
|
|
foreach (['wrong purpose', 'tampered', 'wrong key'] as $case) {
|
|
try {
|
|
$bytes = base64_decode(substr($encrypted, 3));
|
|
$bytes[30] = chr(ord($bytes[30]) ^ 1);
|
|
($case === 'wrong key' ? new Cipher(str_repeat('different-key-', 4)) : $cipher)->decrypt(
|
|
$case === 'tampered' ? 'v1:' . base64_encode($bytes) : $encrypted,
|
|
$case === 'wrong purpose' ? 'report:2' : 'report:1');
|
|
$expect(false, $case . ' rejected');
|
|
} catch (RuntimeException $e) { $checks++; }
|
|
}
|
|
$catalog = [['id' => 1, 'name' => '测试药']];
|
|
$candidate = ['prescription_type' => '饮片', 'dose_basis' => 'per_dose', 'herbs' => [[
|
|
'name' => '测试药', 'dosage' => 10, 'unit' => 'g', 'formula_type' => '主方', 'processing' => '无', 'instructions' => '无',
|
|
]]];
|
|
$compare = \app\common\service\prescriptionai\PrescriptionAiComparison::compare($rx, $candidate, $catalog);
|
|
$expect($compare['score'] === 100.0, 'explicit no additional processing matches catalog identity');
|
|
$candidate['herbs'][0]['processing'] = '未知';
|
|
$expect(\app\common\service\prescriptionai\PrescriptionAiComparison::compare($rx, $candidate, $catalog)['score'] === null, 'unknown processing is not absence');
|
|
$candidate['herbs'][0]['processing'] = '无';
|
|
$candidate['herbs'][0]['instructions'] = '先煎';
|
|
$candidate['herbs'][0]['dosage'] = 4;
|
|
$candidate['herbs'][] = array_replace($candidate['herbs'][0], ['dosage' => 6, 'instructions' => '后下']);
|
|
$expect(\app\common\service\prescriptionai\PrescriptionAiComparison::compare($rx, $candidate, $catalog)['score'] === null, 'different per-herb instructions prohibit duplicate merge');
|
|
echo "Prescription AI policy/cipher: {$checks} checks passed\n";
|