mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
fix(ci): allow cold Chrome startup in runtime fixtures
Let the fixture lifecycle own CDP readiness with the same 60-second startup deadline as the broader browser verifier. Return the discovered port to both consumers and clean up failed startup before returning a lease. Validation: five startup/cleanup boundary tests, npm run check, and real Chrome 154 runtime and source-map CLI/MCP verifiers passed on macOS arm64. Hosted Linux verification follows the push.
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { TraceMap, eachMapping } from "@jridgewell/trace-mapping";
|
||||
import { CdpBrowserProvider } from "../../dist/browser/CdpBrowserProvider.js";
|
||||
import { CdpConnection } from "../../dist/browser/CdpConnection.js";
|
||||
import { waitForBrowserDevtoolsPort } from "../../dist/browser/BrowserProcessStartup.js";
|
||||
import { parseEvidence } from "../../dist/domain/evidence.js";
|
||||
import { traceSourceMap } from "../../dist/javascript/sourceMaps/TraceSourceMap.js";
|
||||
import { startBrowserSourceMapSite } from "../fixtures/browser-source-map-site.mjs";
|
||||
@@ -63,7 +61,6 @@ async function verifyCase(executable, entrypoint, scenario) {
|
||||
let fixture;
|
||||
let connection;
|
||||
let primaryError;
|
||||
let stderr = "";
|
||||
let phase = "native-map-admission";
|
||||
let browserVersion;
|
||||
let expected;
|
||||
@@ -90,17 +87,7 @@ async function verifyCase(executable, entrypoint, scenario) {
|
||||
|
||||
phase = "owned-browser-startup";
|
||||
fixture = await startRuntimeFixtureBrowser(executable, site.origin);
|
||||
fixture.browser.stderr.on("data", (chunk) => {
|
||||
if (stderr.length < 65536) stderr += chunk;
|
||||
});
|
||||
const port = await waitForBrowserDevtoolsPort({
|
||||
child: fixture.browser,
|
||||
executable,
|
||||
activePortPath: join(fixture.profile, "DevToolsActivePort"),
|
||||
stderr: () => stderr,
|
||||
timeoutMs: 20_000,
|
||||
});
|
||||
const endpoint = `http://127.0.0.1:${port}`;
|
||||
const endpoint = `http://127.0.0.1:${fixture.port}`;
|
||||
const provider = new CdpBrowserProvider();
|
||||
const targets = await provider.listTargets({
|
||||
cdp_endpoint: endpoint,
|
||||
|
||||
@@ -3,14 +3,12 @@ import { spawn, execFile } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { once } from "node:events";
|
||||
import { access } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { promisify } from "node:util";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { CdpBrowserProvider } from "../../dist/browser/CdpBrowserProvider.js";
|
||||
import { CdpConnection } from "../../dist/browser/CdpConnection.js";
|
||||
import { waitForBrowserDevtoolsPort } from "../../dist/browser/BrowserProcessStartup.js";
|
||||
import { parseEvidence } from "../../dist/domain/evidence.js";
|
||||
import { webExecutionSchema } from "../../dist/domain/webExecution.js";
|
||||
import { webEventListenersSchema } from "../../dist/domain/webEventListeners.js";
|
||||
@@ -42,23 +40,11 @@ export async function verifyBrowserRuntime(
|
||||
const site = await startBrowserRuntimeSite();
|
||||
let fixture;
|
||||
let primaryError;
|
||||
let stderr = "";
|
||||
let action;
|
||||
let client;
|
||||
try {
|
||||
fixture = await startRuntimeFixtureBrowser(executable, site.origin);
|
||||
const { browser, profile } = fixture;
|
||||
browser.stderr.on("data", (chunk) => {
|
||||
if (stderr.length < 65536) stderr += chunk;
|
||||
});
|
||||
const port = await waitForBrowserDevtoolsPort({
|
||||
child: browser,
|
||||
executable,
|
||||
activePortPath: join(profile, "DevToolsActivePort"),
|
||||
stderr: () => stderr,
|
||||
timeoutMs: 20_000,
|
||||
});
|
||||
const endpoint = `http://127.0.0.1:${port}`;
|
||||
const endpoint = `http://127.0.0.1:${fixture.port}`;
|
||||
const targets = await new CdpBrowserProvider().listTargets({
|
||||
cdp_endpoint: endpoint,
|
||||
allowed_origins: [site.origin],
|
||||
|
||||
@@ -8,8 +8,9 @@ import { spawnOwnedProviderProcess } from "../../dist/process/ProviderProcess.js
|
||||
import { createSystemProcessOwnershipHost } from "../../dist/process/ProcessOwnershipObservation.js";
|
||||
import { liveProcesses } from "../../dist/process/ProcessOwnershipProcessTree.js";
|
||||
import { CdpConnection } from "../../dist/browser/CdpConnection.js";
|
||||
import { waitForBrowserDevtoolsPort } from "../../dist/browser/BrowserProcessStartup.js";
|
||||
|
||||
/** Launch only the synthetic fixture browser in its own verified process group. */
|
||||
/** Launch the synthetic fixture browser and await CDP in its own verified process group. */
|
||||
export async function startRuntimeFixtureBrowser(executable, origin) {
|
||||
const profile = await mkdtemp(join(tmpdir(), "rea-real-web-runtime-"));
|
||||
let owned;
|
||||
@@ -38,9 +39,21 @@ export async function startRuntimeFixtureBrowser(executable, origin) {
|
||||
expectedCommand: null,
|
||||
});
|
||||
owned.process.stdout?.resume();
|
||||
let stderr = "";
|
||||
owned.process.stderr?.on("data", (chunk) => {
|
||||
if (stderr.length < 65536) stderr += chunk;
|
||||
});
|
||||
const port = await waitForBrowserDevtoolsPort({
|
||||
child: owned.process,
|
||||
executable,
|
||||
activePortPath: join(profile, "DevToolsActivePort"),
|
||||
stderr: () => stderr,
|
||||
// Match the broader real-browser verifier's allowance for cold Chrome
|
||||
// startup on shared runners; early process failures still reject at once.
|
||||
timeoutMs: 60_000,
|
||||
});
|
||||
return {
|
||||
browser: owned.process,
|
||||
profile,
|
||||
port,
|
||||
close: async () => {
|
||||
await stopOwnedBrowser(owned, profile);
|
||||
await rm(profile, {
|
||||
|
||||
@@ -1,6 +1,49 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { readdir } from "node:fs/promises";
|
||||
import { promisify } from "node:util";
|
||||
import { expect, it } from "vitest";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"rejects an exited fixture before returning a lease and removes its profile",
|
||||
async () => {
|
||||
const root = await createTestTempDirectory("rea-runtime-startup-failure-");
|
||||
const helper = new URL(
|
||||
"../../../scripts/lib/browser-runtime-fixture-lifecycle.mjs",
|
||||
import.meta.url,
|
||||
);
|
||||
const script = `
|
||||
const { startRuntimeFixtureBrowser } = await import(${JSON.stringify(helper.href)});
|
||||
// Node rejects Chrome's arguments immediately, exercising actual process exit.
|
||||
try {
|
||||
const fixture = await startRuntimeFixtureBrowser(process.execPath, 'http://127.0.0.1');
|
||||
await fixture.close();
|
||||
throw new Error('Expected startup failure');
|
||||
} catch (error) {
|
||||
if (error._tag !== 'BrowserStartupError') throw error;
|
||||
console.log(JSON.stringify({
|
||||
failure: error.failure,
|
||||
exitCode: error.exitCode,
|
||||
stderr: error.stderr,
|
||||
}));
|
||||
}
|
||||
`;
|
||||
const { stdout } = await promisify(execFile)(
|
||||
process.execPath,
|
||||
["--input-type=module", "-e", script],
|
||||
{
|
||||
env: { ...process.env, TMPDIR: root, TMP: root, TEMP: root },
|
||||
timeout: 10_000,
|
||||
},
|
||||
);
|
||||
expect(JSON.parse(stdout)).toEqual({
|
||||
failure: "exited",
|
||||
exitCode: expect.any(Number),
|
||||
stderr: expect.stringContaining("bad option"),
|
||||
});
|
||||
expect(await readdir(root)).toEqual([]);
|
||||
},
|
||||
);
|
||||
|
||||
it("attempts every fixture cleanup and retains the primary capture failure", async () => {
|
||||
const helper = new URL(
|
||||
|
||||
Reference in New Issue
Block a user