| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- <?php
- declare(strict_types=1);
- /** Exercise the provider adapter with an in-memory HTTP transport. No SMS is sent. */
- namespace app\miniapi\service {
- final class SmsTransportFixture
- {
- public static string $url = '';
- public static array $options = [];
- public static string|false $response = '0';
- public static int $status = 200;
- public static int $errorNumber = 0;
- public static string $error = '';
- }
- function curl_init(string $url): \stdClass
- {
- SmsTransportFixture::$url = $url;
- return new \stdClass();
- }
- function curl_setopt_array(\stdClass $handle, array $options): bool
- {
- SmsTransportFixture::$options = $options;
- return true;
- }
- function curl_exec(\stdClass $handle): string|false
- {
- return SmsTransportFixture::$response;
- }
- function curl_getinfo(\stdClass $handle, int $option): int
- {
- return SmsTransportFixture::$status;
- }
- function curl_errno(\stdClass $handle): int
- {
- return SmsTransportFixture::$errorNumber;
- }
- function curl_error(\stdClass $handle): string
- {
- return SmsTransportFixture::$error;
- }
- function curl_close(\stdClass $handle): void
- {
- }
- }
- namespace {
- require dirname(__DIR__) . '/vendor/autoload.php';
- \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->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";
- }
|