This commit is contained in:
Your Name
2026-10-17 16:51:48 +08:00
parent 56f85f7d89
commit ba2e2e4087
41 changed files with 2851 additions and 96 deletions
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
// Pure policy normalization and presentation checks: no App initialize, .env, database or network.
require dirname(__DIR__) . '/vendor/autoload.php';
use app\common\service\prescriptionai\PrescriptionAiVisibility as Visibility;
$checks = 0;
$expect = static function (bool $condition, string $message) use (&$checks): void {
if (!$condition) { throw new RuntimeException($message); }
$checks++;
};
$expect(Visibility::normalizePolicy(['role_ids' => ['7', 2, 7], 'admin_ids' => ['9', 3]])
=== ['role_ids' => [2, 7], 'admin_ids' => [3, 9]], 'IDs normalize, deduplicate and sort');
$expect(Visibility::normalizePolicy(['role_ids' => [], 'admin_ids' => []])
=== ['role_ids' => [], 'admin_ids' => []], 'empty policy stays empty');
foreach ([[], ['role_ids' => []], ['role_ids' => [], 'admin_ids' => [], 'root' => true],
['role_ids' => '*', 'admin_ids' => []], ['role_ids' => [true], 'admin_ids' => []],
['role_ids' => [1.0], 'admin_ids' => []], ['role_ids' => [-1], 'admin_ids' => []],
['role_ids' => ['01'], 'admin_ids' => []], ['role_ids' => ['1,2'], 'admin_ids' => []],
['role_ids' => ['id' => 1], 'admin_ids' => []], ['role_ids' => [PHP_INT_MAX], 'admin_ids' => []],
['role_ids' => array_fill(0, Visibility::MAX_ROLES + 1, 1), 'admin_ids' => []]] as $invalid) {
try { Visibility::normalizePolicy($invalid); $expect(false, 'invalid policy must fail'); }
catch (DomainException $e) { $checks++; }
}
$sentinel = 'private-agreement-sentinel';
$body = ['report' => ['summary' => 'clinical report', 'comparison' => ['reason' => $sentinel], 'raw' => [$sentinel],
'unknown_debug' => ['normalization' => $sentinel]],
'candidate' => ['status' => 'available_for_review', 'reason' => 'clinical reasoning', 'herbs' => [['name' => '测试药材']]],
'comparison' => ['score' => 88, 'normalization' => ['matched_contribution_sum' => 2]],
'coverage' => ['status' => 'complete'], 'usage' => ['total_calls' => 2],
'knowledge_source' => ['title' => 'clinical reference'], 'ancient_book_sources' => [['title' => 'book']],
'score' => $sentinel, 'result' => [$sentinel], 'raw' => [$sentinel], 'body' => [$sentinel],
'progress' => ['stage' => 'poisoned']];
$public = Visibility::reportBody($body, false);
$expect(!str_contains(json_encode($public), $sentinel) && !isset($public['comparison']), 'comparison and unknown raw body paths cannot escape');
$expect($public['report']['summary'] === 'clinical report' && $public['candidate']['reason'] === 'clinical reasoning'
&& $public['coverage']['status'] === 'complete', 'report, candidate reasoning and coverage survive');
$private = Visibility::reportBody($body, true);
$expect($private['comparison']['score'] === 88 && !isset($private['result']) && !isset($private['progress']),
'authorized comparison survives but arbitrary body cannot override response fields');
$expect(!isset($private['report']['unknown_debug']) && !isset($private['report']['raw']),
'known clinical report fields are projected at every depth for all viewers');
$batch = ['comparison_type' => $sentinel, 'baseline_eligible' => true, 'baseline_exclusion_reasons' => [$sentinel],
'models' => ['qwen' => ['status' => 'success', 'score' => 88, 'herb_score' => 90,
'reason' => $sentinel, 'comparison_status' => 'comparable', 'comparison_reason_code' => $sentinel,
'algorithm_version' => $sentinel, 'dictionary_version' => $sentinel, 'error_code' => '',
'comparison' => ['rows' => [$sentinel]], 'progress' => ['stage' => 'completed']] + $public,
'openai' => ['status' => 'failed', 'error_code' => 'UPSTREAM_TIMEOUT', 'reason' => '模型请求超时']]];
$hidden = Visibility::projectBatch($batch, false);
$expect($hidden['agreement_visible'] === false && !str_contains(json_encode($hidden), $sentinel), 'whole batch removes all metric aliases and derived fields');
$expect($hidden['models']['openai']['reason'] === '模型请求超时'
&& $hidden['models']['qwen']['progress']['stage'] === 'completed', 'failure explanation and trusted progress remain');
$expect(Visibility::projectBatch($batch, true)['models']['qwen']['score'] === 88, 'authorized stored metric is unchanged');
$expect(Visibility::projectBatch(['models' => []], false) === ['models' => [], 'agreement_visible' => false], 'empty batch advertises capability');
echo "Prescription AI visibility: {$checks} offline checks passed\n";