From 78d6ccc31933b3dc18bcd435cfb44ac2dc5fd617 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 4 Jul 2026 00:09:36 +0800 Subject: [PATCH] test: require PNVS provider in SMS smoke --- .../web-launch-acceptance-checklist.md | 2 ++ scripts/remote-sms-login-smoke-test.js | 25 ++++++++++++++++++- scripts/remote-sms-login-smoke.js | 16 +++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/docs/refactor/web-launch-acceptance-checklist.md b/docs/refactor/web-launch-acceptance-checklist.md index c367c80b..9bdf55aa 100644 --- a/docs/refactor/web-launch-acceptance-checklist.md +++ b/docs/refactor/web-launch-acceptance-checklist.md @@ -119,6 +119,8 @@ SMS_SMOKE_ORIGIN=https://admin.tjszsb.com \ npm run smoke:sms-login:remote -- --write docs/refactor/launch-artifacts/sms-pnvs-remote-smoke.json ``` +`smoke:sms-login:remote` 默认要求短信发送接口返回 `provider=aliyun-pnvs`;如果生产仍走传统 `aliyun`/`tencent` provider,脚本会直接失败。 + PNVS 只验收手机号登录/换绑验证码链路。催缴、营销、CRM 等非验证码短信不应接 PNVS;平台催缴通知仍按 webhook 类通知渠道验收。 `security:repo` 是仓库自带的静态安全扫描,会拦截密钥形态、前端旧鉴权头、真实 runtime-config 和生产证据误入 Git。它不能替代真实 `@codex-security`;如插件在当前 Codex 环境暴露扫描工具,再补插件扫描结果。若工具不可用,不能把该项标记为已完成,只能在上线证据里标记为待补。 diff --git a/scripts/remote-sms-login-smoke-test.js b/scripts/remote-sms-login-smoke-test.js index f119c89c..6de6152d 100644 --- a/scripts/remote-sms-login-smoke-test.js +++ b/scripts/remote-sms-login-smoke-test.js @@ -30,6 +30,7 @@ function bodyJson(req) { } const seen = []; +let sendProvider = 'aliyun-pnvs'; const server = http.createServer(async (req, res) => { const url = new URL(req.url || '/', 'http://127.0.0.1'); seen.push({ path: url.pathname, method: req.method, tenantId: req.headers['x-tenant-id'], origin: req.headers.origin }); @@ -40,7 +41,7 @@ const server = http.createServer(async (req, res) => { 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: body.phone, purpose: body.purpose, provider: 'aliyun-pnvs', status: 'sent' }, + item: { id: 'sms-id', phone: body.phone, purpose: body.purpose, provider: sendProvider, status: 'sent' }, expireIn: 300, cooldown: 60, }); @@ -129,6 +130,7 @@ try { timeoutMs: 5000, skipSend: false, skipMe: false, + expectedProvider: 'aliyun-pnvs', }, { quiet: true }, ); @@ -162,6 +164,27 @@ try { const writeOnlyArtifactSummary = JSON.parse(writeOnlyCliResult.artifact); assert.equal(writeOnlyArtifactSummary.provider, 'aliyun-pnvs'); assert.equal(writeOnlyArtifactSummary.bindProvider, 'aliyun-pnvs'); + + sendProvider = 'aliyun'; + await assert.rejects( + () => runRemoteSmsLoginSmoke( + { + apiBaseUrl: `http://127.0.0.1:${address.port}`, + tenantId, + phone, + origin: 'https://admin.tjszsb.com', + purpose: 'login', + code: '123456', + timeoutMs: 5000, + skipSend: false, + skipMe: true, + expectedProvider: 'aliyun-pnvs', + }, + { quiet: true }, + ), + /sms\.send returned provider=aliyun, expected aliyun-pnvs/, + ); + console.log('[PASS] remote SMS login smoke script'); } finally { await new Promise(resolve => server.close(resolve)); diff --git a/scripts/remote-sms-login-smoke.js b/scripts/remote-sms-login-smoke.js index 8f465e3a..980a812d 100644 --- a/scripts/remote-sms-login-smoke.js +++ b/scripts/remote-sms-login-smoke.js @@ -56,6 +56,7 @@ function buildConfig(env = process.env) { const purpose = envString(env, 'SMS_SMOKE_PURPOSE', 'login'); const code = envString(env, 'SMS_SMOKE_CODE'); const bindCode = envString(env, 'SMS_SMOKE_BIND_CODE'); + const expectedProvider = envString(env, 'SMS_SMOKE_EXPECT_PROVIDER', 'aliyun-pnvs'); const missing = []; if (!apiBaseUrl) missing.push('SMS_SMOKE_API_BASE_URL'); @@ -74,6 +75,7 @@ function buildConfig(env = process.env) { purpose, code, bindCode, + expectedProvider, 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()), @@ -119,6 +121,11 @@ function assertOk(name, response) { throw error; } +function assertExpectedProvider(name, actualProvider, expectedProvider) { + if (!expectedProvider || actualProvider === expectedProvider) return; + throw new Error(`${name} returned provider=${actualProvider || 'unknown'}, expected ${expectedProvider}`); +} + async function promptCode(config) { if (config.code) return config.code; const readline = createInterface({ input, output: config.jsonOutput ? stderr : output }); @@ -154,10 +161,11 @@ async function runRemoteSmsLoginSmoke(inputConfig, options = {}) { body: { phone: config.phone, purpose: config.purpose }, }); assertOk('sms.send', send); - if (!options.quiet) { - console.log(`PASS sms.send provider=${send.payload?.item?.provider || 'unknown'} expireIn=${send.payload?.expireIn || '-'} cooldown=${send.payload?.cooldown || '-'}`); - } config.sendProvider = send.payload?.item?.provider || ''; + assertExpectedProvider('sms.send', config.sendProvider, config.expectedProvider); + if (!options.quiet) { + console.log(`PASS sms.send provider=${config.sendProvider || 'unknown'} expireIn=${send.payload?.expireIn || '-'} cooldown=${send.payload?.cooldown || '-'}`); + } } const code = await promptCode(config); @@ -190,6 +198,7 @@ async function runRemoteSmsLoginSmoke(inputConfig, options = {}) { }); assertOk('sms.bind.send', sendBind); bindProvider = sendBind.payload?.item?.provider || ''; + assertExpectedProvider('sms.bind.send', bindProvider, config.expectedProvider); if (!options.quiet) { console.log(`PASS sms.bind.send provider=${bindProvider || 'unknown'} phone=${maskPhone(config.bindPhone)}`); } @@ -256,6 +265,7 @@ Optional: SMS_SMOKE_CODE= SMS_SMOKE_BIND_PHONE= SMS_SMOKE_BIND_CODE= + SMS_SMOKE_EXPECT_PROVIDER=aliyun-pnvs SMS_SMOKE_SKIP_SEND=true --json --write docs/refactor/launch-artifacts/sms-pnvs-remote-smoke.json