133 lines
4.7 KiB
JavaScript
133 lines
4.7 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* Local smoke test for Portal template catalog API (Memind 8081).
|
|||
|
|
* Usage: node scripts/verify-template-catalog-portal.mjs
|
|||
|
|
*/
|
|||
|
|
import fs from 'node:fs';
|
|||
|
|
import path from 'node:path';
|
|||
|
|
import { fileURLToPath } from 'node:url';
|
|||
|
|
import { loadH5Environment } from './load-env.mjs';
|
|||
|
|
|
|||
|
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|||
|
|
const root = path.join(scriptDir, '..');
|
|||
|
|
|
|||
|
|
function loadEnvFile(filePath) {
|
|||
|
|
if (!fs.existsSync(filePath)) return;
|
|||
|
|
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
|
|||
|
|
const trimmed = line.trim();
|
|||
|
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|||
|
|
const eq = trimmed.indexOf('=');
|
|||
|
|
if (eq <= 0) continue;
|
|||
|
|
const key = trimmed.slice(0, eq).trim();
|
|||
|
|
let value = trimmed.slice(eq + 1).trim();
|
|||
|
|
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|||
|
|
value = value.slice(1, -1);
|
|||
|
|
}
|
|||
|
|
if (!(key in process.env)) process.env[key] = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
loadH5Environment(scriptDir);
|
|||
|
|
loadEnvFile(path.join(root, '../memind_adm/.env'));
|
|||
|
|
|
|||
|
|
const base = process.env.PORTAL_API_BASE ?? `http://127.0.0.1:${process.env.H5_PORT ?? 8081}`;
|
|||
|
|
|
|||
|
|
const username =
|
|||
|
|
process.env.VERIFY_PORTAL_USERNAME?.trim() ||
|
|||
|
|
process.env.H5_TEST_USERNAME?.trim() ||
|
|||
|
|
process.env.MEMIND_E2E_USERNAME?.trim() ||
|
|||
|
|
process.env.H5_ADMIN_USERNAME?.trim() ||
|
|||
|
|
'admin';
|
|||
|
|
const password =
|
|||
|
|
process.env.VERIFY_PORTAL_PASSWORD?.trim() ||
|
|||
|
|
process.env.H5_TEST_PASSWORD?.trim() ||
|
|||
|
|
process.env.MEMIND_E2E_PASSWORD?.trim() ||
|
|||
|
|
process.env.JOHN_PASSWORD?.trim() ||
|
|||
|
|
process.env.VERIFY_PASSWORD?.trim() ||
|
|||
|
|
process.env.H5_ADMIN_PASSWORD?.trim() ||
|
|||
|
|
process.env.ADMIN_PASSWORD?.trim() ||
|
|||
|
|
'';
|
|||
|
|
|
|||
|
|
async function request(method, urlPath, { body, cookie } = {}) {
|
|||
|
|
const res = await fetch(`${base}${urlPath}`, {
|
|||
|
|
method,
|
|||
|
|
headers: {
|
|||
|
|
...(body ? { 'Content-Type': 'application/json' } : {}),
|
|||
|
|
...(cookie ? { Cookie: cookie } : {}),
|
|||
|
|
},
|
|||
|
|
body: body ? JSON.stringify(body) : undefined,
|
|||
|
|
});
|
|||
|
|
const text = await res.text();
|
|||
|
|
let json = null;
|
|||
|
|
try {
|
|||
|
|
json = text ? JSON.parse(text) : null;
|
|||
|
|
} catch {
|
|||
|
|
json = { raw: text.slice(0, 300) };
|
|||
|
|
}
|
|||
|
|
return { status: res.status, json, text, setCookie: res.headers.getSetCookie?.() ?? [] };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function cookieFromSetCookie(setCookie) {
|
|||
|
|
return setCookie.map((c) => c.split(';')[0]).join('; ');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function assertOk(cond, msg) {
|
|||
|
|
if (!cond) throw new Error(msg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
console.log(`==> Portal API base: ${base}`);
|
|||
|
|
|
|||
|
|
if (!password) {
|
|||
|
|
console.error('缺少 Portal 登录密码(VERIFY_PORTAL_PASSWORD / H5_ADMIN_PASSWORD / memind_adm/.env)');
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const login = await request('POST', '/auth/login', {
|
|||
|
|
body: { username, password },
|
|||
|
|
});
|
|||
|
|
assertOk(login.status === 200, `登录失败 HTTP ${login.status}: ${JSON.stringify(login.json)}`);
|
|||
|
|
const cookie = cookieFromSetCookie(login.setCookie);
|
|||
|
|
assertOk(cookie, '登录未返回 session cookie');
|
|||
|
|
console.log(`✓ Portal 登录 (${username})`);
|
|||
|
|
|
|||
|
|
const catalog = await request('GET', '/api/mindspace/v1/template-catalog', { cookie });
|
|||
|
|
assertOk(catalog.status === 200, `catalog HTTP ${catalog.status}: ${JSON.stringify(catalog.json)}`);
|
|||
|
|
const items = catalog.json?.data?.items ?? catalog.json?.items ?? [];
|
|||
|
|
assertOk(Array.isArray(items), 'catalog items 应为数组');
|
|||
|
|
assertOk(items.length >= 13, `应至少 13 个模板,实际 ${items.length}`);
|
|||
|
|
const travel = items.find((item) => item.skillName === 'page-template-travel');
|
|||
|
|
assertOk(travel, '缺少 page-template-travel');
|
|||
|
|
console.log(
|
|||
|
|
`✓ GET template-catalog (${items.length} 项,travel owned=${Boolean(travel.owned)} price=${travel.priceCents})`,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const preview = await request('GET', '/api/mindspace/v1/template-catalog/page-template-travel/preview', {
|
|||
|
|
cookie,
|
|||
|
|
});
|
|||
|
|
assertOk(preview.status === 200, `preview HTTP ${preview.status}`);
|
|||
|
|
assertOk(String(preview.text).includes('周末去哪玩'), '预览 HTML 应含示例文案');
|
|||
|
|
console.log('✓ GET template preview HTML');
|
|||
|
|
|
|||
|
|
if (travel.owned) {
|
|||
|
|
console.log('• travel 已拥有,跳过余额购买测试');
|
|||
|
|
} else if (travel.priceCents > 0) {
|
|||
|
|
const purchase = await request('POST', '/api/mindspace/v1/template-catalog/page-template-travel/purchase', {
|
|||
|
|
cookie,
|
|||
|
|
});
|
|||
|
|
if (purchase.status === 402) {
|
|||
|
|
console.log('• 余额不足 (402),purchase 路由正常');
|
|||
|
|
} else {
|
|||
|
|
assertOk(purchase.status === 200, `purchase HTTP ${purchase.status}: ${JSON.stringify(purchase.json)}`);
|
|||
|
|
console.log('✓ POST balance purchase 成功');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('\n全部 Portal template-catalog API 联调通过。');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main().catch((err) => {
|
|||
|
|
console.error('\n联调失败:', err.message);
|
|||
|
|
process.exit(1);
|
|||
|
|
});
|