Files
zyt/server/tests/FollowupAudioAccessTest.php
T

86 lines
5.3 KiB
PHP

<?php
declare(strict_types=1);
namespace app\common\service\followupaudio {
final class FollowupAudioStore
{
public static function task(int $id): array
{
return ['id' => $id, 'diagnosis_id' => 91, 'patient_id' => 101];
}
}
}
namespace {
require dirname(__DIR__) . '/vendor/autoload.php';
$app = new \think\App(dirname(__DIR__) . '/'); $app->initialize();
set_exception_handler(static function (\Throwable $e): void {
fwrite(STDERR, $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL); exit(1);
});
$app->config->set(['default' => 'mysql', 'connections' => ['mysql' => [
'type' => 'mysql', 'hostname' => '127.0.0.1', 'hostport' => 23316,
'database' => 'followup_audio_test', 'username' => 'root', 'password' => 'followup_audio_isolated_test',
'charset' => 'utf8mb4', 'prefix' => 'faa_', 'debug' => false,
]]], 'database');
$db = \think\facade\Db::class;
$tables = [
'admin' => 'id BIGINT PRIMARY KEY, name VARCHAR(30), root INT, disable INT, delete_time BIGINT NULL',
'admin_role' => 'admin_id BIGINT, role_id BIGINT, PRIMARY KEY(admin_id,role_id)',
'admin_dept' => 'admin_id BIGINT, dept_id BIGINT, PRIMARY KEY(admin_id,dept_id)',
'system_role_menu' => 'role_id BIGINT, menu_id BIGINT, PRIMARY KEY(role_id,menu_id)',
'system_menu' => 'id BIGINT PRIMARY KEY, perms VARCHAR(100), is_disable INT',
'tcm_diagnosis' => 'id BIGINT PRIMARY KEY, patient_id BIGINT, assistant_id BIGINT, status INT, delete_time BIGINT NULL',
];
foreach ($tables as $name => $fields) { $db::execute("DROP TABLE IF EXISTS faa_{$name}"); $db::execute("CREATE TABLE faa_{$name} ({$fields}) ENGINE=InnoDB"); }
$db::name('admin')->insertAll([
['id' => 7, 'name' => 'root fixture', 'root' => 1, 'disable' => 0],
['id' => 8, 'name' => 'assistant fixture', 'root' => 0, 'disable' => 0],
['id' => 9, 'name' => 'other fixture', 'root' => 0, 'disable' => 0],
]);
$db::name('admin_role')->insert(['admin_id' => 8, 'role_id' => 2]);
$db::name('system_menu')->insertAll([
['id' => 1, 'perms' => 'tcm.diagnosis/edit', 'is_disable' => 0],
['id' => 2, 'perms' => 'tcm.diagnosis/dailyRecord', 'is_disable' => 0],
]);
$db::name('system_role_menu')->insert(['role_id' => 2, 'menu_id' => 1]);
$db::name('tcm_diagnosis')->insertAll([
['id' => 91, 'patient_id' => 101, 'assistant_id' => 8, 'status' => 1],
['id' => 92, 'patient_id' => 102, 'assistant_id' => 9, 'status' => 1],
]);
$a = \app\common\service\followupaudio\FollowupAudioAccess::class;
$checks = 0;
$ok = function (bool $yes, string $label) use (&$checks): void { if (!$yes) { throw new \RuntimeException($label); } $checks++; };
$deny = function (callable $f, string $label) use ($ok): void {
try { $f(); } catch (\DomainException $e) { $ok(true, $label); return; } $ok(false, $label);
};
$other = new \PDO('mysql:host=127.0.0.1;port=23316;dbname=followup_audio_test;charset=utf8mb4', 'root', 'followup_audio_isolated_test', [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
try {
$ok((int) $a::diagnosis(91, 8, ['root' => 1])['id'] === 91, 'own patient');
$deny(fn () => $a::diagnosis(92, 8, ['root' => 1]), 'forged cached root must fail');
$ok(!$a::canDaily(8, ['root' => 1]), 'missing daily permission');
$deny(fn () => $a::diagnosis(91, 8, [], true), 'daily write denied');
$db::name('system_role_menu')->insert(['role_id' => 2, 'menu_id' => 2]);
$ok($a::canDaily(8, []), 'fresh daily grant');
// A consistent read exists before another connection commits permission revocation.
$db::startTrans(); $db::name('system_role_menu')->select();
$other->exec('DELETE FROM faa_system_role_menu WHERE role_id=2 AND menu_id=2');
$ok(!$a::canDaily(8, []), 'revocation must beat repeatable-read snapshot'); $db::rollback();
$db::startTrans(); $db::name('tcm_diagnosis')->where('id', 91)->find();
$other->exec('UPDATE faa_tcm_diagnosis SET assistant_id=9 WHERE id=91');
$deny(fn () => $a::diagnosis(91, 8, []), 'reassignment must beat repeatable-read snapshot'); $db::rollback();
$other->exec('UPDATE faa_tcm_diagnosis SET assistant_id=8 WHERE id=91');
$db::startTrans(); $db::name('admin')->where('id', 8)->find();
$other->exec('UPDATE faa_admin SET disable=1 WHERE id=8');
$deny(fn () => $a::diagnosis(91, 8, []), 'disabled actor must beat repeatable-read snapshot'); $db::rollback();
$other->exec('UPDATE faa_admin SET disable=0 WHERE id=8');
$ok((int) $a::task(1, 8, [])['id'] === 1, 'task binding before rebind');
$other->exec('UPDATE faa_tcm_diagnosis SET patient_id=999 WHERE id=91');
$deny(fn () => $a::task(1, 8, []), 'historical recording must not follow patient rebind');
$other->exec('UPDATE faa_tcm_diagnosis SET status=0 WHERE id=91');
$deny(fn () => $a::diagnosis(91, 7, ['root' => 1]), 'inactive diagnosis even root');
echo "Followup audio real authorization: {$checks} checks passed (isolated MySQL; current-read revocation/reassignment)\n";
} finally {
try { $db::rollback(); } catch (\Throwable $e) {}
foreach (array_keys($tables) as $name) { $db::execute("DROP TABLE IF EXISTS faa_{$name}"); }
}
}