forked from wangziqi/gongxue-base
test: extend remote SMS smoke to phone binding
This commit is contained in:
@@ -339,6 +339,12 @@ SMS_SMOKE_API_BASE_URL=https://api.tjszsb.com SMS_SMOKE_TENANT_ID=00000000-0000-
|
||||
npm run perf:api:local
|
||||
```
|
||||
|
||||
如果要同时验证手机号绑定也走 PNVS provider verification,准备一个未绑定测试手机号后执行:
|
||||
|
||||
```bash
|
||||
SMS_SMOKE_API_BASE_URL=https://api.tjszsb.com SMS_SMOKE_TENANT_ID=00000000-0000-0000-0000-000000000001 SMS_SMOKE_PHONE=replace-with-login-phone SMS_SMOKE_BIND_PHONE=replace-with-bind-phone SMS_SMOKE_ORIGIN=https://admin.tjszsb.com npm run smoke:sms-login:remote
|
||||
```
|
||||
|
||||
压测必须在目标云服务器、目标数据库参数、目标对象存储和目标 Nginx 配置下重新计算,本地 Windows 压测数据只能作为开发参考。
|
||||
|
||||
## 关键安全要求
|
||||
|
||||
@@ -4,6 +4,7 @@ import { runRemoteSmsLoginSmoke } from './remote-sms-login-smoke.js';
|
||||
|
||||
const tenantId = '00000000-0000-0000-0000-000000000001';
|
||||
const phone = '13800138000';
|
||||
const bindPhone = '13900139000';
|
||||
|
||||
function json(res, status, payload) {
|
||||
res.writeHead(status, { 'content-type': 'application/json' });
|
||||
@@ -31,10 +32,11 @@ const server = http.createServer(async (req, res) => {
|
||||
|
||||
if (url.pathname === '/api/auth/sms/send' && req.method === 'POST') {
|
||||
const body = await bodyJson(req);
|
||||
assert.equal(body.phone, phone);
|
||||
assert.equal(body.purpose, 'login');
|
||||
assert.ok([phone, bindPhone].includes(body.phone));
|
||||
assert.equal(body.purpose, body.phone === bindPhone ? 'bind_phone' : 'login');
|
||||
if (body.phone === bindPhone) assert.equal(req.headers.authorization, 'Bearer session-token');
|
||||
json(res, 200, {
|
||||
item: { id: 'sms-id', phone, purpose: 'login', provider: 'aliyun-pnvs', status: 'sent' },
|
||||
item: { id: 'sms-id', phone: body.phone, purpose: body.purpose, provider: 'aliyun-pnvs', status: 'sent' },
|
||||
expireIn: 300,
|
||||
cooldown: 60,
|
||||
});
|
||||
@@ -58,6 +60,16 @@ const server = http.createServer(async (req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.pathname === '/api/auth/phone/bind' && req.method === 'POST') {
|
||||
assert.equal(req.headers.authorization, 'Bearer session-token');
|
||||
const body = await bodyJson(req);
|
||||
assert.equal(body.phone, bindPhone);
|
||||
assert.equal(body.code, '654321');
|
||||
assert.equal(body.purpose, 'bind_phone');
|
||||
json(res, 200, { ok: true, user: { id: 'user-id', phone: bindPhone }, phoneChanged: true, revokedOtherSessions: 0 });
|
||||
return;
|
||||
}
|
||||
|
||||
json(res, 404, { code: 'NOT_FOUND', path: url.pathname });
|
||||
});
|
||||
|
||||
@@ -73,6 +85,8 @@ try {
|
||||
origin: 'https://admin.tjszsb.com',
|
||||
purpose: 'login',
|
||||
code: '123456',
|
||||
bindPhone,
|
||||
bindCode: '654321',
|
||||
timeoutMs: 5000,
|
||||
skipSend: false,
|
||||
skipMe: false,
|
||||
@@ -81,9 +95,11 @@ try {
|
||||
);
|
||||
|
||||
assert.equal(result.sessionToken, 'session-token');
|
||||
assert.equal(result.boundPhone, bindPhone);
|
||||
assert.equal(seen.some(item => item.path === '/api/auth/sms/send' && item.tenantId === tenantId), true);
|
||||
assert.equal(seen.some(item => item.path === '/api/auth/sms/verify' && item.origin === 'https://admin.tjszsb.com'), true);
|
||||
assert.equal(seen.some(item => item.path === '/api/auth/me'), true);
|
||||
assert.equal(seen.some(item => item.path === '/api/auth/phone/bind'), true);
|
||||
console.log('[PASS] remote SMS login smoke script');
|
||||
} finally {
|
||||
await new Promise(resolve => server.close(resolve));
|
||||
|
||||
@@ -31,9 +31,11 @@ function buildConfig(env = process.env) {
|
||||
const apiBaseUrl = envString(env, 'SMS_SMOKE_API_BASE_URL', envString(env, 'API_BASE', ''));
|
||||
const tenantId = envString(env, 'SMS_SMOKE_TENANT_ID', envString(env, 'TENANT_ID', ''));
|
||||
const phone = envString(env, 'SMS_SMOKE_PHONE');
|
||||
const bindPhone = envString(env, 'SMS_SMOKE_BIND_PHONE');
|
||||
const origin = envString(env, 'SMS_SMOKE_ORIGIN', 'https://admin.tjszsb.com');
|
||||
const purpose = envString(env, 'SMS_SMOKE_PURPOSE', 'login');
|
||||
const code = envString(env, 'SMS_SMOKE_CODE');
|
||||
const bindCode = envString(env, 'SMS_SMOKE_BIND_CODE');
|
||||
|
||||
const missing = [];
|
||||
if (!apiBaseUrl) missing.push('SMS_SMOKE_API_BASE_URL');
|
||||
@@ -47,9 +49,11 @@ function buildConfig(env = process.env) {
|
||||
apiBaseUrl: normalizeBaseUrl(apiBaseUrl),
|
||||
tenantId,
|
||||
phone,
|
||||
bindPhone,
|
||||
origin,
|
||||
purpose,
|
||||
code,
|
||||
bindCode,
|
||||
timeoutMs: envNumber(env, 'SMS_SMOKE_TIMEOUT_MS', DEFAULT_TIMEOUT_MS),
|
||||
skipSend: ['1', 'true', 'yes', 'on'].includes(envString(env, 'SMS_SMOKE_SKIP_SEND').toLowerCase()),
|
||||
skipMe: ['1', 'true', 'yes', 'on'].includes(envString(env, 'SMS_SMOKE_SKIP_ME').toLowerCase()),
|
||||
@@ -105,6 +109,16 @@ async function promptCode(config) {
|
||||
}
|
||||
}
|
||||
|
||||
async function promptBindCode(config) {
|
||||
if (config.bindCode) return config.bindCode;
|
||||
const readline = createInterface({ input, output });
|
||||
try {
|
||||
return (await readline.question('Enter received bind_phone SMS verification code: ')).trim();
|
||||
} finally {
|
||||
readline.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function runRemoteSmsLoginSmoke(inputConfig, options = {}) {
|
||||
const config = inputConfig?.apiBaseUrl ? inputConfig : buildConfig(options.env || process.env);
|
||||
if (!options.quiet) {
|
||||
@@ -145,9 +159,37 @@ async function runRemoteSmsLoginSmoke(inputConfig, options = {}) {
|
||||
if (!options.quiet) console.log(`PASS auth.me user=${me.payload?.user?.id || me.payload?.item?.userId || 'unknown'}`);
|
||||
}
|
||||
|
||||
let boundPhone = null;
|
||||
if (config.bindPhone) {
|
||||
const sendBind = await requestJson(config, '/api/auth/sms/send', {
|
||||
method: 'POST',
|
||||
token,
|
||||
body: { phone: config.bindPhone, purpose: 'bind_phone' },
|
||||
});
|
||||
assertOk('sms.bind.send', sendBind);
|
||||
if (!options.quiet) {
|
||||
console.log(`PASS sms.bind.send provider=${sendBind.payload?.item?.provider || 'unknown'} phone=${maskPhone(config.bindPhone)}`);
|
||||
}
|
||||
|
||||
const bindCode = await promptBindCode(config);
|
||||
if (!bindCode) throw new Error('Missing bind_phone SMS verification code');
|
||||
|
||||
const bind = await requestJson(config, '/api/auth/phone/bind', {
|
||||
method: 'POST',
|
||||
token,
|
||||
body: { phone: config.bindPhone, code: bindCode, purpose: 'bind_phone' },
|
||||
});
|
||||
assertOk('auth.phone.bind', bind);
|
||||
boundPhone = bind.payload?.user?.phone || config.bindPhone;
|
||||
if (!options.quiet) {
|
||||
console.log(`PASS auth.phone.bind phone=${maskPhone(boundPhone)} phoneChanged=${bind.payload?.phoneChanged ?? '-'}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sessionToken: token,
|
||||
user: verify.payload?.user || verify.payload?.item || null,
|
||||
boundPhone,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -167,6 +209,8 @@ Required example:
|
||||
Optional:
|
||||
SMS_SMOKE_ORIGIN=https://admin.tjszsb.com
|
||||
SMS_SMOKE_CODE=<code-you-received>
|
||||
SMS_SMOKE_BIND_PHONE=<another-phone-to-bind>
|
||||
SMS_SMOKE_BIND_CODE=<bind-code-you-received>
|
||||
SMS_SMOKE_SKIP_SEND=true
|
||||
`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user