mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
perf(test): split gateway server control-plane shard
This commit is contained in:
@@ -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"],
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user