diff --git a/scripts/test-live-shard.mts b/scripts/test-live-shard.mts index acc137b99ffb..cc4896303539 100644 --- a/scripts/test-live-shard.mts +++ b/scripts/test-live-shard.mts @@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url"; import { asSafeIntegerInRange } from "../packages/normalization-core/src/number-coercion.ts"; import { isRecord as isUnknownRecord } from "../packages/normalization-core/src/record-coerce.ts"; import { parsePermissiveBooleanToken } from "./lib/arg-utils.mts"; +import { RUNTIME_POSTBUILD_STAMP_FILE } from "./lib/local-build-metadata-paths.mts"; import { spawnPnpmRunner, type PnpmRunnerParams } from "./pnpm-runner.mts"; import { createVitestProcessCompletion, @@ -53,7 +54,14 @@ const OPTIONAL_LIVE_SHARD_FILE_ENVS = new Map([ const SKIPPED_ASSERTION_STATUSES = new Set(["disabled", "pending", "skipped", "todo"]); const QA_RUNTIME_LIVE_TEST = "extensions/qa-lab/src/matrix-channel-driver.lifecycle.live.test.ts"; const QA_RUNTIME_ARTIFACT = "dist/extensions/qa-lab/runtime-api.js"; +const SOURCE_PERFORMANCE_ARTIFACT = `dist/${RUNTIME_POSTBUILD_STAMP_FILE}`; type ProcessSignal = `SIG${string}`; +type LiveShardPreparation = { + env: NodeJS.ProcessEnv; + profile: string; + requiredArtifact: string; + runtimeEnv?: NodeJS.ProcessEnv; +}; /** Live-test shards included in release validation. */ export const RELEASE_LIVE_TEST_SHARDS = Object.freeze([ @@ -387,14 +395,29 @@ export function buildLiveShardPnpmArgs(files: string[], passthroughArgs: string[ /** * Resolves build profiles required by selected live tests. */ -export function resolveLiveShardPreparation(files: string[]) { - return files.includes(QA_RUNTIME_LIVE_TEST) - ? { - env: { OPENCLAW_BUILD_PRIVATE_QA: "1" }, - profile: "qaRuntime", - requiredArtifact: QA_RUNTIME_ARTIFACT, - } - : null; +export function resolveLiveShardPreparation(files: string[]): LiveShardPreparation | null { + if (files.some(isGatewayProfilesLiveTest)) { + return { + env: {}, + profile: "sourcePerformance", + requiredArtifact: SOURCE_PERFORMANCE_ARTIFACT, + runtimeEnv: { + OPENCLAW_DISABLE_BONJOUR: "1", + OPENCLAW_GATEWAY_STARTUP_TRACE: "1", + OPENCLAW_LIVE_TEST_QUIET: "0", + OPENCLAW_LOG_LEVEL: "info", + OPENCLAW_PLUGIN_LIFECYCLE_TRACE: "1", + }, + }; + } + if (files.includes(QA_RUNTIME_LIVE_TEST)) { + return { + env: { OPENCLAW_BUILD_PRIVATE_QA: "1" }, + profile: "qaRuntime", + requiredArtifact: QA_RUNTIME_ARTIFACT, + }; + } + return null; } /** @@ -661,10 +684,14 @@ function validateLiveShardReport( /** * Builds spawn options for the live-shard Vitest child. */ -export function buildLiveShardSpawnParams(env = process.env, platform = process.platform) { +export function buildLiveShardSpawnParams( + env = process.env, + platform = process.platform, + runtimeEnv?: NodeJS.ProcessEnv, +) { return { detached: shouldUseDetachedVitestProcessGroup(platform), - env, + env: { ...env, ...runtimeEnv }, stdio: "inherit", } satisfies Pick; } @@ -750,7 +777,11 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me const reportPath = buildLiveShardReportPath(shard, process.env); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); removeLiveShardReportFile(reportPath); - const spawnParams = buildLiveShardSpawnParams(process.env); + const spawnParams = buildLiveShardSpawnParams( + process.env, + process.platform, + preparation?.runtimeEnv, + ); const child = spawnPnpmRunner({ pnpmArgs: buildLiveShardPnpmArgs(files, addLiveShardReportArgs(passthroughArgs, reportPath)), ...spawnParams, diff --git a/test/scripts/test-live-shard.test.ts b/test/scripts/test-live-shard.test.ts index 3c901e0f91d5..2420aef0ee10 100644 --- a/test/scripts/test-live-shard.test.ts +++ b/test/scripts/test-live-shard.test.ts @@ -244,6 +244,28 @@ describe("scripts/test-live-shard", () => { ).toBeNull(); }); + it("prepares gateway profile shards with observable source-runtime diagnostics", () => { + const preparation = resolveLiveShardPreparation( + selectLiveShardFiles("native-live-src-gateway-profiles", allFiles), + ); + + expect(preparation).toEqual({ + env: {}, + profile: "sourcePerformance", + requiredArtifact: "dist/.runtime-postbuildstamp", + runtimeEnv: { + OPENCLAW_DISABLE_BONJOUR: "1", + OPENCLAW_GATEWAY_STARTUP_TRACE: "1", + OPENCLAW_LIVE_TEST_QUIET: "0", + OPENCLAW_LOG_LEVEL: "info", + OPENCLAW_PLUGIN_LIFECYCLE_TRACE: "1", + }, + }); + expect( + resolveLiveShardPreparation(selectLiveShardFiles("native-live-src-gateway-core", allFiles)), + ).toBeNull(); + }); + it("fails live shard reports with no passing tests", () => { expect(validateLiveShardReportPayload({ numPassedTests: 1, numTotalTests: 3 })).toEqual({ ok: true, @@ -558,6 +580,26 @@ describe("scripts/test-live-shard", () => { env: { PATH: "/usr/bin" }, stdio: "inherit", }); + expect( + buildLiveShardSpawnParams({ OPENCLAW_LOG_LEVEL: "warn", PATH: "/usr/bin" }, "darwin", { + OPENCLAW_DISABLE_BONJOUR: "1", + OPENCLAW_GATEWAY_STARTUP_TRACE: "1", + OPENCLAW_LIVE_TEST_QUIET: "0", + OPENCLAW_LOG_LEVEL: "info", + OPENCLAW_PLUGIN_LIFECYCLE_TRACE: "1", + }), + ).toEqual({ + detached: true, + env: { + OPENCLAW_DISABLE_BONJOUR: "1", + OPENCLAW_GATEWAY_STARTUP_TRACE: "1", + OPENCLAW_LIVE_TEST_QUIET: "0", + OPENCLAW_LOG_LEVEL: "info", + OPENCLAW_PLUGIN_LIFECYCLE_TRACE: "1", + PATH: "/usr/bin", + }, + stdio: "inherit", + }); }); it.skipIf(process.platform === "win32")(