dab6140f99
Add verify→Aider→OpenHands dev loop, memindadm config/history UI, and john4 customer-order Page Data demo with scenario + verification scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
156 lines
5.8 KiB
JavaScript
156 lines
5.8 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* 验证 john4 客户下单系统 Page Data 链路(insert + admin read)。
|
||
*/
|
||
import { loadH5Environment } from './load-env.mjs';
|
||
import {
|
||
createReporter,
|
||
loginViaApi,
|
||
resolvePortalBase,
|
||
} from './scenario-test-lib.mjs';
|
||
import path from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
import { PUBLISH_ROOT_DIR } from '../user-publish.mjs';
|
||
import fs from 'node:fs/promises';
|
||
|
||
const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||
const USER_ID = process.env.CUSTOMER_ORDER_USER_ID ?? '32035858-9a20-425b-89da-c118ef0779aa';
|
||
const DATASET = 'customer_orders';
|
||
const FORM_PAGE = 'customer-order-form.html';
|
||
const ADMIN_PAGE = 'customer-order-admin.html';
|
||
|
||
loadH5Environment(import.meta.dirname);
|
||
|
||
async function findPageIdFromPolicy(publishKey) {
|
||
const policyDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'page-data-policies');
|
||
const entries = await fs.readdir(policyDir);
|
||
for (const name of entries.filter((item) => item.endsWith('.json'))) {
|
||
const raw = await fs.readFile(path.join(policyDir, name), 'utf8');
|
||
const policy = JSON.parse(raw);
|
||
if (policy?.datasets?.[DATASET]?.insert) {
|
||
return { pageId: policy.pageId, fileName: name };
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
async function main() {
|
||
const port = Number(process.env.H5_PORT ?? 8081);
|
||
const baseUrl = resolvePortalBase(port);
|
||
const reporter = createReporter();
|
||
|
||
console.log('==> 客户下单系统 Page Data 验证');
|
||
console.log(` Portal: ${baseUrl}`);
|
||
console.log(` 用户: ${USER_ID}\n`);
|
||
|
||
const auth = await loginViaApi(baseUrl, { username: 'john4', password: '888888' }, reporter);
|
||
const publishKey = auth.user?.id ?? USER_ID;
|
||
|
||
const formUrl = `${baseUrl}/MindSpace/${publishKey}/public/${FORM_PAGE}`;
|
||
const adminUrl = `${baseUrl}/MindSpace/${publishKey}/public/${ADMIN_PAGE}`;
|
||
|
||
for (const [label, url] of [['下单页', formUrl], ['后台页', adminUrl]]) {
|
||
const res = await fetch(url, { headers: { Cookie: auth.cookie } });
|
||
const html = await res.text();
|
||
if (res.status !== 200) {
|
||
reporter.fail(`${label} HTTP`, `${res.status}`);
|
||
} else {
|
||
reporter.pass(`${label} HTTP 200`, url);
|
||
}
|
||
if (!html.includes('page-data-client.js')) {
|
||
reporter.fail(`${label} 脚本`, '缺少 page-data-client.js');
|
||
} else {
|
||
reporter.pass(`${label} Page Data 客户端`, '已引用');
|
||
}
|
||
}
|
||
|
||
const formMeta = await findPageIdFromPolicy(publishKey);
|
||
if (!formMeta?.pageId) {
|
||
reporter.fail('Page Data policy', '未找到 customer_orders insert policy');
|
||
process.exit(reporter.summary());
|
||
}
|
||
reporter.pass('Page Data policy', `${formMeta.fileName} pageId=${formMeta.pageId}`);
|
||
|
||
const insertPayload = {
|
||
customer_name: '测试客户',
|
||
phone: '13800138000',
|
||
product_name: '演示商品A',
|
||
quantity: '2',
|
||
address: '上海市浦东新区测试路 1 号',
|
||
remark: '自动化验证',
|
||
status: '待处理',
|
||
};
|
||
|
||
const insertRes = await fetch(
|
||
`${baseUrl}/api/public/pages/${formMeta.pageId}/data/${DATASET}/rows`,
|
||
{
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json', Cookie: auth.cookie },
|
||
body: JSON.stringify(insertPayload),
|
||
},
|
||
);
|
||
const insertBody = await insertRes.json().catch(() => ({}));
|
||
if (!insertRes.ok) {
|
||
reporter.fail('下单 insert', `${insertRes.status} ${JSON.stringify(insertBody)}`);
|
||
} else {
|
||
reporter.pass('下单 insert', `row id=${insertBody?.data?.id ?? insertBody?.id ?? 'ok'}`);
|
||
}
|
||
|
||
const adminMeta = await (async () => {
|
||
const policyDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'page-data-policies');
|
||
const entries = await fs.readdir(policyDir);
|
||
for (const name of entries.filter((item) => item.endsWith('.json'))) {
|
||
const policy = JSON.parse(await fs.readFile(path.join(policyDir, name), 'utf8'));
|
||
if (policy?.datasets?.[DATASET]?.read) return policy.pageId;
|
||
}
|
||
return null;
|
||
})();
|
||
|
||
if (!adminMeta) {
|
||
reporter.fail('后台 policy', '未找到 read policy');
|
||
} else {
|
||
const tokenRes = await fetch(`${baseUrl}/api/public/pages/${adminMeta}/data-auth`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json', Cookie: auth.cookie },
|
||
body: JSON.stringify({ password: '88888888' }),
|
||
});
|
||
const tokenBody = await tokenRes.json().catch(() => ({}));
|
||
const token = tokenBody?.data?.token ?? tokenBody?.token;
|
||
if (!tokenRes.ok || !token) {
|
||
reporter.fail('后台口令认证', `${tokenRes.status}`);
|
||
} else {
|
||
const listRes = await fetch(
|
||
`${baseUrl}/api/public/pages/${adminMeta}/data/${DATASET}?limit=20`,
|
||
{ headers: { 'x-page-data-token': token, Cookie: auth.cookie } },
|
||
);
|
||
const listBody = await listRes.json().catch(() => ({}));
|
||
const rows = listBody?.data?.rows ?? listBody?.rows ?? [];
|
||
const hit = rows.some((row) => row.customer_name === insertPayload.customer_name && row.product_name === insertPayload.product_name);
|
||
if (!listRes.ok || !hit) {
|
||
reporter.fail('后台读订单', `rows=${rows.length}, 未找到测试订单`);
|
||
} else {
|
||
reporter.pass('后台读订单', `共 ${rows.length} 条,含测试订单`);
|
||
}
|
||
}
|
||
}
|
||
|
||
const sqlitePath = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'private-data.sqlite');
|
||
try {
|
||
await fs.stat(sqlitePath);
|
||
reporter.pass('用户数据空间', 'private-data.sqlite 存在');
|
||
} catch {
|
||
reporter.pass('用户数据空间', 'PostgreSQL 模式(无本地 sqlite 文件)');
|
||
}
|
||
|
||
console.log('\n--- 访问入口 ---');
|
||
console.log(`下单页: ${formUrl}`);
|
||
console.log(`后台页: ${adminUrl} (口令 88888888)`);
|
||
|
||
process.exit(reporter.summary());
|
||
}
|
||
|
||
main().catch((err) => {
|
||
console.error(err instanceof Error ? err.stack ?? err.message : err);
|
||
process.exit(1);
|
||
});
|