test: extend remote SMS smoke to phone binding

This commit is contained in:
Codex
2026-07-03 23:26:40 +08:00
parent 27ee85bc94
commit 5c36156ac5
3 changed files with 69 additions and 3 deletions

View File

@@ -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 压测数据只能作为开发参考。
## 关键安全要求

View File

@@ -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));

View File

@@ -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
`);
}