forked from wangziqi/gongxue-base
156 lines
5.7 KiB
JavaScript
156 lines
5.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import http from 'node:http';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { spawn } from 'node:child_process';
|
|
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' });
|
|
res.end(JSON.stringify(payload));
|
|
}
|
|
|
|
function bodyJson(req) {
|
|
return new Promise(resolve => {
|
|
const chunks = [];
|
|
req.on('data', chunk => chunks.push(chunk));
|
|
req.on('end', () => {
|
|
try {
|
|
resolve(JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}'));
|
|
} catch {
|
|
resolve({});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
const seen = [];
|
|
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 });
|
|
|
|
if (url.pathname === '/api/auth/sms/send' && req.method === 'POST') {
|
|
const body = await bodyJson(req);
|
|
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: body.phone, purpose: body.purpose, provider: 'aliyun-pnvs', status: 'sent' },
|
|
expireIn: 300,
|
|
cooldown: 60,
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (url.pathname === '/api/auth/sms/verify' && req.method === 'POST') {
|
|
const body = await bodyJson(req);
|
|
assert.equal(body.phone, phone);
|
|
assert.equal(body.code, '123456');
|
|
json(res, 200, {
|
|
user: { id: 'user-id', phone },
|
|
session: { token: 'session-token' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (url.pathname === '/api/auth/me' && req.method === 'GET') {
|
|
assert.equal(req.headers.authorization, 'Bearer session-token');
|
|
json(res, 200, { user: { id: 'user-id', phone } });
|
|
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 });
|
|
});
|
|
|
|
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
|
|
function runCliSmoke(address) {
|
|
return new Promise(resolve => {
|
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tiku-sms-smoke-'));
|
|
const writePath = path.join(tempDir, 'nested', 'sms-pnvs-remote-smoke.json');
|
|
const child = spawn(process.execPath, ['scripts/remote-sms-login-smoke.js', '--json', '--write', writePath], {
|
|
cwd: process.cwd(),
|
|
env: {
|
|
...process.env,
|
|
SMS_SMOKE_API_BASE_URL: `http://127.0.0.1:${address.port}`,
|
|
SMS_SMOKE_TENANT_ID: tenantId,
|
|
SMS_SMOKE_PHONE: phone,
|
|
SMS_SMOKE_BIND_PHONE: bindPhone,
|
|
SMS_SMOKE_ORIGIN: 'https://admin.tjszsb.com',
|
|
SMS_SMOKE_CODE: '123456',
|
|
SMS_SMOKE_BIND_CODE: '654321',
|
|
},
|
|
});
|
|
let stdout = '';
|
|
let stderr = '';
|
|
child.stdout.on('data', chunk => { stdout += chunk.toString('utf8'); });
|
|
child.stderr.on('data', chunk => { stderr += chunk.toString('utf8'); });
|
|
child.on('close', status => {
|
|
let artifact = '';
|
|
if (fs.existsSync(writePath)) artifact = fs.readFileSync(writePath, 'utf8');
|
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
resolve({ status, stdout, stderr, artifact });
|
|
});
|
|
});
|
|
}
|
|
|
|
try {
|
|
const address = server.address();
|
|
const result = await runRemoteSmsLoginSmoke(
|
|
{
|
|
apiBaseUrl: `http://127.0.0.1:${address.port}`,
|
|
tenantId,
|
|
phone,
|
|
origin: 'https://admin.tjszsb.com',
|
|
purpose: 'login',
|
|
code: '123456',
|
|
bindPhone,
|
|
bindCode: '654321',
|
|
timeoutMs: 5000,
|
|
skipSend: false,
|
|
skipMe: false,
|
|
},
|
|
{ quiet: true },
|
|
);
|
|
|
|
assert.equal(result.sessionToken, 'session-token');
|
|
assert.equal(result.boundPhone, bindPhone);
|
|
assert.equal(result.summary.provider, 'aliyun-pnvs');
|
|
assert.equal(result.summary.loginVerified, true);
|
|
assert.equal(result.summary.authMe, true);
|
|
assert.equal(result.summary.bindProvider, 'aliyun-pnvs');
|
|
assert.equal(result.summary.bindVerified, true);
|
|
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);
|
|
|
|
const cliResult = await runCliSmoke(address);
|
|
assert.equal(cliResult.status, 0, `CLI --json smoke should pass: ${cliResult.stdout} ${cliResult.stderr}`);
|
|
const cliSummary = JSON.parse(cliResult.stdout);
|
|
const artifactSummary = JSON.parse(cliResult.artifact);
|
|
assert.equal(cliSummary.provider, 'aliyun-pnvs');
|
|
assert.equal(cliSummary.bindProvider, 'aliyun-pnvs');
|
|
assert.deepEqual(artifactSummary, cliSummary);
|
|
assert.doesNotMatch(cliResult.stdout, /Enter received/, 'CLI --json stdout must stay machine-readable JSON');
|
|
assert.doesNotMatch(cliResult.artifact, /Enter received/, 'CLI --write artifact must stay machine-readable JSON');
|
|
console.log('[PASS] remote SMS login smoke script');
|
|
} finally {
|
|
await new Promise(resolve => server.close(resolve));
|
|
}
|