perf(test): split gateway server control-plane shard

This commit is contained in:
Peter Steinberger
2026-05-01 03:37:19 +01:00
parent 4987482e4c
commit 6cc7432cd1
3 changed files with 151 additions and 38 deletions

View File

@@ -137,6 +137,81 @@ function createAgenticCommandSplitShards() {
.filter((shard) => shard.includePatterns.length > 0);
}
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 resolveGatewayServerShardName(file) {
const name = relative("src/gateway", file).replaceAll("\\", "/");
if (
GATEWAY_SERVER_BACKED_HTTP_TESTS.has(file) ||
name.startsWith("server.models") ||
name.startsWith("server.talk")
) {
return "agentic-control-plane-http-models";
}
if (
name.startsWith("server.agent") ||
name.startsWith("server.chat") ||
name.startsWith("server.sessions")
) {
return "agentic-control-plane-agent-chat";
}
if (
name.includes("auth") ||
name.includes("device") ||
name.includes("node") ||
name.includes("roles") ||
name.includes("silent") ||
name.includes("preauth") ||
name.includes("control-plane-rate-limit")
) {
return "agentic-control-plane-auth-node";
}
return "agentic-control-plane-runtime";
}
function createGatewayServerSplitShards() {
const groups = new Map();
for (const file of listTestFiles("src/gateway").filter(isGatewayServerTestFile)) {
const shardName = resolveGatewayServerShardName(file);
groups.set(shardName, [...(groups.get(shardName) ?? []), file]);
}
return [
"agentic-control-plane-agent-chat",
"agentic-control-plane-auth-node",
"agentic-control-plane-http-models",
"agentic-control-plane-runtime",
]
.map((shardName) => ({
configs: ["test/vitest/vitest.gateway-server.config.ts"],
includePatterns: groups.get(shardName) ?? [],
requiresDist: false,
runner: "blacksmith-4vcpu-ubuntu-2404",
shardName,
}))
.filter((shard) => shard.includePatterns.length > 0);
}
const SPLIT_NODE_SHARDS = new Map([
[
"core-unit-fast",
@@ -225,12 +300,7 @@ const SPLIT_NODE_SHARDS = new Map([
[
"agentic",
[
{
shardName: "agentic-control-plane",
configs: ["test/vitest/vitest.gateway-server.config.ts"],
requiresDist: false,
runner: "blacksmith-4vcpu-ubuntu-2404",
},
...createGatewayServerSplitShards(),
{
shardName: "agentic-cli",
configs: ["test/vitest/vitest.cli.config.ts"],

View File

@@ -3,7 +3,6 @@ import { createServer } from "node:net";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import { WebSocket } from "ws";
import { resetModelCatalogCacheForTest } from "../agents/model-catalog.js";
import type { ChannelOutboundAdapter } from "../channels/plugins/types.js";
import { clearConfigCache, clearRuntimeConfigSnapshot } from "../config/config.js";
import { resolveCanvasHostUrl } from "../infra/canvas-host-url.js";
@@ -11,6 +10,7 @@ import { createOutboundTestPlugin } from "../test-utils/channel-plugins.js";
import { withEnvAsync } from "../test-utils/env.js";
import { createTempHomeEnv } from "../test-utils/temp-home.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import { __resetModelCatalogCacheForTest as resetGatewayModelCatalogCacheForTest } from "./server-model-catalog.js";
import { createRegistry } from "./server.e2e-registry-helpers.js";
import {
connectOk,
@@ -89,6 +89,8 @@ type ModelCatalogRpcEntry = {
provider: string;
alias?: string;
contextWindow?: number;
input?: string[];
reasoning?: boolean;
};
type PiCatalogFixtureEntry = {
@@ -154,14 +156,14 @@ describe("gateway server models + voicewake", () => {
: await rpcReq<{ models: ModelCatalogRpcEntry[] }>(ws, "models.list"),
);
const setPiCatalog = (entries: PiCatalogFixtureEntry[]) => {
const setPiCatalog = async (entries: PiCatalogFixtureEntry[]) => {
piSdkMock.enabled = true;
piSdkMock.models = entries;
resetModelCatalogCacheForTest();
await resetGatewayModelCatalogCacheForTest();
};
const seedPiCatalog = () => {
setPiCatalog(buildPiCatalogFixture());
const seedPiCatalog = async () => {
await setPiCatalog(buildPiCatalogFixture());
};
const withModelsConfig = async <T>(config: unknown, run: () => Promise<T>): Promise<T> => {
@@ -220,7 +222,7 @@ describe("gateway server models + voicewake", () => {
},
},
async () => {
seedPiCatalog();
await seedPiCatalog();
const res = await listModels();
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual(options.expected);
@@ -472,7 +474,7 @@ describe("gateway server models + voicewake", () => {
});
test("models.list all view returns model catalog", async () => {
seedPiCatalog();
await seedPiCatalog();
const res1 = await listModels({ view: "all" });
const res2 = await listModels({ view: "all" });
@@ -499,18 +501,18 @@ describe("gateway server models + voicewake", () => {
},
},
async () => {
setPiCatalog([
await setPiCatalog([
{ id: "remote-a", provider: "unauth-a", name: "Remote A" },
{ id: "remote-b", provider: "unauth-b", name: "Remote B" },
]);
const res = await listModels();
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual([
{
expect.objectContaining({
id: "MiniMax-M2.7-highspeed",
name: "MiniMax M2.7 Highspeed",
provider: "minimax",
},
}),
]);
},
);
@@ -525,7 +527,7 @@ describe("gateway server models + voicewake", () => {
},
async () => {
await withModelsConfig({}, async () => {
seedPiCatalog();
await seedPiCatalog();
const res = await listModels({ view: "configured" });
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual([
@@ -563,24 +565,24 @@ describe("gateway server models + voicewake", () => {
},
},
async () => {
setPiCatalog([
await setPiCatalog([
{ id: "remote-a", provider: "unauth-a", name: "Remote A" },
{ id: "remote-b", provider: "unauth-b", name: "Remote B" },
]);
const res = await listModels({ view: "configured" });
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual([
{
expect.objectContaining({
id: "MiniMax-M2.7-highspeed",
name: "MiniMax M2.7 Highspeed",
provider: "minimax",
},
{
}),
expect.objectContaining({
id: "glm-4.5-air",
name: "GLM 4.5 Air",
provider: "zhipu",
reasoning: true,
},
}),
]);
},
);
@@ -607,7 +609,7 @@ describe("gateway server models + voicewake", () => {
},
},
async () => {
seedPiCatalog();
await seedPiCatalog();
const res = await listModels({ view: "configured" });
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual([
@@ -634,7 +636,7 @@ describe("gateway server models + voicewake", () => {
},
},
async () => {
seedPiCatalog();
await seedPiCatalog();
const res = await listModels({ view: "all" });
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual(expectedSortedCatalog());
@@ -708,17 +710,17 @@ describe("gateway server models + voicewake", () => {
},
},
async () => {
seedPiCatalog();
await seedPiCatalog();
const res = await listModels();
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual([
{
expect.objectContaining({
id: "moonshotai/kimi-k2.5",
name: "Kimi K2.5 (Configured)",
alias: "Kimi K2.5 (NVIDIA)",
provider: "nvidia",
contextWindow: 32_000,
},
}),
]);
},
);
@@ -751,17 +753,17 @@ describe("gateway server models + voicewake", () => {
},
},
async () => {
seedPiCatalog();
await seedPiCatalog();
const res = await listModels();
expect(res.ok).toBe(true);
expect(res.payload?.models).toEqual([
{
expect.objectContaining({
id: "gpt-test-z",
name: "Configured GPT Test Z",
alias: "GPT Test Z Alias",
provider: "openai",
contextWindow: 64_000,
},
}),
]);
},
);

View File

@@ -17,6 +17,19 @@ type VitestConfig = {
};
const PLUGIN_PRERELEASE_NPM_SPEC_TEST = "src/plugins/install.npm-spec.test.ts";
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[] {
if (!existsSync(rootDir)) {
@@ -53,6 +66,15 @@ function listMatchedTestFiles(config: VitestConfig): string[] {
.toSorted((a, b) => a.localeCompare(b));
}
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("combines the small core unit shards to reduce CI runner fanout", () => {
const coreUnitShards = createNodeTestShards()
@@ -172,7 +194,9 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
it("splits the agentic lane into control-plane, command, agent, gateway, SDK, and plugin shards", () => {
const shards = createNodeTestShards();
const controlPlaneShard = shards.find((shard) => shard.shardName === "agentic-control-plane");
const controlPlaneShards = shards.filter((shard) =>
shard.shardName.startsWith("agentic-control-plane-"),
);
const cliShard = shards.find((shard) => shard.shardName === "agentic-cli");
const commandSupportShard = shards.find(
(shard) => shard.shardName === "agentic-command-support",
@@ -186,13 +210,30 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => {
const pluginSdkShard = shards.find((shard) => shard.shardName === "agentic-plugin-sdk");
const pluginsShard = shards.find((shard) => shard.shardName === "agentic-plugins");
expect(controlPlaneShard).toEqual({
checkName: "checks-node-agentic-control-plane",
shardName: "agentic-control-plane",
configs: ["test/vitest/vitest.gateway-server.config.ts"],
runner: "blacksmith-4vcpu-ubuntu-2404",
requiresDist: false,
});
expect(controlPlaneShards.map((shard) => shard.shardName)).toEqual([
"agentic-control-plane-agent-chat",
"agentic-control-plane-auth-node",
"agentic-control-plane-http-models",
"agentic-control-plane-runtime",
]);
expect(controlPlaneShards).toEqual(
controlPlaneShards.map((shard) => ({
checkName: `checks-node-${shard.shardName}`,
configs: ["test/vitest/vitest.gateway-server.config.ts"],
includePatterns: shard.includePatterns,
requiresDist: false,
runner: "blacksmith-4vcpu-ubuntu-2404",
shardName: shard.shardName,
})),
);
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));
expect(controlPlaneShardFiles).toEqual(expectedControlPlaneFiles);
expect(new Set(controlPlaneShardFiles).size).toBe(controlPlaneShardFiles.length);
expect(cliShard).toEqual({
checkName: "checks-node-agentic-cli",
shardName: "agentic-cli",