Files
memind/scripts/verify-customer-order-system.mjs
john dab6140f99 feat: Page Data dev loop, adm Code Run UI, and customer order demo
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>
2026-07-23 22:11:18 +08:00

156 lines
5.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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);
});