#!/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); });