152 lines
8.0 KiB
PHP
152 lines
8.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use app\common\service\TencentImService;
|
|
use think\facade\Config;
|
|
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
require dirname(__DIR__) . '/vendor/topthink/framework/src/helper.php';
|
|
|
|
// Do not initialize the app or load environment/DB settings; HTTP is always replaced.
|
|
$testApp = new think\App();
|
|
$testApp->instance('log', new Psr\Log\NullLogger());
|
|
Config::set(['trtc' => ['sdkAppId' => 123, 'secretKey' => 'account-check-test-key']], 'project');
|
|
|
|
final class AccountCheckHttpFixture extends TencentImService
|
|
{
|
|
public array $responses = [];
|
|
public array $requests = [];
|
|
|
|
protected function httpPost(string $url, string $data, int $timeout = 10)
|
|
{
|
|
parse_str((string) parse_url($url, PHP_URL_QUERY), $query);
|
|
$this->requests[] = [
|
|
'path' => parse_url($url, PHP_URL_PATH), 'query' => $query,
|
|
'data' => json_decode($data, true, 512, JSON_THROW_ON_ERROR), 'timeout' => $timeout,
|
|
];
|
|
if ($this->responses === []) throw new RuntimeException('Unexpected additional HTTP request');
|
|
$response = array_shift($this->responses);
|
|
if ($response instanceof Throwable) throw $response;
|
|
return is_array($response) ? json_encode($response, JSON_THROW_ON_ERROR) : $response;
|
|
}
|
|
|
|
public function importAccount(string $userId, string $nick = '', string $faceUrl = '')
|
|
{
|
|
throw new RuntimeException('Read-only account checks must not import accounts');
|
|
}
|
|
|
|
public function batchImportAccounts(array $accounts): array
|
|
{
|
|
throw new RuntimeException('Read-only account checks must not import accounts');
|
|
}
|
|
}
|
|
|
|
function accountCheckExpect(bool $ok, string $message): void
|
|
{
|
|
if (!$ok) throw new RuntimeException($message);
|
|
}
|
|
|
|
function accountCheckFails(callable $action, string $expected, int $code = 0): void
|
|
{
|
|
try { $action(); }
|
|
catch (RuntimeException $exception) {
|
|
accountCheckExpect(str_contains($exception->getMessage(), $expected), 'Expected ' . $expected . '; got ' . $exception->getMessage());
|
|
accountCheckExpect($exception->getCode() === $code, 'Account-check errors must retain the original code');
|
|
return;
|
|
}
|
|
throw new RuntimeException('Expected account check to fail: ' . $expected);
|
|
}
|
|
|
|
$item = static fn (string $account, string $status): array => [
|
|
'UserID' => $account, 'ResultCode' => 0, 'ResultInfo' => '', 'AccountStatus' => $status,
|
|
];
|
|
$success = static fn (array $items): array => [
|
|
'ActionStatus' => 'OK', 'ErrorCode' => 0, 'ErrorInfo' => '', 'ResultItem' => $items,
|
|
];
|
|
$service = new AccountCheckHttpFixture();
|
|
accountCheckExpect($service->checkAccounts([]) === ['existing' => [], 'missing' => []] && $service->requests === [], 'Empty input makes no network request');
|
|
|
|
$service->responses = [$success([
|
|
$item('doctor_3', 'Imported'), $item('patient_501', 'NotImported'), $item('doctor_1', 'Imported'),
|
|
])];
|
|
$result = $service->checkAccounts(['doctor_1', 'patient_501', 'doctor_3', 'doctor_1']);
|
|
accountCheckExpect($result === ['existing' => ['doctor_1', 'doctor_3'], 'missing' => ['patient_501']], 'Only explicit NotImported is missing; results preserve requested order and deduplicate input');
|
|
accountCheckExpect(count($service->requests) === 1 && $service->requests[0]['timeout'] === 15, 'A check performs exactly one bounded request');
|
|
accountCheckExpect($service->requests[0]['data'] === ['CheckItem' => [
|
|
['UserID' => 'doctor_1'], ['UserID' => 'patient_501'], ['UserID' => 'doctor_3'],
|
|
]], 'Use the official CheckItem/UserID request shape');
|
|
accountCheckExpect($service->requests[0]['query']['sdkappid'] === '123'
|
|
&& $service->requests[0]['query']['identifier'] === 'administrator'
|
|
&& $service->requests[0]['query']['contenttype'] === 'json'
|
|
&& $service->requests[0]['query']['usersig'] !== '', 'Use configured IM app and administrator signature');
|
|
|
|
$hundred = array_map(static fn (int $id): string => 'doctor_' . $id, range(1, 100));
|
|
$service->responses = [$success(array_map(static fn (string $account): array => $item($account, 'Imported'), $hundred))];
|
|
$before = count($service->requests);
|
|
accountCheckExpect($service->checkAccounts($hundred) === ['existing' => $hundred, 'missing' => []]
|
|
&& count($service->requests) === $before + 1 && count($service->requests[$before]['data']['CheckItem']) === 100, 'One hundred accounts fit one official batch');
|
|
accountCheckFails(static fn () => $service->checkAccounts(array_merge($hundred, ['doctor_101'])), '最多支持100');
|
|
accountCheckExpect(count($service->requests) === $before + 1, 'Oversized batch is rejected rather than silently making several requests');
|
|
|
|
foreach ([[''], [' '], [null], [1], [false], [[]]] as $invalidAccounts) {
|
|
$before = count($service->requests);
|
|
accountCheckFails(static fn () => $service->checkAccounts($invalidAccounts), '非空账号字符串');
|
|
accountCheckExpect(count($service->requests) === $before, 'Invalid account input is rejected before HTTP');
|
|
}
|
|
|
|
foreach ([
|
|
['ActionStatus' => 'FAIL', 'ErrorCode' => 70403, 'ErrorInfo' => 'Administrator permission required'],
|
|
['ActionStatus' => 'OK', 'ErrorCode' => 70500, 'ErrorInfo' => 'Server internal error'],
|
|
] as $failure) {
|
|
$service->responses = [$failure];
|
|
$before = count($service->requests);
|
|
accountCheckFails(static fn () => $service->checkAccounts(['doctor_1']), $failure['ErrorInfo'], $failure['ErrorCode']);
|
|
accountCheckExpect(count($service->requests) === $before + 1, 'API failure is visible without inline retries');
|
|
}
|
|
|
|
$service->responses = [$success([
|
|
$item('doctor_1', 'Imported'),
|
|
['UserID' => 'doctor_2', 'ResultCode' => 70169, 'ResultInfo' => 'Per-account timeout', 'AccountStatus' => 'NotImported'],
|
|
])];
|
|
accountCheckFails(static fn () => $service->checkAccounts(['doctor_1', 'doctor_2']), 'Per-account timeout', 70169);
|
|
$service->responses = [$success([
|
|
['UserID' => 'doctor_1', 'ResultCode' => 70202, 'AccountStatus' => 'NotImported'],
|
|
])];
|
|
accountCheckFails(static fn () => $service->checkAccounts(['doctor_1']), 'ResultCode 70202', 70202);
|
|
|
|
foreach ([
|
|
[false, '无响应', 0],
|
|
['', '无响应', 0],
|
|
[new RuntimeException('transport timed out', 28), 'transport timed out', 28],
|
|
[new LogicException('transport refused', 7), 'transport refused', 7],
|
|
['not-json', '响应格式非法', 0],
|
|
['null', '响应格式非法', 0],
|
|
[['ActionStatus' => 'OK', 'ResultItem' => []], 'ErrorCode', 0],
|
|
[['ActionStatus' => 'OK', 'ErrorCode' => '0', 'ResultItem' => []], 'ErrorCode', 0],
|
|
[['ErrorCode' => 0, 'ResultItem' => []], '查询失败', 0],
|
|
[['ActionStatus' => 'OK', 'ErrorCode' => 0], 'ResultItem', 0],
|
|
[$success(['named' => $item('doctor_1', 'Imported')]), 'ResultItem', 0],
|
|
[$success([]), '不完整', 0],
|
|
[$success([null]), 'UserID', 0],
|
|
[$success([['ResultCode' => 0, 'AccountStatus' => 'Imported']]), 'UserID', 0],
|
|
[$success([$item('unrequested', 'Imported')]), '未请求或重复', 0],
|
|
[$success([$item('doctor_1', 'Imported'), $item('doctor_1', 'NotImported')]), '未请求或重复', 0],
|
|
[$success([['UserID' => 'doctor_1', 'CheckResult' => 0, 'AccountStatus' => 'Imported']]), 'ResultCode', 0],
|
|
[$success([['UserID' => 'doctor_1', 'ResultCode' => '0', 'AccountStatus' => 'Imported']]), 'ResultCode', 0],
|
|
[$success([['UserID' => 'doctor_1', 'ResultCode' => 0]]), 'AccountStatus', 0],
|
|
[$success([$item('doctor_1', 'Unknown')]), 'AccountStatus', 0],
|
|
] as [$failure, $error, $code]) {
|
|
$service->responses = [$failure];
|
|
accountCheckFails(static fn () => $service->checkAccounts(['doctor_1']), $error, $code);
|
|
}
|
|
|
|
$service->responses = [$success([$item('doctor_1', 'Imported')])];
|
|
accountCheckFails(static fn () => $service->checkAccounts(['doctor_1', 'doctor_2']), '不完整');
|
|
foreach ($service->requests as $request) {
|
|
accountCheckExpect($request['path'] === '/v4/im_open_login_svc/account_check', 'All requests use the read-only account_check endpoint, never account_import');
|
|
accountCheckExpect($request['timeout'] === 15, 'Every account check has a 15 second timeout');
|
|
}
|
|
|
|
echo "TENCENT_IM_ACCOUNT_CHECK_TEST_OK\n";
|