chore: prepare anxinyan release
This commit is contained in:
206
server-api/tools/wechat_h5_auth_mock_test.php
Normal file
206
server-api/tools/wechat_h5_auth_mock_test.php
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user