chore: prepare anxinyan release

This commit is contained in:
wushumin
2026-05-25 14:53:59 +08:00
parent 21360a6a2c
commit fa8c9015d9
26 changed files with 2124 additions and 120 deletions

View File

@@ -0,0 +1,206 @@
<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
$dotenv->safeLoad();
$_ENV['APP_DEBUG'] = 'true';
$_ENV['WECHAT_H5_AUTH_MOCK'] = 'true';
use app\support\AppAuthService;
use support\think\Db;
use Webman\Http\Request;
Db::setConfig(require dirname(__DIR__) . '/config/think-orm.php');
function assertTrue(bool $condition, string $message): void
{
if (!$condition) {
throw new RuntimeException($message);
}
}
function makeRequest(): Request
{
return new class("GET /api/app/auth/wechat/mock-test HTTP/1.1\r\nHost: 127.0.0.1\r\nUser-Agent: wechat-h5-auth-mock-test\r\n\r\n") extends Request {
public function getRealIp(bool $safeMode = true): string
{
return '127.0.0.1';
}
};
}
function ensureConfig(string $group, string $key, string $value): void
{
$now = date('Y-m-d H:i:s');
$exists = Db::name('system_configs')
->where('config_group', $group)
->where('config_key', $key)
->find();
$payload = [
'config_group' => $group,
'config_key' => $key,
'config_value' => $value,
'remark' => '微信 H5 授权测试配置',
'updated_at' => $now,
];
if ($exists) {
Db::name('system_configs')->where('id', $exists['id'])->update($payload);
return;
}
$payload['created_at'] = $now;
Db::name('system_configs')->insert($payload);
}
function latestDebugCode(string $mobile): string
{
$record = Db::name('sms_code_logs')
->where('mobile', $mobile)
->where('scene', 'login')
->where('send_status', 'mock')
->whereNull('used_at')
->order('id', 'desc')
->find();
assertTrue((bool)$record, 'mock sms code record missing');
for ($code = 100000; $code <= 999999; $code++) {
$codeText = (string)$code;
if (hash_equals((string)$record['code_hash'], hash('sha256', implode('|', [$mobile, 'login', $codeText])))) {
return $codeText;
}
}
throw new RuntimeException('mock sms code not found');
}
function cleanupWechatMockData(): void
{
$userIds = Db::name('users')
->whereLike('mobile', '1399900%')
->column('id');
if ($userIds) {
Db::name('user_api_tokens')->whereIn('user_id', $userIds)->delete();
Db::name('user_auths')->whereIn('user_id', $userIds)->delete();
Db::name('users')->whereIn('id', $userIds)->delete();
}
Db::name('sms_code_logs')->whereLike('mobile', '1399900%')->delete();
Db::name('user_auths')->where('auth_type', 'wechat_h5')->whereLike('auth_key', 'mock_openid_%')->delete();
}
$service = new AppAuthService();
$request = makeRequest();
$originalConfigs = [
'h5.app_id' => Db::name('system_configs')->where('config_group', 'h5')->where('config_key', 'app_id')->value('config_value'),
'h5.app_secret' => Db::name('system_configs')->where('config_group', 'h5')->where('config_key', 'app_secret')->value('config_value'),
'h5.page_base_url' => Db::name('system_configs')->where('config_group', 'h5')->where('config_key', 'page_base_url')->value('config_value'),
];
Db::startTrans();
try {
cleanupWechatMockData();
ensureConfig('h5', 'app_id', 'wx_mock_appid');
ensureConfig('h5', 'app_secret', 'mock_secret');
ensureConfig('h5', 'page_base_url', 'https://m.example.com');
ensureConfig('sms', 'access_key_id', '');
ensureConfig('sms', 'access_key_secret', '');
ensureConfig('sms', 'sign_name', '');
ensureConfig('sms', 'login_template_code', '');
$config = $service->wechatConfig();
assertTrue($config['enabled'] === true, 'wechat config should be enabled');
assertTrue($config['oauth_redirect_url'] === 'https://m.example.com/#/pages/auth/login', 'oauth redirect url mismatch');
$stateOne = (string)$config['state'];
$exchange = $service->exchangeWechatCode('mock_newuser', $stateOne, $request);
assertTrue(($exchange['status'] ?? '') === 'need_bind', 'new wechat user should need binding');
assertTrue(!empty($exchange['bind_ticket']), 'bind ticket missing');
$mobile = '13999000001';
$service->sendLoginCode($mobile, $request);
$bind = $service->bindWechatMobile((string)$exchange['bind_ticket'], $mobile, latestDebugCode($mobile), $request);
assertTrue(($bind['status'] ?? '') === 'logged_in' && !empty($bind['token']), 'bind should return token');
$stateTwo = (string)$service->wechatConfig()['state'];
$linked = $service->exchangeWechatCode('mock_newuser', $stateTwo, $request);
assertTrue(($linked['status'] ?? '') === 'logged_in' && !empty($linked['token']), 'linked wechat user should login');
$stateThree = (string)$service->wechatConfig()['state'];
$existingUser = $service->exchangeWechatCode('mock_existingmobile', $stateThree, $request);
$existingMobile = '13999000002';
Db::name('users')->insert([
'nickname' => '已有手机号用户',
'avatar' => '',
'mobile' => $existingMobile,
'password' => '',
'status' => 'enabled',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$service->sendLoginCode($existingMobile, $request);
$boundExisting = $service->bindWechatMobile((string)$existingUser['bind_ticket'], $existingMobile, latestDebugCode($existingMobile), $request);
assertTrue(($boundExisting['status'] ?? '') === 'logged_in', 'existing mobile should bind');
assertTrue((int)Db::name('users')->where('mobile', $existingMobile)->count() === 1, 'existing mobile should not duplicate user');
$otherUserId = (int)Db::name('users')->insertGetId([
'nickname' => '占用微信用户',
'avatar' => '',
'mobile' => '13999000003',
'password' => '',
'status' => 'enabled',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
Db::name('user_auths')->insert([
'user_id' => $otherUserId,
'auth_type' => 'wechat_h5',
'auth_key' => 'mock_openid_conflict',
'auth_open_id' => 'mock_openid_conflict',
'auth_union_id' => 'mock_unionid_conflict',
'auth_extra' => json_encode(['mock' => true]),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$conflictUser = $service->exchangeWechatCode('mock_conflict', (string)$service->wechatConfig()['state'], $request);
assertTrue(($conflictUser['status'] ?? '') === 'logged_in', 'existing conflicting openid should login owner directly');
try {
$service->exchangeWechatCode('mock_expired', (string)$service->wechatConfig()['state'], $request);
throw new RuntimeException('expired code should fail');
} catch (RuntimeException $e) {
assertTrue(strpos($e->getMessage(), 'code 无效') !== false, 'expired code message mismatch');
}
try {
$service->bindWechatMobile('invalid.ticket', '13999000004', '123456', $request);
throw new RuntimeException('invalid bind ticket should fail');
} catch (RuntimeException $e) {
assertTrue(strpos($e->getMessage(), '绑定凭证') !== false, 'invalid bind ticket message mismatch');
}
try {
$service->exchangeWechatCode('mock_statefail', 'wrongstate', $request);
throw new RuntimeException('invalid state should fail');
} catch (RuntimeException $e) {
assertTrue(strpos($e->getMessage(), '状态') !== false, 'invalid state message mismatch');
}
echo "WECHAT_H5_AUTH_MOCK_TEST_OK\n";
Db::rollback();
} catch (Throwable $e) {
Db::rollback();
fwrite(STDERR, "WECHAT_H5_AUTH_MOCK_TEST_FAIL: " . $e->getMessage() . "\n");
exit(1);
} finally {
foreach ($originalConfigs as $mapKey => $value) {
[$group, $key] = explode('.', $mapKey, 2);
ensureConfig($group, $key, (string)$value);
}
}