load(); \support\App::loadAllConfig(['route']); use app\miniapi\exception\ApiException; use app\miniapi\service\SmsbaoSender; use app\miniapi\service\SmsTransportFixture; use Monolog\Handler\TestHandler; use support\Log; $checks = 0; function check(bool $condition, string $label): void { global $checks; $checks++; if (!$condition) { throw new RuntimeException($label); } } function settings(array $changes): void { $property = new ReflectionProperty(\Webman\Config::class, 'config'); $all = $property->getValue(); $all['mini_api'] = array_replace($all['mini_api'], $changes); $property->setValue(null, $all); (new ReflectionProperty(\Webman\Config::class, 'flatCache'))->setValue(null, []); } function rejects(callable $action, int $expectedCode): void { try { $action(); } catch (ApiException $exception) { check($exception->getCode() === $expectedCode, 'Expected API error ' . $expectedCode); return; } throw new RuntimeException('Expected API rejection'); } $original = config('mini_api'); $handler = new TestHandler(\Monolog\Logger::DEBUG, false); $logger = Log::channel(); $logger->pushHandler($handler); try { $sender = new SmsbaoSender(); $password = md5('fixture-password'); settings([ 'sms_endpoint' => 'https://api.smsbao.com/sms', 'sms_user' => 'fixture-user', 'sms_password' => $password, 'sms_signature' => '', ]); rejects(fn () => $sender->send('13800138000', '验证码123456'), 50301); check(SmsTransportFixture::$url === '', 'Missing signature prevents the provider request'); settings(['sms_signature' => '云泉智配']); $sender->send('13800138000', '验证码123456'); parse_str((string) parse_url(SmsTransportFixture::$url, PHP_URL_QUERY), $query); check($query['p'] === $password, 'Configured MD5 password is not hashed twice'); check($query['c'] === '【云泉智配】验证码123456', 'Content includes the configured company signature'); check($query['m'] === '13800138000' && $query['u'] === 'fixture-user', 'Provider receives the intended account and recipient'); check(SmsTransportFixture::$options[CURLOPT_SSL_VERIFYPEER] === true, 'TLS peer verification stays enabled'); check(SmsTransportFixture::$options[CURLOPT_SSL_VERIFYHOST] === 2, 'TLS hostname verification stays enabled'); settings(['sms_signature' => '【云泉智配】', 'sms_password' => 'fixture-api-key']); $sender->send('13800138000', '验证码123456'); parse_str((string) parse_url(SmsTransportFixture::$url, PHP_URL_QUERY), $query); check($query['c'] === '【云泉智配】验证码123456', 'Bracketed signatures are not duplicated'); check($query['p'] === 'fixture-api-key', 'API keys pass through unchanged'); foreach ([ '30' => '账户密码错误', '40' => '账户不存在', '41' => '账户余额不足', '43' => '来源IP受限', '50' => '内容包含敏感词', '51' => '手机号码不正确', ] as $status => $reason) { $handler->clear(); SmsTransportFixture::$response = (string) $status; rejects(fn () => $sender->send('13800138000', '验证码123456'), 50302); $record = $handler->getRecords()[0]; check($record['context']['reason'] === $reason, 'Official provider status is interpreted correctly: ' . $status); check(array_key_exists('request_id', $record['context']), 'Provider rejection supports request correlation'); } $handler->clear(); SmsTransportFixture::$response = false; SmsTransportFixture::$status = 0; SmsTransportFixture::$errorNumber = 28; SmsTransportFixture::$error = 'Timeout accessing ' . SmsTransportFixture::$url; rejects(fn () => $sender->send('13800138000', '验证码123456'), 50302); $record = $handler->getRecords()[0]; check($record['context']['curl_errno'] === 28, 'Transport errno is available for diagnosis'); check(!str_contains(json_encode($record), 'fixture-api-key'), 'Transport logs redact provider credentials'); check(!str_contains(json_encode($record), '13800138000'), 'Transport logs do not contain a request URL or recipient'); $handler->clear(); SmsTransportFixture::$response = 'Unexpected HTML response containing fixture-api-key'; SmsTransportFixture::$status = 200; rejects(fn () => $sender->send('13800138000', '验证码123456'), 50302); $record = $handler->getRecords()[0]; check($record['context']['status'] === 'unexpected_response', 'Unexpected provider response is not logged verbatim'); check(!str_contains(json_encode($record), 'fixture-api-key'), 'Unexpected response cannot expose credentials'); } finally { settings($original); $logger->popHandler(); } echo "PASS: $checks SMS provider checks; no network request or SMS sent.\n"; }