mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(test): bound direct gateway server runs (#119184)
This commit is contained in:
committed by
GitHub
parent
121c6c1042
commit
b5d41f9005
@@ -5,6 +5,10 @@ import {
|
||||
embeddedAgentVitestProjectOwners,
|
||||
} from "../../test/vitest/vitest.agents-paths.mjs";
|
||||
import { commandsLightTestFiles } from "../../test/vitest/vitest.commands-light-paths.mjs";
|
||||
import {
|
||||
isGatewayServerBackedHttpTestFile,
|
||||
isGatewayServerTestFile,
|
||||
} from "../../test/vitest/vitest.gateway-server-paths.mjs";
|
||||
import { fullSuiteVitestShards } from "../../test/vitest/vitest.test-shards.mjs";
|
||||
import { toolingIsolatedTestFiles } from "../../test/vitest/vitest.tooling-isolated-paths.mjs";
|
||||
import {
|
||||
@@ -612,29 +616,6 @@ function createAgentCoreSplitShards() {
|
||||
];
|
||||
}
|
||||
|
||||
const GATEWAY_SERVER_BACKED_HTTP_TESTS = new Set([
|
||||
"src/gateway/embeddings-http.test.ts",
|
||||
"src/gateway/models-http.test.ts",
|
||||
"src/gateway/openai-http.test.ts",
|
||||
"src/gateway/openresponses-http.test.ts",
|
||||
"src/gateway/probe.auth.integration.test.ts",
|
||||
]);
|
||||
|
||||
const GATEWAY_SERVER_EXCLUDED_TESTS = new Set([
|
||||
"src/gateway/gateway.test.ts",
|
||||
"src/gateway/server.startup-matrix-migration.integration.test.ts",
|
||||
"src/gateway/sessions-history-http.test.ts",
|
||||
]);
|
||||
|
||||
function isGatewayServerTestFile(file) {
|
||||
return (
|
||||
file.startsWith("src/gateway/") &&
|
||||
!file.startsWith("src/gateway/server-methods/") &&
|
||||
!GATEWAY_SERVER_EXCLUDED_TESTS.has(file) &&
|
||||
(file.includes("server") || GATEWAY_SERVER_BACKED_HTTP_TESTS.has(file))
|
||||
);
|
||||
}
|
||||
|
||||
function resolveGatewayStartupShardName(file) {
|
||||
const name = relative("src/gateway", file).replaceAll("\\", "/");
|
||||
if (name.startsWith("server-startup-config") || name.startsWith("server-startup-early")) {
|
||||
@@ -643,9 +624,7 @@ function resolveGatewayStartupShardName(file) {
|
||||
if (
|
||||
name.startsWith("server-runtime") ||
|
||||
name.startsWith("server.health") ||
|
||||
name.startsWith("server.lazy") ||
|
||||
name.startsWith("server/health-state") ||
|
||||
name.startsWith("server/readiness")
|
||||
name.startsWith("server.lazy")
|
||||
) {
|
||||
return "agentic-control-plane-startup-health-runtime";
|
||||
}
|
||||
@@ -658,7 +637,7 @@ function resolveGatewayStartupShardName(file) {
|
||||
function resolveGatewayServerShardName(file) {
|
||||
const name = relative("src/gateway", file).replaceAll("\\", "/");
|
||||
if (
|
||||
GATEWAY_SERVER_BACKED_HTTP_TESTS.has(file) ||
|
||||
isGatewayServerBackedHttpTestFile(file) ||
|
||||
name.startsWith("server.models") ||
|
||||
name.startsWith("server.talk")
|
||||
) {
|
||||
@@ -688,8 +667,6 @@ function resolveGatewayServerShardName(file) {
|
||||
name.startsWith("server-runtime") ||
|
||||
name.startsWith("server.lazy") ||
|
||||
name.startsWith("server.health") ||
|
||||
name.startsWith("server/health-state") ||
|
||||
name.startsWith("server/readiness") ||
|
||||
name === "server-close.test.ts"
|
||||
) {
|
||||
return resolveGatewayStartupShardName(file);
|
||||
@@ -725,10 +702,7 @@ function resolveGatewayServerShardName(file) {
|
||||
) {
|
||||
return "agentic-control-plane-runtime-ui-tools";
|
||||
}
|
||||
if (name.startsWith("server/")) {
|
||||
return "agentic-control-plane-runtime-events";
|
||||
}
|
||||
if (name.startsWith("server.") || name.startsWith("server/")) {
|
||||
if (name.startsWith("server.")) {
|
||||
return "agentic-control-plane-runtime-state";
|
||||
}
|
||||
return "agentic-control-plane-runtime";
|
||||
@@ -748,7 +722,6 @@ function createGatewayServerSplitShards() {
|
||||
"agentic-control-plane-runtime",
|
||||
"agentic-control-plane-runtime-config",
|
||||
"agentic-control-plane-runtime-cron",
|
||||
"agentic-control-plane-runtime-events",
|
||||
"agentic-control-plane-runtime-network",
|
||||
"agentic-control-plane-runtime-server",
|
||||
"agentic-control-plane-runtime-shared-token",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export const GATEWAY_SERVER_TEST_PROCESS_COUNT: 4;
|
||||
export function listGatewayServerTestTargets(cwd?: string): string[];
|
||||
export function splitTestTargetChunks(targets: string[], chunkCount: number): string[][];
|
||||
export function createGatewayServerTestTargetChunks(cwd?: string): string[][];
|
||||
@@ -0,0 +1,72 @@
|
||||
// Bounds the non-isolated Gateway server project before its shared module graph
|
||||
// reaches the V8 worker heap limit.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { isGatewayServerTestFile } from "../../test/vitest/vitest.gateway-server-paths.mjs";
|
||||
|
||||
export const GATEWAY_SERVER_TEST_PROCESS_COUNT = 4;
|
||||
|
||||
function normalizePath(value) {
|
||||
return value.replaceAll("\\", "/");
|
||||
}
|
||||
|
||||
function listRepoFilesRecursive(root, cwd) {
|
||||
const entries = fs.readdirSync(root, { withFileTypes: true });
|
||||
return entries.flatMap((entry) => {
|
||||
const absolute = path.join(root, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
return listRepoFilesRecursive(absolute, cwd);
|
||||
}
|
||||
return entry.isFile() ? [normalizePath(path.relative(cwd, absolute))] : [];
|
||||
});
|
||||
}
|
||||
|
||||
function listGatewayFilesFromGit(cwd) {
|
||||
const result = spawnSync("git", ["ls-files", "--", "src/gateway"], {
|
||||
cwd,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
return null;
|
||||
}
|
||||
return result.stdout
|
||||
.split("\n")
|
||||
.map((line) => normalizePath(line.trim()))
|
||||
.filter((line) => line.length > 0);
|
||||
}
|
||||
|
||||
export function listGatewayServerTestTargets(cwd = process.cwd()) {
|
||||
const gatewayDir = path.join(cwd, "src/gateway");
|
||||
if (!fs.existsSync(gatewayDir)) {
|
||||
return [];
|
||||
}
|
||||
return (listGatewayFilesFromGit(cwd) ?? listRepoFilesRecursive(gatewayDir, cwd))
|
||||
.filter(isGatewayServerTestFile)
|
||||
.toSorted((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
export function splitTestTargetChunks(targets, chunkCount) {
|
||||
if (targets.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const normalizedChunkCount = Math.min(chunkCount, targets.length);
|
||||
const baseSize = Math.floor(targets.length / normalizedChunkCount);
|
||||
const remainder = targets.length % normalizedChunkCount;
|
||||
const chunks = [];
|
||||
let offset = 0;
|
||||
for (let index = 0; index < normalizedChunkCount; index += 1) {
|
||||
const chunkSize = baseSize + (index < remainder ? 1 : 0);
|
||||
chunks.push(targets.slice(offset, offset + chunkSize));
|
||||
offset += chunkSize;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
export function createGatewayServerTestTargetChunks(cwd = process.cwd()) {
|
||||
return splitTestTargetChunks(
|
||||
listGatewayServerTestTargets(cwd),
|
||||
GATEWAY_SERVER_TEST_PROCESS_COUNT,
|
||||
);
|
||||
}
|
||||
@@ -41,6 +41,14 @@ export function resolveMissingExplicitTestFiles(
|
||||
fsImpl?: VitestFs,
|
||||
): string[];
|
||||
export function resolveImplicitVitestArgs(argv: string[], cwd?: string): string[];
|
||||
export function resolveBoundedVitestInvocations(
|
||||
argv: string[],
|
||||
options?: {
|
||||
cwd?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
gatewayServerTargetChunks?: string[][];
|
||||
},
|
||||
): string[][];
|
||||
export function installVitestNoOutputWatchdog(params: {
|
||||
streams?: Array<{ on(event: string, listener: (...args: unknown[]) => void): unknown } | null>;
|
||||
timeoutMs: number | null;
|
||||
|
||||
+93
-20
@@ -9,6 +9,7 @@ import { toolingIsolatedTestFiles } from "../test/vitest/vitest.tooling-isolated
|
||||
import { isUiTestTarget } from "../test/vitest/vitest.ui-paths.mjs";
|
||||
import { boundaryTestFiles } from "../test/vitest/vitest.unit-paths.mjs";
|
||||
import { runWithFailedTrailer, writeFailedTrailer } from "./lib/failed-trailer.mjs";
|
||||
import { createGatewayServerTestTargetChunks } from "./lib/gateway-server-test-plan.mjs";
|
||||
import { signalExitCode } from "./lib/managed-child-process.mjs";
|
||||
import { resolveRepoRoot } from "./lib/repo-root.mjs";
|
||||
import { resolveLocalVitestEnv } from "./lib/vitest-local-scheduling.mjs";
|
||||
@@ -126,6 +127,16 @@ const VITEST_DOTTED_OPTIONS_WITH_VALUE_PREFIXES = [
|
||||
"--sequence.",
|
||||
"--typecheck.",
|
||||
];
|
||||
const UNBOUNDED_CONFIG_ONLY_OPTIONS = [
|
||||
"--changed",
|
||||
"--coverage",
|
||||
"--dir",
|
||||
"--mergeReports",
|
||||
"--outputFile",
|
||||
"--project",
|
||||
"--root",
|
||||
"--shard",
|
||||
];
|
||||
const require = createRequire(import.meta.url);
|
||||
const repoRoot = resolveRepoRoot(import.meta.url);
|
||||
const testProjectsRunnerPath = path.join(repoRoot, "scripts", "test-projects.mjs");
|
||||
@@ -427,15 +438,66 @@ function resolveVitestConfigArg(argv) {
|
||||
}
|
||||
|
||||
function resolveVitestConfigNoOutputTimeoutMs(config) {
|
||||
const normalized = path.normalize(config).replaceAll(path.sep, "/").replace(/^\.\//u, "");
|
||||
const normalized = normalizeVitestConfigPath(config);
|
||||
for (const [candidate, timeoutMs] of VITEST_CONFIG_NO_OUTPUT_TIMEOUT_MS) {
|
||||
if (normalized === candidate || normalized.endsWith(`/${candidate}`)) {
|
||||
if (matchesVitestConfigPath(normalized, candidate)) {
|
||||
return timeoutMs;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeVitestConfigPath(config) {
|
||||
return path.normalize(config).replaceAll(path.sep, "/").replace(/^\.\//u, "");
|
||||
}
|
||||
|
||||
function matchesVitestConfigPath(normalized, candidate) {
|
||||
return normalized === candidate || normalized.endsWith("/" + candidate);
|
||||
}
|
||||
|
||||
function hasVitestOption(argv, option) {
|
||||
for (const arg of argv) {
|
||||
if (arg === "--") {
|
||||
return false;
|
||||
}
|
||||
if (arg === option || arg.startsWith(option + "=") || arg.startsWith(option + ".")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function insertVitestTargets(argv, targets) {
|
||||
const separatorIndex = argv.indexOf("--");
|
||||
const insertionIndex = separatorIndex < 0 ? argv.length : separatorIndex;
|
||||
return [...argv.slice(0, insertionIndex), ...targets, ...argv.slice(insertionIndex)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits config-only Gateway server runs into fresh processes before the
|
||||
* non-isolated module graph reaches the worker heap limit.
|
||||
*/
|
||||
export function resolveBoundedVitestInvocations(argv, options = {}) {
|
||||
const config = resolveVitestConfigArg(argv);
|
||||
const normalizedConfig = config === null ? "" : normalizeVitestConfigPath(config);
|
||||
const env = options.env ?? process.env;
|
||||
const cwd = options.cwd ?? process.cwd();
|
||||
const mode = resolveExplicitVitestMode(argv);
|
||||
if (
|
||||
!matchesVitestConfigPath(normalizedConfig, GATEWAY_SERVER_VITEST_CONFIG) ||
|
||||
mode === "watch" ||
|
||||
(mode !== "run" && !isTruthyEnvValue(env.CI)) ||
|
||||
hasNonRunVitestSubcommand(argv) ||
|
||||
hasAlternateVitestRootArg(argv) ||
|
||||
collectExplicitProjectRouterTargetArgs(argv, cwd).length > 0 ||
|
||||
UNBOUNDED_CONFIG_ONLY_OPTIONS.some((option) => hasVitestOption(argv, option))
|
||||
) {
|
||||
return [argv];
|
||||
}
|
||||
const chunks = options.gatewayServerTargetChunks ?? createGatewayServerTestTargetChunks(cwd);
|
||||
return chunks.length > 1 ? chunks.map((targets) => insertVitestTargets(argv, targets)) : [argv];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds spawn options for the primary Vitest child process.
|
||||
*/
|
||||
@@ -1115,9 +1177,11 @@ async function finishVitestProcess({ completion, getForwardedSignal }) {
|
||||
if (exitSignal) {
|
||||
writeFailedTrailer("vitest", signalExitCode(exitSignal));
|
||||
process.kill(process.pid, exitSignal);
|
||||
return;
|
||||
return signalExitCode(exitSignal);
|
||||
}
|
||||
process.exitCode = code ?? 1;
|
||||
const exitCode = code ?? 1;
|
||||
process.exitCode = exitCode;
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
async function main(argv = process.argv.slice(2), env = process.env) {
|
||||
@@ -1146,8 +1210,7 @@ async function main(argv = process.argv.slice(2), env = process.env) {
|
||||
}
|
||||
|
||||
const vitestArgs = resolveImplicitVitestArgs(argv);
|
||||
const guardedVitestArgs = resolveExplicitTestFileNoPassArgs(vitestArgs);
|
||||
const spawnEnv = resolveRunVitestSpawnEnv(env, guardedVitestArgs);
|
||||
const invocations = resolveBoundedVitestInvocations(vitestArgs, { env });
|
||||
let vitestCliEntry;
|
||||
try {
|
||||
vitestCliEntry = resolveVitestCliEntry();
|
||||
@@ -1160,20 +1223,30 @@ async function main(argv = process.argv.slice(2), env = process.env) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
await finishVitestProcess(
|
||||
spawnWatchedVitestProcess({
|
||||
pnpmArgs: [
|
||||
"exec",
|
||||
"node",
|
||||
...resolveVitestNodeArgs(env),
|
||||
vitestCliEntry,
|
||||
...guardedVitestArgs,
|
||||
],
|
||||
spawnParams: resolveVitestSpawnParams(spawnEnv),
|
||||
env: spawnEnv,
|
||||
label: guardedVitestArgs.join(" "),
|
||||
}),
|
||||
);
|
||||
for (let index = 0; index < invocations.length; index += 1) {
|
||||
const guardedVitestArgs = resolveExplicitTestFileNoPassArgs(invocations[index]);
|
||||
const spawnEnv = resolveRunVitestSpawnEnv(env, guardedVitestArgs);
|
||||
if (invocations.length > 1) {
|
||||
console.error("[vitest] Gateway server process " + (index + 1) + "/" + invocations.length);
|
||||
}
|
||||
const exitCode = await finishVitestProcess(
|
||||
spawnWatchedVitestProcess({
|
||||
pnpmArgs: [
|
||||
"exec",
|
||||
"node",
|
||||
...resolveVitestNodeArgs(env),
|
||||
vitestCliEntry,
|
||||
...guardedVitestArgs,
|
||||
],
|
||||
spawnParams: resolveVitestSpawnParams(spawnEnv),
|
||||
env: spawnEnv,
|
||||
label: guardedVitestArgs.join(" "),
|
||||
}),
|
||||
);
|
||||
if (exitCode !== 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
|
||||
@@ -86,6 +86,11 @@ import {
|
||||
} from "./changed-lanes.mjs";
|
||||
import { getChangedPathFacts } from "./lib/changed-path-facts.mjs";
|
||||
import { createExtensionTestProcessTargetChunks } from "./lib/extension-test-plan.mjs";
|
||||
import {
|
||||
GATEWAY_SERVER_TEST_PROCESS_COUNT,
|
||||
listGatewayServerTestTargets,
|
||||
splitTestTargetChunks as splitTargetChunks,
|
||||
} from "./lib/gateway-server-test-plan.mjs";
|
||||
import { isCiLikeEnv, resolveLocalFullSuiteProfile } from "./lib/vitest-local-scheduling.mjs";
|
||||
import {
|
||||
DEFAULT_VITEST_NO_OUTPUT_HEARTBEAT_MS,
|
||||
@@ -723,19 +728,6 @@ export function formatNoChangedTestTargetLines(skippedBroadFallbackPaths) {
|
||||
}
|
||||
|
||||
const EXPLICIT_SOURCE_FULL_IMPORT_GRAPH_THRESHOLD = 12;
|
||||
const GATEWAY_SERVER_FULL_SUITE_TARGET_CHUNK_COUNT = 4;
|
||||
const GATEWAY_SERVER_BACKED_HTTP_TEST_TARGETS = new Set([
|
||||
"src/gateway/embeddings-http.test.ts",
|
||||
"src/gateway/models-http.test.ts",
|
||||
"src/gateway/openai-http.test.ts",
|
||||
"src/gateway/openresponses-http.test.ts",
|
||||
"src/gateway/probe.auth.integration.test.ts",
|
||||
]);
|
||||
const GATEWAY_SERVER_EXCLUDED_TEST_TARGETS = new Set([
|
||||
"src/gateway/gateway.test.ts",
|
||||
"src/gateway/server.startup-matrix-migration.integration.test.ts",
|
||||
"src/gateway/sessions-history-http.test.ts",
|
||||
]);
|
||||
function resolveTestProjectsVitestNoOutputTimeoutMs(config) {
|
||||
const directRunnerTimeoutMs = resolveDefaultVitestNoOutputTimeoutMs(["run", "--config", config]);
|
||||
return String(
|
||||
@@ -776,63 +768,6 @@ function listRepoFilesRecursive(root, cwd) {
|
||||
});
|
||||
}
|
||||
|
||||
function listGatewayFilesFromGit(cwd) {
|
||||
const result = spawnSync("git", ["ls-files", "--", "src/gateway"], {
|
||||
cwd,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
return null;
|
||||
}
|
||||
return result.stdout
|
||||
.split("\n")
|
||||
.map((line) => normalizePathPattern(line.trim()))
|
||||
.filter((line) => line.length > 0);
|
||||
}
|
||||
|
||||
function isGatewayServerFullSuiteTarget(relative) {
|
||||
if (
|
||||
GATEWAY_SERVER_EXCLUDED_TEST_TARGETS.has(relative) ||
|
||||
relative.startsWith("src/gateway/server-methods/")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
GATEWAY_SERVER_BACKED_HTTP_TEST_TARGETS.has(relative) ||
|
||||
(relative.startsWith("src/gateway/") &&
|
||||
path.posix.basename(relative).includes("server") &&
|
||||
relative.endsWith(".test.ts"))
|
||||
);
|
||||
}
|
||||
|
||||
function resolveGatewayServerFullSuiteTargets(cwd) {
|
||||
const gatewayDir = path.join(cwd, "src/gateway");
|
||||
if (!fs.existsSync(gatewayDir)) {
|
||||
return [];
|
||||
}
|
||||
return (listGatewayFilesFromGit(cwd) ?? listRepoFilesRecursive(gatewayDir, cwd))
|
||||
.filter(isGatewayServerFullSuiteTarget)
|
||||
.toSorted((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
function splitTargetChunks(targets, chunkCount) {
|
||||
if (targets.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const normalizedChunkCount = Math.min(chunkCount, targets.length);
|
||||
const baseSize = Math.floor(targets.length / normalizedChunkCount);
|
||||
const remainder = targets.length % normalizedChunkCount;
|
||||
const chunks = [];
|
||||
let offset = 0;
|
||||
for (let index = 0; index < normalizedChunkCount; index += 1) {
|
||||
const chunkSize = baseSize + (index < remainder ? 1 : 0);
|
||||
chunks.push(targets.slice(offset, offset + chunkSize));
|
||||
offset += chunkSize;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
let cachedBroadScriptTestTargets = null;
|
||||
let cachedBroadScriptTestTargetsCwd = null;
|
||||
|
||||
@@ -3684,8 +3619,8 @@ export function buildFullSuiteVitestRunPlans(args, cwd = process.cwd()) {
|
||||
chunks = splitTargetChunks(targets, chunkCount);
|
||||
} else if (config === GATEWAY_SERVER_VITEST_CONFIG) {
|
||||
chunks = splitTargetChunks(
|
||||
resolveGatewayServerFullSuiteTargets(cwd),
|
||||
GATEWAY_SERVER_FULL_SUITE_TARGET_CHUNK_COUNT,
|
||||
listGatewayServerTestTargets(cwd),
|
||||
GATEWAY_SERVER_TEST_PROCESS_COUNT,
|
||||
);
|
||||
} else if (config === EXTENSION_MATRIX_VITEST_CONFIG) {
|
||||
chunks = createExtensionTestProcessTargetChunks(
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
embeddedAgentVitestProjectOwners,
|
||||
} from "../vitest/vitest.agents-paths.mjs";
|
||||
import { commandsLightTestFiles } from "../vitest/vitest.commands-light-paths.mjs";
|
||||
import { isGatewayServerTestFile } from "../vitest/vitest.gateway-server-paths.mjs";
|
||||
import { createGatewayServerVitestConfig } from "../vitest/vitest.gateway-server.config.ts";
|
||||
import { createPluginsVitestConfig } from "../vitest/vitest.plugins.config.ts";
|
||||
import { createToolingVitestConfig } from "../vitest/vitest.tooling.config.ts";
|
||||
|
||||
@@ -33,20 +35,6 @@ const PLUGIN_PRERELEASE_NPM_SPEC_TEST = "src/plugins/install.npm-spec.test.ts";
|
||||
const PLUGIN_NPM_INSTALL_SECURITY_SCAN_TEST =
|
||||
"src/plugins/npm-install-security-scan.release.test.ts";
|
||||
const DEFAULT_NODE_TEST_RUNNER = "blacksmith-8vcpu-ubuntu-2404";
|
||||
const GATEWAY_SERVER_BACKED_HTTP_TESTS = new Set([
|
||||
"src/gateway/embeddings-http.test.ts",
|
||||
"src/gateway/models-http.test.ts",
|
||||
"src/gateway/openai-http.test.ts",
|
||||
"src/gateway/openresponses-http.test.ts",
|
||||
"src/gateway/probe.auth.integration.test.ts",
|
||||
]);
|
||||
|
||||
const GATEWAY_SERVER_EXCLUDED_TESTS = new Set([
|
||||
"src/gateway/gateway.test.ts",
|
||||
"src/gateway/server.startup-matrix-migration.integration.test.ts",
|
||||
"src/gateway/sessions-history-http.test.ts",
|
||||
]);
|
||||
|
||||
function listTestFiles(rootDir: string): string[] {
|
||||
const gitFiles = listGitTrackedFiles({ pathspecs: rootDir });
|
||||
expect(gitFiles).not.toBeNull();
|
||||
@@ -103,15 +91,6 @@ function listAllToolingTestFiles(): string[] {
|
||||
}
|
||||
}
|
||||
|
||||
function isGatewayServerTestFile(file: string): boolean {
|
||||
return (
|
||||
file.startsWith("src/gateway/") &&
|
||||
!file.startsWith("src/gateway/server-methods/") &&
|
||||
!GATEWAY_SERVER_EXCLUDED_TESTS.has(file) &&
|
||||
(file.includes("server") || GATEWAY_SERVER_BACKED_HTTP_TESTS.has(file))
|
||||
);
|
||||
}
|
||||
|
||||
describe("scripts/lib/ci-node-test-plan.mjs", () => {
|
||||
it("assigns one semantic Vitest cache writer without changing shard order", () => {
|
||||
const full = createNodeTestShardBundles({ includeReleaseOnlyPluginShards: false });
|
||||
@@ -850,7 +829,6 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
|
||||
"agentic-control-plane-runtime",
|
||||
"agentic-control-plane-runtime-config",
|
||||
"agentic-control-plane-runtime-cron",
|
||||
"agentic-control-plane-runtime-events",
|
||||
"agentic-control-plane-runtime-network",
|
||||
"agentic-control-plane-runtime-server",
|
||||
"agentic-control-plane-runtime-shared-token",
|
||||
@@ -880,9 +858,17 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
|
||||
const controlPlaneShardFiles = controlPlaneShards
|
||||
.flatMap((shard) => shard.includePatterns ?? [])
|
||||
.toSorted((a, b) => a.localeCompare(b));
|
||||
const expectedControlPlaneFiles = listTestFiles("src/gateway")
|
||||
.filter(isGatewayServerTestFile)
|
||||
.toSorted((a, b) => a.localeCompare(b));
|
||||
const expectedControlPlaneFiles = listMatchedTestFiles(
|
||||
createGatewayServerVitestConfig({
|
||||
...process.env,
|
||||
OPENCLAW_VITEST_INCLUDE_FILE: undefined,
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
listTestFiles("src/gateway")
|
||||
.filter(isGatewayServerTestFile)
|
||||
.toSorted((a, b) => a.localeCompare(b)),
|
||||
).toEqual(expectedControlPlaneFiles);
|
||||
expect(controlPlaneShardFiles).toEqual(expectedControlPlaneFiles);
|
||||
expect(new Set(controlPlaneShardFiles).size).toBe(controlPlaneShardFiles.length);
|
||||
expect(cliShard).toEqual({
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
TOOLING_EXCLUDED_TESTS,
|
||||
VITEST_CONFIG_NO_OUTPUT_TIMEOUT_MS,
|
||||
installVitestNoOutputWatchdog,
|
||||
resolveBoundedVitestInvocations,
|
||||
resolveDefaultVitestNoOutputTimeoutMs,
|
||||
resolveDirectNodeVitestArgs,
|
||||
resolveExplicitTestFileNoPassArgs,
|
||||
@@ -194,6 +195,90 @@ describe("scripts/run-vitest", () => {
|
||||
).toEqual(["extensions/linux-canvas", "src/node-host", "--isolate", "--", "--no-isolate"]);
|
||||
});
|
||||
|
||||
it("bounds config-only Gateway server runs in fresh worker processes", () => {
|
||||
const argv = [
|
||||
"run",
|
||||
"--config",
|
||||
"test/vitest/vitest.gateway-server.config.ts",
|
||||
"--reporter=verbose",
|
||||
"--",
|
||||
"-x",
|
||||
];
|
||||
|
||||
expect(
|
||||
resolveBoundedVitestInvocations(argv, {
|
||||
env: {},
|
||||
gatewayServerTargetChunks: [
|
||||
["src/gateway/server-a.test.ts"],
|
||||
["src/gateway/server-b.test.ts"],
|
||||
],
|
||||
}),
|
||||
).toEqual([
|
||||
[
|
||||
"run",
|
||||
"--config",
|
||||
"test/vitest/vitest.gateway-server.config.ts",
|
||||
"--reporter=verbose",
|
||||
"src/gateway/server-a.test.ts",
|
||||
"--",
|
||||
"-x",
|
||||
],
|
||||
[
|
||||
"run",
|
||||
"--config",
|
||||
"test/vitest/vitest.gateway-server.config.ts",
|
||||
"--reporter=verbose",
|
||||
"src/gateway/server-b.test.ts",
|
||||
"--",
|
||||
"-x",
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("bounds implicit CI runs for absolute Gateway server config paths", () => {
|
||||
expect(
|
||||
resolveBoundedVitestInvocations(
|
||||
["--config", "/repo/test/vitest/vitest.gateway-server.config.ts"],
|
||||
{
|
||||
env: { CI: "1" },
|
||||
gatewayServerTargetChunks: [
|
||||
["src/gateway/server-a.test.ts"],
|
||||
["src/gateway/server-b.test.ts"],
|
||||
],
|
||||
},
|
||||
),
|
||||
).toHaveLength(2);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["local watch default", ["--config", "test/vitest/vitest.gateway-server.config.ts"]],
|
||||
["explicit watch", ["watch", "--config", "test/vitest/vitest.gateway-server.config.ts"]],
|
||||
[
|
||||
"explicit target",
|
||||
[
|
||||
"run",
|
||||
"--config",
|
||||
"test/vitest/vitest.gateway-server.config.ts",
|
||||
"src/gateway/server-startup.test.ts",
|
||||
],
|
||||
],
|
||||
[
|
||||
"coverage output",
|
||||
["run", "--config", "test/vitest/vitest.gateway-server.config.ts", "--coverage"],
|
||||
],
|
||||
["different config", ["run", "--config", "test/vitest/vitest.gateway-core.config.ts"]],
|
||||
])("keeps %s as one direct Vitest invocation", (_label, argv) => {
|
||||
expect(
|
||||
resolveBoundedVitestInvocations(argv, {
|
||||
env: {},
|
||||
gatewayServerTargetChunks: [
|
||||
["src/gateway/server-a.test.ts"],
|
||||
["src/gateway/server-b.test.ts"],
|
||||
],
|
||||
}),
|
||||
).toEqual([argv]);
|
||||
});
|
||||
|
||||
it("routes explicit tooling tests through the tooling config", () => {
|
||||
expect(resolveImplicitVitestArgs(["run", "test/scripts/run-vitest.test.ts"])).toEqual([
|
||||
"run",
|
||||
|
||||
@@ -4645,7 +4645,7 @@ describe("scripts/test-projects full-suite sharding", () => {
|
||||
expect(gatewayPlans).toHaveLength(4);
|
||||
expect(gatewayTargets.length).toBeGreaterThan(90);
|
||||
expect(new Set(gatewayTargets).size).toBe(gatewayTargets.length);
|
||||
expect(gatewayTargets).toContain("src/gateway/server-network-runtime.e2e.test.ts");
|
||||
expect(gatewayTargets).not.toContain("src/gateway/server-network-runtime.e2e.test.ts");
|
||||
expect(gatewayTargets).not.toContain("src/gateway/gateway.test.ts");
|
||||
expect(Math.max(...gatewayChunkSizes) - Math.min(...gatewayChunkSizes)).toBeLessThanOrEqual(1);
|
||||
const agentsCoreTargets = agentsCorePlans.flatMap((plan) => plan.forwardedArgs);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export const gatewayServerBackedHttpTestFiles: string[];
|
||||
export const gatewayServerExcludedTestFiles: string[];
|
||||
export function isGatewayServerBackedHttpTestFile(file: string): boolean;
|
||||
export function isGatewayServerTestFile(file: string): boolean;
|
||||
@@ -0,0 +1,39 @@
|
||||
// Canonical file ownership for the non-isolated Gateway server Vitest project.
|
||||
export const gatewayServerBackedHttpTestFiles = [
|
||||
"src/gateway/embeddings-http.test.ts",
|
||||
"src/gateway/models-http.test.ts",
|
||||
"src/gateway/openai-http.test.ts",
|
||||
"src/gateway/openresponses-http.test.ts",
|
||||
"src/gateway/probe.auth.integration.test.ts",
|
||||
];
|
||||
|
||||
export const gatewayServerExcludedTestFiles = [
|
||||
"src/gateway/gateway.test.ts",
|
||||
"src/gateway/server.startup-matrix-migration.integration.test.ts",
|
||||
"src/gateway/sessions-history-http.test.ts",
|
||||
];
|
||||
|
||||
const gatewayServerBackedHttpTestFileSet = new Set(gatewayServerBackedHttpTestFiles);
|
||||
const gatewayServerExcludedTestFileSet = new Set(gatewayServerExcludedTestFiles);
|
||||
|
||||
export function isGatewayServerBackedHttpTestFile(file) {
|
||||
return gatewayServerBackedHttpTestFileSet.has(file.replaceAll("\\", "/"));
|
||||
}
|
||||
|
||||
export function isGatewayServerTestFile(file) {
|
||||
const normalized = file.replaceAll("\\", "/");
|
||||
if (
|
||||
gatewayServerExcludedTestFileSet.has(normalized) ||
|
||||
normalized.startsWith("src/gateway/server-methods/") ||
|
||||
normalized.endsWith(".e2e.test.ts")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const basename = normalized.slice(normalized.lastIndexOf("/") + 1);
|
||||
return (
|
||||
isGatewayServerBackedHttpTestFile(normalized) ||
|
||||
(normalized.startsWith("src/gateway/") &&
|
||||
basename.includes("server") &&
|
||||
normalized.endsWith(".test.ts"))
|
||||
);
|
||||
}
|
||||
@@ -1,26 +1,17 @@
|
||||
import {
|
||||
gatewayServerBackedHttpTestFiles,
|
||||
gatewayServerExcludedTestFiles,
|
||||
} from "./vitest.gateway-server-paths.mjs";
|
||||
// Vitest gateway server config wires the gateway server test shard.
|
||||
import { createScopedVitestConfig } from "./vitest.scoped-config.ts";
|
||||
|
||||
const gatewayServerBackedHttpTests = [
|
||||
"src/gateway/embeddings-http.test.ts",
|
||||
"src/gateway/models-http.test.ts",
|
||||
"src/gateway/openai-http.test.ts",
|
||||
"src/gateway/openresponses-http.test.ts",
|
||||
"src/gateway/probe.auth.integration.test.ts",
|
||||
];
|
||||
|
||||
export function createGatewayServerVitestConfig(env?: Record<string, string | undefined>) {
|
||||
return createScopedVitestConfig(
|
||||
["src/gateway/**/*server*.test.ts", ...gatewayServerBackedHttpTests],
|
||||
["src/gateway/**/*server*.test.ts", ...gatewayServerBackedHttpTestFiles],
|
||||
{
|
||||
dir: "src/gateway",
|
||||
env,
|
||||
exclude: [
|
||||
"src/gateway/server-methods/**/*.test.ts",
|
||||
"src/gateway/gateway.test.ts",
|
||||
"src/gateway/server.startup-matrix-migration.integration.test.ts",
|
||||
"src/gateway/sessions-history-http.test.ts",
|
||||
],
|
||||
exclude: ["src/gateway/server-methods/**/*.test.ts", ...gatewayServerExcludedTestFiles],
|
||||
fileParallelism: false,
|
||||
// Gateway child projects share one include file; preserve this project's ownership.
|
||||
intersectIncludeFile: true,
|
||||
|
||||
Reference in New Issue
Block a user