const assert = require('assert'); const fs = require('fs'); const http = require('http'); const os = require('os'); const path = require('path'); const vm = require('vm'); const { startLoginFlowServer, stopLoginFlowServer, subscribeErrorStatus, subscribeErrorBody } = require('../mcp/src/core/login-flow-server'); const { classifySubscribeError } = require('../mcp/src/core/subscribe-page'); async function body(req) { const chunks = []; for await (const chunk of req) chunks.push(chunk); if (!chunks.length) return {}; return JSON.parse(Buffer.concat(chunks).toString('utf8')); } async function startGateway() { const requests = []; let failSubscribe = false; const server = http.createServer(async (req, res) => { const url = new URL(req.url, 'http://localhost'); const input = await body(req); requests.push({ path: url.pathname, method: req.method, input, authorization: req.headers.authorization }); let data; if (url.pathname === '/subscribe/status') { const quotedSeats = Number(url.searchParams.get('seats')); data = quotedSeats ? { subscribed: true, seats: 1, usedSeats: 1, quote: { seats: quotedSeats, amount: 125 } } : { subscribed: true, seats: 1, usedSeats: 1, price: 500 }; } else if (url.pathname === '/login/accounts') { data = { seats: 1, usedSeats: 1, accounts: [{ id: `account_${'a'.repeat(40)}`, nickname: '原账号', corpName: '测试企业', online: false }] }; } else if (url.pathname === '/login/status') { data = { configured: true, online: false, statusCode: -1 }; } else if (url.pathname === '/login/select') { data = { selected: true, uid: input.uid, accountId: input.accountId, online: false }; } else if (url.pathname === '/login/start') { data = { uid: input.newAccount ? 'qiwei-temporary-login' : input.uid, guid: input.newAccount ? 'GUID-NEW-DEVICE' : 'GUID-EXISTING-DEVICE', loginQrcodeBase64Data: Buffer.from('mock-png').toString('base64') }; } else if (url.pathname === '/login/check') { data = { uid: input.uid, status: 2, detail: { corpId: 'corp-new', userId: 'user-new', nickname: '新账号' }, decisionRequired: true, seats: 1, usedSeats: 1, account: { id: `account_${'b'.repeat(40)}`, nickname: '新账号' }, accounts: [{ id: `account_${'a'.repeat(40)}`, nickname: '原账号' }], actions: ['purchase', 'replace', 'cancel'] }; } else if (url.pathname === '/login/resolve') { data = { resolved: true, action: input.action, cancelled: input.action === 'cancel' }; } else if (url.pathname === '/subscribe') { if (failSubscribe) { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ code: 402, mess: '余额不足:当前 0 元,本次订阅需 500 元' })); return; } data = { seats: input.seats, amount: input.expectedAmount, state: 'active' }; } else { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ code: 404, mess: 'not found' })); return; } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ code: 200, data })); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); return { apiBase: `http://127.0.0.1:${server.address().port}`, requests, setSubscribeFailure(value) { failSubscribe = Boolean(value); }, close: () => new Promise(resolve => server.close(resolve)) }; } async function main() { assert.equal( classifySubscribeError(409, '上游账号当前没有可用企微登录数量,请稍后重试'), 'QW-UP-409', 'upstream login capacity errors must not be shown as generic 502' ); assert.strictEqual( subscribeErrorStatus({ httpStatus: 200, bizCode: 402 }), 402, 'business billing code must survive an HTTP 200 gateway response' ); assert.strictEqual( subscribeErrorBody(402, '余额不足').errorCode, 'QW-PAY-402', 'billing errors must use the payment error code' ); const gateway = await startGateway(); const port = 4397; const envRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-guid-persist-')); fs.writeFileSync(path.join(envRoot, '.env.local'), 'QIWEI_UID=qiwei-client-one\nQIWEI_GUID=\n', 'utf8'); const previousRoots = { package: process.env.QIWEI_PACKAGE_ROOT, workspace: process.env.QIWEI_WORKSPACE_ROOT, }; process.env.QIWEI_PACKAGE_ROOT = envRoot; process.env.QIWEI_WORKSPACE_ROOT = envRoot; try { const { url } = await startLoginFlowServer({ token: 'sk-login-seat-smoke', apiBase: gateway.apiBase, uid: 'qiwei-client-one', port }); const page = await fetch(url).then(res => res.text()); const inlineScript = page.match(/