#!/usr/bin/env node import { spawn } from 'node:child_process'; import path from 'node:path'; import { createLocalGateStack, grantGateUserSkillsByUsername, selectBackendLlmProvider, seedSelectedProviderKeys, } from '../release-gate/local-stack.mjs'; const root = path.resolve(new URL('..', import.meta.url).pathname); const scenarioIds = [ 'john2-suzhou-page', 'long-content-rich-page', ]; async function runScenario(scenarioId, port) { const childTimeoutMs = Math.max( 60_000, Number(process.env.RELEASE_GATE_SCENARIO_PROCESS_TIMEOUT_MS ?? 900_000) || 900_000, ); const stepTimeoutMs = Math.max( 60_000, Number(process.env.RELEASE_GATE_SCENARIO_TIMEOUT_MS ?? 900_000) || 900_000, ); const code = await new Promise((resolve, reject) => { const child = spawn( process.execPath, ['scripts/run-scenario-test.mjs', '--scenario', scenarioId, '--port', String(port)], { cwd: root, env: { ...process.env, JOHN_PASSWORD: '888888', RELEASE_GATE_SCENARIO_TIMEOUT_MS: String(stepTimeoutMs), }, stdio: 'inherit', }, ); const timeout = setTimeout(() => { child.kill('SIGTERM'); setTimeout(() => child.kill('SIGKILL'), 3_000).unref(); }, childTimeoutMs); timeout.unref(); child.once('error', reject); child.once('close', (status) => { clearTimeout(timeout); resolve(status ?? 1); }); }); return code; } const runId = `page-${new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14)}`; const stack = await createLocalGateStack({ root, runId, port: Number(process.env.MEMIND_RELEASE_GATE_PAGE_PORT ?? 19087), portalRoot: path.join(root, '.runtime', 'portal'), nodeEnv: 'test', runtimeProfile: 'local', }); await seedSelectedProviderKeys({ sourceUrl: stack.sourceDatabaseUrl, targetUrl: stack.mysqlUrl }); await selectBackendLlmProvider({ targetUrl: stack.mysqlUrl }); async function register(username) { const response = await fetch(`${stack.baseUrl}/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password: '888888', displayName: `Release Gate ${username}`, email: `${username}@example.invalid`, }), }); if (!response.ok) throw new Error(`register ${username} failed: ${response.status}`); } try { await register('john2'); await grantGateUserSkillsByUsername({ targetUrl: stack.mysqlUrl, username: 'john2', skillNames: ['static-page-publish'], }); const results = []; for (const scenarioId of scenarioIds) { results.push({ scenarioId, code: await runScenario(scenarioId, new URL(stack.baseUrl).port), }); } for (const result of results) { console.log(`${result.code === 0 ? 'PASS' : 'FAIL'} ${result.scenarioId}`); } if (results.some((result) => result.code !== 0)) process.exitCode = 1; } finally { await stack.cleanup(); }