Files
memind/scripts/run-wechat-gate-evidence.mjs
T
john 570ecbd1b0 feat(wechat): add WX gate evidence and v1.49 103 cutover runbook
Prioritize scheduled-task intent over page.generate for automation phrases, add run-wechat-gate-evidence.mjs for WX-01..13, and document 103 maintenance-window steps for Goose v1.49.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 15:01:32 +08:00

159 lines
5.3 KiB
JavaScript

#!/usr/bin/env node
/**
* WX-01..WX-13 release-gate evidence + optional live WeChat webhook smoke.
*
* Deterministic suites mirror release-gate/coverage.mjs:
* - wechat-channel-contract → WX-01..08 (+ FILE-08 overlap)
* - wechat-terminal-and-atomic → WX-09..13
* - wechat-native-image-delivery → WX-05
*
* Live smoke (optional): POST /webhooks/wechat-mp/messages via run-wechat-scenario-test.mjs
*/
import { spawnSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const DETERMINISTIC_SUITES = [
{
id: 'wechat-channel-contract',
scenarios: ['WX-01', 'WX-02', 'WX-03', 'WX-04', 'WX-06', 'WX-07', 'WX-08'],
command: [
'--test',
'wechat-mp.test.mjs',
'wechat-oauth.test.mjs',
'wechat-pay.test.mjs',
'wechat-media.test.mjs',
'wechat/wechat-channel.test.mjs',
'wechat/image-generation-policy.test.mjs',
'mindspace-chat-docx-package.test.mjs',
'mindspace-html-download-links.test.mjs',
'mindspace-public-asset-token.test.mjs',
'server/portal-mindspace-asset-routes.test.mjs',
],
},
{
id: 'wechat-terminal-and-atomic-delivery',
scenarios: ['WX-09', 'WX-10', 'WX-11', 'WX-12', 'WX-13'],
command: [
'--test',
'user-auth.test.mjs',
'wechat-mp.test.mjs',
'wechat-media.test.mjs',
'wechat/wechat-channel.test.mjs',
'chat-image-turn-scope.test.mjs',
'tkmind-proxy.test.mjs',
],
},
{
id: 'wechat-native-image-delivery',
scenarios: ['WX-05'],
command: ['--test', 'wechat-media.test.mjs'],
},
];
function runNode(args, label) {
const result = spawnSync(process.execPath, args, {
cwd: root,
encoding: 'utf8',
env: process.env,
});
if (result.status !== 0) {
const detail = result.stderr?.trim() || result.stdout?.trim() || `${label} exited ${result.status}`;
throw new Error(`${label} failed: ${detail.slice(-2000)}`);
}
return result.stdout?.trim() ?? '';
}
async function assertLiveEnv() {
const { loadH5Environment } = await import('./load-env.mjs');
loadH5Environment(import.meta.dirname);
const missing = ['H5_WECHAT_MP_TOKEN', 'H5_WECHAT_MP_APP_ID', 'DATABASE_URL']
.filter((key) => !String(process.env[key] ?? '').trim());
if (missing.length) {
throw new Error(
`live smoke 缺少环境变量: ${missing.join(', ')}。请在 .env.local 配置(见 docs/local-dev.md §公众号 Agent 调试)`,
);
}
}
function parseArgs(argv) {
const options = {
live: false,
liveOnly: false,
liveScenario: 'poem-page',
liveAll: false,
skipDeterministic: false,
};
for (let index = 2; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === '--live') options.live = true;
else if (arg === '--live-only') {
options.live = true;
options.liveOnly = true;
options.skipDeterministic = true;
} else if (arg === '--live-all') {
options.live = true;
options.liveAll = true;
} else if (arg === '--live-scenario' && argv[index + 1]) {
options.live = true;
options.liveScenario = argv[++index];
} else if (arg === '--skip-deterministic') options.skipDeterministic = true;
else if (arg === '-h' || arg === '--help') {
console.log(`Usage:
node scripts/run-wechat-gate-evidence.mjs
node scripts/run-wechat-gate-evidence.mjs --live-only --live-scenario poem-page
node scripts/run-wechat-gate-evidence.mjs --live-only --live-all
node scripts/run-wechat-gate-evidence.mjs --skip-deterministic --live-only --live-scenario chat-general
Deterministic: WX-01..WX-13 unit/contract suites (no LLM).
Live smoke: Portal :8081 + DATABASE_URL + H5_WECHAT_MP_TOKEN + H5_WECHAT_MP_APP_ID.`);
process.exit(0);
} else {
throw new Error(`Unknown argument: ${arg}`);
}
}
return options;
}
async function main() {
const options = parseArgs(process.argv);
const covered = new Set();
if (!options.skipDeterministic) {
runNode(['--test', 'wechat-intent-router.test.mjs'], 'wechat-intent-router');
for (const suite of DETERMINISTIC_SUITES) {
runNode(suite.command, suite.id);
for (const scenarioId of suite.scenarios) covered.add(scenarioId);
console.log(`[wechat-gate] PASS ${suite.id}${suite.scenarios.join(', ')}`);
}
}
if (options.live) {
await assertLiveEnv();
const liveArgs = ['scripts/run-wechat-scenario-test.mjs'];
if (options.liveAll) liveArgs.push('--all');
else liveArgs.push('--scenario', options.liveScenario);
runNode(liveArgs, 'wechat-live-smoke');
console.log('[wechat-gate] PASS live smoke');
}
console.log('WECHAT_GATE_EVIDENCE_OK:');
console.log(` deterministic=${options.skipDeterministic ? 'skipped' : 'WX-01..WX-13'}`);
console.log(` live=${options.live ? (options.liveAll ? 'all-scenarios' : options.liveScenario) : 'not-run'}`);
if (covered.size) {
console.log(` scenarios=${[...covered].sort().join(',')}`);
}
if (!options.live) {
console.log(' note=add --live-only --live-scenario poem-page for WX-04/13 webhook E2E');
} else if (options.liveOnly) {
console.log(' mode=live-only');
}
}
main().catch((error) => {
console.error(`WECHAT_GATE_EVIDENCE_FAIL: ${error.message}`);
process.exit(1);
});