test: trim test startup overhead

This commit is contained in:
Peter Steinberger
2026-03-21 23:30:51 +00:00
parent cf4d301a69
commit 37d5cbe43a
17 changed files with 1206 additions and 1047 deletions

View File

@@ -256,17 +256,27 @@ function formatInventoryHuman(inventory) {
return lines.join("\n");
}
export async function main(argv = process.argv.slice(2)) {
function writeLine(stream, text) {
stream.write(`${text}\n`);
}
export async function runArchitectureSmellsCheck(argv = process.argv.slice(2), io) {
const streams = io ?? { stdout: process.stdout, stderr: process.stderr };
const json = argv.includes("--json");
const inventory = await collectArchitectureSmells();
if (json) {
process.stdout.write(`${JSON.stringify(inventory, null, 2)}\n`);
return;
writeLine(streams.stdout, JSON.stringify(inventory, null, 2));
return 0;
}
console.log(formatInventoryHuman(inventory));
console.log(`${inventory.length} smell${inventory.length === 1 ? "" : "s"} found.`);
writeLine(streams.stdout, formatInventoryHuman(inventory));
writeLine(streams.stdout, `${inventory.length} smell${inventory.length === 1 ? "" : "s"} found.`);
return 0;
}
export async function main(argv = process.argv.slice(2), io) {
return await runArchitectureSmellsCheck(argv, io);
}
runAsScript(import.meta.url, main);

View File

@@ -35,6 +35,9 @@ const baselinePathByMode = {
),
};
const inventoryPromiseByMode = new Map();
let parsedExtensionSourceFilesPromise;
const ruleTextByMode = {
"src-outside-plugin-sdk":
"Rule: production extensions/** must not import src/** outside src/plugin-sdk/**",
@@ -87,6 +90,34 @@ async function collectExtensionSourceFiles(rootDir) {
return out.toSorted((left, right) => normalizePath(left).localeCompare(normalizePath(right)));
}
async function collectParsedExtensionSourceFiles() {
if (!parsedExtensionSourceFilesPromise) {
parsedExtensionSourceFilesPromise = (async () => {
const files = await collectExtensionSourceFiles(extensionsRoot);
return await Promise.all(
files.map(async (filePath) => {
const source = await fs.readFile(filePath, "utf8");
const scriptKind =
filePath.endsWith(".tsx") || filePath.endsWith(".jsx")
? ts.ScriptKind.TSX
: ts.ScriptKind.TS;
return {
filePath,
sourceFile: ts.createSourceFile(
filePath,
source,
ts.ScriptTarget.Latest,
true,
scriptKind,
),
};
}),
);
})();
}
return await parsedExtensionSourceFilesPromise;
}
function toLine(sourceFile, node) {
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
}
@@ -216,22 +247,19 @@ export async function collectExtensionPluginSdkBoundaryInventory(mode) {
if (!MODES.has(mode)) {
throw new Error(`Unknown mode: ${mode}`);
}
const files = await collectExtensionSourceFiles(extensionsRoot);
const inventory = [];
for (const filePath of files) {
const source = await fs.readFile(filePath, "utf8");
const scriptKind =
filePath.endsWith(".tsx") || filePath.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
const sourceFile = ts.createSourceFile(
filePath,
source,
ts.ScriptTarget.Latest,
true,
scriptKind,
);
inventory.push(...collectFromSourceFile(mode, sourceFile, filePath));
let pending = inventoryPromiseByMode.get(mode);
if (!pending) {
pending = (async () => {
const files = await collectParsedExtensionSourceFiles();
const inventory = [];
for (const { filePath, sourceFile } of files) {
inventory.push(...collectFromSourceFile(mode, sourceFile, filePath));
}
return inventory.toSorted(compareEntries);
})();
inventoryPromiseByMode.set(mode, pending);
}
return inventory.toSorted(compareEntries);
return await pending;
}
export async function readExpectedInventory(mode) {
@@ -286,7 +314,12 @@ function formatInventoryHuman(mode, inventory) {
return lines.join("\n");
}
export async function main(argv = process.argv.slice(2)) {
function writeLine(stream, text) {
stream.write(`${text}\n`);
}
export async function runExtensionPluginSdkBoundaryCheck(argv = process.argv.slice(2), io) {
const streams = io ?? { stdout: process.stdout, stderr: process.stderr };
const json = argv.includes("--json");
const modeArg = argv.find((arg) => arg.startsWith("--mode="));
const mode = modeArg?.slice("--mode=".length) ?? "src-outside-plugin-sdk";
@@ -296,30 +329,38 @@ export async function main(argv = process.argv.slice(2)) {
const actual = await collectExtensionPluginSdkBoundaryInventory(mode);
if (json) {
process.stdout.write(`${JSON.stringify(actual, null, 2)}\n`);
return;
writeLine(streams.stdout, JSON.stringify(actual, null, 2));
return 0;
}
const expected = await readExpectedInventory(mode);
const diff = diffInventory(expected, actual);
console.log(formatInventoryHuman(mode, actual));
writeLine(streams.stdout, formatInventoryHuman(mode, actual));
if (diff.missing.length === 0 && diff.unexpected.length === 0) {
console.log(`Baseline matches (${actual.length} entries).`);
return;
writeLine(streams.stdout, `Baseline matches (${actual.length} entries).`);
return 0;
}
if (diff.missing.length > 0) {
console.error(`Missing baseline entries (${diff.missing.length}):`);
writeLine(streams.stderr, `Missing baseline entries (${diff.missing.length}):`);
for (const entry of diff.missing) {
console.error(` - ${entry.file}:${entry.line} ${entry.reason}`);
writeLine(streams.stderr, ` - ${entry.file}:${entry.line} ${entry.reason}`);
}
}
if (diff.unexpected.length > 0) {
console.error(`Unexpected inventory entries (${diff.unexpected.length}):`);
writeLine(streams.stderr, `Unexpected inventory entries (${diff.unexpected.length}):`);
for (const entry of diff.unexpected) {
console.error(` - ${entry.file}:${entry.line} ${entry.reason}`);
writeLine(streams.stderr, ` - ${entry.file}:${entry.line} ${entry.reason}`);
}
}
process.exitCode = 1;
return 1;
}
export async function main(argv = process.argv.slice(2), io) {
const exitCode = await runExtensionPluginSdkBoundaryCheck(argv, io);
if (!io) {
process.exitCode = exitCode;
}
return exitCode;
}
if (path.resolve(process.argv[1] ?? "") === fileURLToPath(import.meta.url)) {

View File

@@ -266,7 +266,12 @@ function formatEntry(entry) {
return `${entry.file}:${entry.line} [${entry.kind}] ${entry.reason} (${entry.specifier} -> ${entry.resolvedPath})`;
}
export async function main(argv = process.argv.slice(2)) {
function writeLine(stream, text) {
stream.write(`${text}\n`);
}
export async function runPluginExtensionImportBoundaryCheck(argv = process.argv.slice(2), io) {
const streams = io ?? { stdout: process.stdout, stderr: process.stderr };
const json = argv.includes("--json");
const actual = await collectPluginExtensionImportBoundaryInventory();
const expected = await readExpectedInventory();
@@ -274,33 +279,43 @@ export async function main(argv = process.argv.slice(2)) {
const matchesBaseline = missing.length === 0 && unexpected.length === 0;
if (json) {
process.stdout.write(`${JSON.stringify(actual, null, 2)}\n`);
writeLine(streams.stdout, JSON.stringify(actual, null, 2));
} else {
console.log(formatInventoryHuman(actual));
console.log(
writeLine(streams.stdout, formatInventoryHuman(actual));
writeLine(
streams.stdout,
matchesBaseline
? `Baseline matches (${actual.length} entries).`
: `Baseline mismatch (${unexpected.length} unexpected, ${missing.length} missing).`,
);
if (!matchesBaseline) {
if (unexpected.length > 0) {
console.error("Unexpected entries:");
writeLine(streams.stderr, "Unexpected entries:");
for (const entry of unexpected) {
console.error(`- ${formatEntry(entry)}`);
writeLine(streams.stderr, `- ${formatEntry(entry)}`);
}
}
if (missing.length > 0) {
console.error("Missing baseline entries:");
writeLine(streams.stderr, "Missing baseline entries:");
for (const entry of missing) {
console.error(`- ${formatEntry(entry)}`);
writeLine(streams.stderr, `- ${formatEntry(entry)}`);
}
}
}
}
if (!matchesBaseline) {
process.exit(1);
return 1;
}
return 0;
}
export async function main(argv = process.argv.slice(2), io) {
const exitCode = await runPluginExtensionImportBoundaryCheck(argv, io);
if (!io && exitCode !== 0) {
process.exit(exitCode);
}
return exitCode;
}
runAsScript(import.meta.url, main);

View File

@@ -255,7 +255,12 @@ function formatEntry(entry) {
return `${entry.provider} ${entry.file}:${entry.line} ${entry.reason}`;
}
export async function main(argv = process.argv.slice(2)) {
function writeLine(stream, text) {
stream.write(`${text}\n`);
}
export async function runWebSearchProviderBoundaryCheck(argv = process.argv.slice(2), io) {
const streams = io ?? { stdout: process.stdout, stderr: process.stderr };
const json = argv.includes("--json");
const actual = await collectWebSearchProviderBoundaryInventory();
const expected = await readExpectedInventory();
@@ -263,33 +268,43 @@ export async function main(argv = process.argv.slice(2)) {
const matchesBaseline = missing.length === 0 && unexpected.length === 0;
if (json) {
process.stdout.write(`${JSON.stringify(actual, null, 2)}\n`);
writeLine(streams.stdout, JSON.stringify(actual, null, 2));
} else {
console.log(formatInventoryHuman(actual));
console.log(
writeLine(streams.stdout, formatInventoryHuman(actual));
writeLine(
streams.stdout,
matchesBaseline
? `Baseline matches (${actual.length} entries).`
: `Baseline mismatch (${unexpected.length} unexpected, ${missing.length} missing).`,
);
if (!matchesBaseline) {
if (unexpected.length > 0) {
console.error("Unexpected entries:");
writeLine(streams.stderr, "Unexpected entries:");
for (const entry of unexpected) {
console.error(`- ${formatEntry(entry)}`);
writeLine(streams.stderr, `- ${formatEntry(entry)}`);
}
}
if (missing.length > 0) {
console.error("Missing baseline entries:");
writeLine(streams.stderr, "Missing baseline entries:");
for (const entry of missing) {
console.error(`- ${formatEntry(entry)}`);
writeLine(streams.stderr, `- ${formatEntry(entry)}`);
}
}
}
}
if (!matchesBaseline) {
process.exit(1);
return 1;
}
return 0;
}
export async function main(argv = process.argv.slice(2), io) {
const exitCode = await runWebSearchProviderBoundaryCheck(argv, io);
if (!io && exitCode !== 0) {
process.exit(exitCode);
}
return exitCode;
}
runAsScript(import.meta.url, main);

View File

@@ -13,11 +13,24 @@ import {
const findVerifiedGatewayListenerPidsOnPortSync = vi.hoisted(() =>
vi.fn<(port: number) => number[]>(() => []),
);
const timeState = vi.hoisted(() => ({ now: 0 }));
const sleepMock = vi.hoisted(() =>
vi.fn(async (ms: number) => {
timeState.now += ms;
}),
);
vi.mock("../infra/gateway-processes.js", () => ({
findVerifiedGatewayListenerPidsOnPortSync: (port: number) =>
findVerifiedGatewayListenerPidsOnPortSync(port),
}));
vi.mock("../utils.js", async () => {
const actual = await vi.importActual<typeof import("../utils.js")>("../utils.js");
return {
...actual,
sleep: (ms: number) => sleepMock(ms),
};
});
const { restartScheduledTask, stopScheduledTask } = await import("./schtasks.js");
const GATEWAY_PORT = 18789;
@@ -81,6 +94,12 @@ beforeEach(() => {
resetSchtasksBaseMocks();
findVerifiedGatewayListenerPidsOnPortSync.mockReset();
findVerifiedGatewayListenerPidsOnPortSync.mockReturnValue([]);
timeState.now = 0;
vi.spyOn(Date, "now").mockImplementation(() => timeState.now);
sleepMock.mockReset();
sleepMock.mockImplementation(async (ms: number) => {
timeState.now += ms;
});
inspectPortUsage.mockResolvedValue(freePortUsage());
});

View File

@@ -13,14 +13,10 @@ vi.mock("./targets.js", async () => {
});
import type { OpenClawConfig } from "../../config/config.js";
type AgentDeliveryModule = typeof import("./agent-delivery.js");
import { resolveAgentDeliveryPlan, resolveAgentOutboundTarget } from "./agent-delivery.js";
let resolveAgentDeliveryPlan: AgentDeliveryModule["resolveAgentDeliveryPlan"];
let resolveAgentOutboundTarget: AgentDeliveryModule["resolveAgentOutboundTarget"];
beforeEach(async () => {
vi.resetModules();
({ resolveAgentDeliveryPlan, resolveAgentOutboundTarget } = await import("./agent-delivery.js"));
beforeEach(() => {
mocks.resolveOutboundTarget.mockClear();
});
describe("agent delivery helpers", () => {

View File

@@ -46,13 +46,10 @@ vi.mock("./deliver.js", () => ({
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
let sendMessage: typeof import("./message.js").sendMessage;
import { sendMessage } from "./message.js";
describe("sendMessage", () => {
beforeEach(async () => {
vi.resetModules();
({ sendMessage } = await import("./message.js"));
beforeEach(() => {
setActivePluginRegistry(createTestRegistry([]));
mocks.getChannelPlugin.mockClear();
mocks.resolveOutboundTarget.mockClear();

View File

@@ -16,25 +16,31 @@ vi.mock("../config/paths.js", () => ({
resolveGatewayPort: (...args: unknown[]) => resolveGatewayPortMock(...args),
}));
let __testing: typeof import("./restart-stale-pids.js").__testing;
let cleanStaleGatewayProcessesSync: typeof import("./restart-stale-pids.js").cleanStaleGatewayProcessesSync;
let findGatewayPidsOnPortSync: typeof import("./restart-stale-pids.js").findGatewayPidsOnPortSync;
import {
__testing,
cleanStaleGatewayProcessesSync,
findGatewayPidsOnPortSync,
} from "./restart-stale-pids.js";
beforeEach(async () => {
vi.resetModules();
({ __testing, cleanStaleGatewayProcessesSync, findGatewayPidsOnPortSync } =
await import("./restart-stale-pids.js"));
let currentTimeMs = 0;
beforeEach(() => {
spawnSyncMock.mockReset();
resolveLsofCommandSyncMock.mockReset();
resolveGatewayPortMock.mockReset();
currentTimeMs = 0;
resolveLsofCommandSyncMock.mockReturnValue("/usr/sbin/lsof");
resolveGatewayPortMock.mockReturnValue(18789);
__testing.setSleepSyncOverride(() => {});
__testing.setSleepSyncOverride((ms) => {
currentTimeMs += ms;
});
__testing.setDateNowOverride(() => currentTimeMs);
});
afterEach(() => {
__testing.setSleepSyncOverride(null);
__testing.setDateNowOverride(null);
vi.restoreAllMocks();
});
@@ -79,11 +85,17 @@ describe.runIf(process.platform !== "win32")("findGatewayPidsOnPortSync", () =>
describe.runIf(process.platform !== "win32")("cleanStaleGatewayProcessesSync", () => {
it("kills stale gateway pids discovered on the gateway port", () => {
spawnSyncMock.mockReturnValue({
error: undefined,
status: 0,
stdout: ["p6001", "copenclaw", "p6002", "copenclaw-gateway"].join("\n"),
});
spawnSyncMock
.mockReturnValueOnce({
error: undefined,
status: 0,
stdout: ["p6001", "copenclaw", "p6002", "copenclaw-gateway"].join("\n"),
})
.mockReturnValue({
error: undefined,
status: 1,
stdout: "",
});
const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true);
const killed = cleanStaleGatewayProcessesSync();
@@ -97,11 +109,17 @@ describe.runIf(process.platform !== "win32")("cleanStaleGatewayProcessesSync", (
});
it("uses explicit port override when provided", () => {
spawnSyncMock.mockReturnValue({
error: undefined,
status: 0,
stdout: ["p7001", "copenclaw"].join("\n"),
});
spawnSyncMock
.mockReturnValueOnce({
error: undefined,
status: 0,
stdout: ["p7001", "copenclaw"].join("\n"),
})
.mockReturnValue({
error: undefined,
status: 1,
stdout: "",
});
const killSpy = vi.spyOn(process, "kill").mockImplementation(() => true);
const killed = cleanStaleGatewayProcessesSync(19999);

View File

@@ -1,6 +1,12 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { MsgContext } from "../auto-reply/templating.js";
import type { OpenClawConfig } from "../config/config.js";
import {
buildProviderRegistry,
createMediaAttachmentCache,
normalizeMediaAttachments,
runCapability,
} from "./runner.js";
const catalog = [
{
@@ -23,20 +29,9 @@ vi.mock("../agents/model-catalog.js", async () => {
};
});
let buildProviderRegistry: typeof import("./runner.js").buildProviderRegistry;
let createMediaAttachmentCache: typeof import("./runner.js").createMediaAttachmentCache;
let normalizeMediaAttachments: typeof import("./runner.js").normalizeMediaAttachments;
let runCapability: typeof import("./runner.js").runCapability;
describe("runCapability image skip", () => {
beforeEach(async () => {
vi.resetModules();
({
buildProviderRegistry,
createMediaAttachmentCache,
normalizeMediaAttachments,
runCapability,
} = await import("./runner.js"));
beforeEach(() => {
loadModelCatalog.mockClear();
});
it("skips image understanding when the active model supports vision", async () => {

View File

@@ -4,21 +4,27 @@ import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { describe, expect, it } from "vitest";
import {
buildPluginSdkEntrySources,
buildPluginSdkPackageExports,
buildPluginSdkSpecifiers,
pluginSdkEntrypoints,
} from "./entrypoints.js";
import { buildPluginSdkEntrySources, pluginSdkEntrypoints } from "./entrypoints.js";
const pluginSdkSpecifiers = buildPluginSdkSpecifiers();
const require = createRequire(import.meta.url);
const tsdownModuleUrl = pathToFileURL(require.resolve("tsdown")).href;
const bundledSmokeEntrypoints = [
"index",
"core",
"runtime",
"channel-runtime",
"provider-setup",
"setup",
"matrix-runtime-heavy",
"windows-spawn",
"gateway-runtime",
"plugin-runtime",
"testing",
] as const;
describe("plugin-sdk bundled exports", () => {
it("emits importable bundled subpath entries", { timeout: 240_000 }, async () => {
const outDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-plugin-sdk-build-"));
const fixtureDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-plugin-sdk-consumer-"));
try {
const { build } = await import(tsdownModuleUrl);
@@ -45,52 +51,20 @@ describe("plugin-sdk bundled exports", () => {
}),
);
const packageDir = path.join(fixtureDir, "openclaw");
const consumerDir = path.join(fixtureDir, "consumer");
const consumerEntry = path.join(consumerDir, "import-plugin-sdk.mjs");
await fs.mkdir(path.join(packageDir, "dist"), { recursive: true });
await fs.symlink(outDir, path.join(packageDir, "dist", "plugin-sdk"), "dir");
// Mirror the installed package layout so subpaths can resolve root deps.
await fs.symlink(
path.join(process.cwd(), "node_modules"),
path.join(packageDir, "node_modules"),
"dir",
// Export list and package-specifier coverage already live in
// package-contract-guardrails.test.ts and subpaths.test.ts. Keep this file
// focused on the expensive part: can tsdown emit working bundle artifacts?
const importResults = await Promise.all(
bundledSmokeEntrypoints.map(async (entry) => [
entry,
typeof (await import(pathToFileURL(path.join(outDir, `${entry}.js`)).href)),
]),
);
await fs.writeFile(
path.join(packageDir, "package.json"),
JSON.stringify(
{
exports: buildPluginSdkPackageExports(),
name: "openclaw",
type: "module",
},
null,
2,
),
);
await fs.mkdir(path.join(consumerDir, "node_modules"), { recursive: true });
await fs.symlink(packageDir, path.join(consumerDir, "node_modules", "openclaw"), "dir");
await fs.writeFile(
consumerEntry,
[
`const specifiers = ${JSON.stringify(pluginSdkSpecifiers)};`,
"const results = {};",
"for (const specifier of specifiers) {",
" results[specifier] = typeof (await import(specifier));",
"}",
"export default results;",
].join("\n"),
);
const { default: importResults } = await import(pathToFileURL(consumerEntry).href);
expect(importResults).toEqual(
Object.fromEntries(pluginSdkSpecifiers.map((specifier: string) => [specifier, "object"])),
expect(Object.fromEntries(importResults)).toEqual(
Object.fromEntries(bundledSmokeEntrypoints.map((entry) => [entry, "object"])),
);
} finally {
await fs.rm(outDir, { recursive: true, force: true });
await fs.rm(fixtureDir, { recursive: true, force: true });
}
});
});

View File

@@ -4,6 +4,7 @@ import path from "node:path";
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import { emitDiagnosticEvent, resetDiagnosticEventsForTest } from "../infra/diagnostic-events.js";
import { withEnv } from "../test-utils/env.js";
import { clearPluginCommands, getPluginCommandSpecs } from "./commands.js";
async function importFreshPluginTestModules() {
vi.resetModules();
@@ -1009,8 +1010,6 @@ module.exports = { id: "skipped-scoped-only", register() { throw new Error("skip
},
};`,
});
const { clearPluginCommands, getPluginCommandSpecs } = await import("./commands.js");
clearPluginCommands();
const scoped = loadOpenClawPlugins({
@@ -1395,6 +1394,8 @@ module.exports = { id: "skipped-scoped-only", register() { throw new Error("skip
filename: "cache-eviction.cjs",
body: `module.exports = { id: "cache-eviction", register() {} };`,
});
const previousCacheCap = __testing.maxPluginRegistryCacheEntries;
__testing.setMaxPluginRegistryCacheEntriesForTest(4);
const stateDirs = Array.from({ length: __testing.maxPluginRegistryCacheEntries + 1 }, () =>
makeTempDir(),
);
@@ -1416,17 +1417,21 @@ module.exports = { id: "skipped-scoped-only", register() { throw new Error("skip
},
});
const first = loadWithStateDir(stateDirs[0] ?? makeTempDir());
const second = loadWithStateDir(stateDirs[1] ?? makeTempDir());
try {
const first = loadWithStateDir(stateDirs[0] ?? makeTempDir());
const second = loadWithStateDir(stateDirs[1] ?? makeTempDir());
expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first);
expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first);
for (const stateDir of stateDirs.slice(2)) {
loadWithStateDir(stateDir);
for (const stateDir of stateDirs.slice(2)) {
loadWithStateDir(stateDir);
}
expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first);
expect(loadWithStateDir(stateDirs[1] ?? makeTempDir())).not.toBe(second);
} finally {
__testing.setMaxPluginRegistryCacheEntriesForTest(previousCacheCap);
}
expect(loadWithStateDir(stateDirs[0] ?? makeTempDir())).toBe(first);
expect(loadWithStateDir(stateDirs[1] ?? makeTempDir())).not.toBe(second);
});
it("normalizes bundled plugin env overrides against the provided env", () => {

View File

@@ -101,6 +101,7 @@ type CachedPluginState = {
};
const MAX_PLUGIN_REGISTRY_CACHE_ENTRIES = 128;
let pluginRegistryCacheEntryCap = MAX_PLUGIN_REGISTRY_CACHE_ENTRIES;
const registryCache = new Map<string, CachedPluginState>();
const openAllowlistWarningCache = new Set<string>();
const LAZY_RUNTIME_REFLECTION_KEYS = [
@@ -139,7 +140,15 @@ export const __testing = {
resolvePluginSdkAliasFile,
resolvePluginRuntimeModulePath,
shouldPreferNativeJiti,
maxPluginRegistryCacheEntries: MAX_PLUGIN_REGISTRY_CACHE_ENTRIES,
get maxPluginRegistryCacheEntries() {
return pluginRegistryCacheEntryCap;
},
setMaxPluginRegistryCacheEntriesForTest(value?: number) {
pluginRegistryCacheEntryCap =
typeof value === "number" && Number.isFinite(value) && value > 0
? Math.max(1, Math.floor(value))
: MAX_PLUGIN_REGISTRY_CACHE_ENTRIES;
},
};
function getCachedPluginRegistry(cacheKey: string): CachedPluginState | undefined {
@@ -158,7 +167,7 @@ function setCachedPluginRegistry(cacheKey: string, state: CachedPluginState): vo
registryCache.delete(cacheKey);
}
registryCache.set(cacheKey, state);
while (registryCache.size > MAX_PLUGIN_REGISTRY_CACHE_ENTRIES) {
while (registryCache.size > pluginRegistryCacheEntryCap) {
const oldestKey = registryCache.keys().next().value;
if (!oldestKey) {
break;

View File

@@ -1,10 +1,26 @@
import { execFileSync } from "node:child_process";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { collectArchitectureSmells } from "../scripts/check-architecture-smells.mjs";
import { collectArchitectureSmells, main } from "../scripts/check-architecture-smells.mjs";
const repoRoot = process.cwd();
const scriptPath = path.join(repoRoot, "scripts", "check-architecture-smells.mjs");
function createCapturedIo() {
let stdout = "";
let stderr = "";
return {
io: {
stdout: {
write(chunk) {
stdout += String(chunk);
},
},
stderr: {
write(chunk) {
stderr += String(chunk);
},
},
},
readStdout: () => stdout,
readStderr: () => stderr,
};
}
describe("architecture smell inventory", () => {
it("produces stable sorted output", async () => {
@@ -26,11 +42,11 @@ describe("architecture smell inventory", () => {
});
it("script json output matches the collector", async () => {
const stdout = execFileSync(process.execPath, [scriptPath, "--json"], {
cwd: repoRoot,
encoding: "utf8",
});
const captured = createCapturedIo();
const exitCode = await main(["--json"], captured.io);
expect(JSON.parse(stdout)).toEqual(await collectArchitectureSmells());
expect(exitCode).toBe(0);
expect(captured.readStderr()).toBe("");
expect(JSON.parse(captured.readStdout())).toEqual(await collectArchitectureSmells());
});
});

View File

@@ -1,11 +1,12 @@
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { collectExtensionPluginSdkBoundaryInventory } from "../scripts/check-extension-plugin-sdk-boundary.mjs";
import {
collectExtensionPluginSdkBoundaryInventory,
main,
} from "../scripts/check-extension-plugin-sdk-boundary.mjs";
const repoRoot = process.cwd();
const scriptPath = path.join(repoRoot, "scripts", "check-extension-plugin-sdk-boundary.mjs");
const relativeOutsidePackageBaselinePath = path.join(
repoRoot,
"test",
@@ -13,6 +14,27 @@ const relativeOutsidePackageBaselinePath = path.join(
"extension-relative-outside-package-inventory.json",
);
function createCapturedIo() {
let stdout = "";
let stderr = "";
return {
io: {
stdout: {
write(chunk) {
stdout += String(chunk);
},
},
stderr: {
write(chunk) {
stderr += String(chunk);
},
},
},
readStdout: () => stdout,
readStderr: () => stderr,
};
}
describe("extension src outside plugin-sdk boundary inventory", () => {
it("is currently empty", async () => {
const inventory = await collectExtensionPluginSdkBoundaryInventory("src-outside-plugin-sdk");
@@ -38,17 +60,13 @@ describe("extension src outside plugin-sdk boundary inventory", () => {
).toEqual(first);
});
it("script json output is empty", () => {
const stdout = execFileSync(
process.execPath,
[scriptPath, "--mode=src-outside-plugin-sdk", "--json"],
{
cwd: repoRoot,
encoding: "utf8",
},
);
it("script json output is empty", async () => {
const captured = createCapturedIo();
const exitCode = await main(["--mode=src-outside-plugin-sdk", "--json"], captured.io);
expect(JSON.parse(stdout)).toEqual([]);
expect(exitCode).toBe(0);
expect(captured.readStderr()).toBe("");
expect(JSON.parse(captured.readStdout())).toEqual([]);
});
});
@@ -59,17 +77,13 @@ describe("extension plugin-sdk-internal boundary inventory", () => {
expect(inventory).toEqual([]);
});
it("script json output is empty", () => {
const stdout = execFileSync(
process.execPath,
[scriptPath, "--mode=plugin-sdk-internal", "--json"],
{
cwd: repoRoot,
encoding: "utf8",
},
);
it("script json output is empty", async () => {
const captured = createCapturedIo();
const exitCode = await main(["--mode=plugin-sdk-internal", "--json"], captured.io);
expect(JSON.parse(stdout)).toEqual([]);
expect(exitCode).toBe(0);
expect(captured.readStderr()).toBe("");
expect(JSON.parse(captured.readStdout())).toEqual([]);
});
});
@@ -81,17 +95,13 @@ describe("extension relative-outside-package boundary inventory", () => {
expect(inventory).toEqual(expected);
});
it("script json output matches the checked-in baseline", () => {
const stdout = execFileSync(
process.execPath,
[scriptPath, "--mode=relative-outside-package", "--json"],
{
cwd: repoRoot,
encoding: "utf8",
},
);
it("script json output matches the checked-in baseline", async () => {
const captured = createCapturedIo();
const exitCode = await main(["--mode=relative-outside-package", "--json"], captured.io);
const expected = JSON.parse(fs.readFileSync(relativeOutsidePackageBaselinePath, "utf8"));
expect(JSON.parse(stdout)).toEqual(expected);
expect(exitCode).toBe(0);
expect(captured.readStderr()).toBe("");
expect(JSON.parse(captured.readStdout())).toEqual(expected);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,13 @@
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
collectPluginExtensionImportBoundaryInventory,
diffInventory,
main,
} from "../scripts/check-plugin-extension-import-boundary.mjs";
const repoRoot = process.cwd();
const scriptPath = path.join(repoRoot, "scripts", "check-plugin-extension-import-boundary.mjs");
const baselinePath = path.join(
repoRoot,
"test",
@@ -20,6 +19,27 @@ function readBaseline() {
return JSON.parse(readFileSync(baselinePath, "utf8"));
}
function createCapturedIo() {
let stdout = "";
let stderr = "";
return {
io: {
stdout: {
write(chunk) {
stdout += String(chunk);
},
},
stderr: {
write(chunk) {
stderr += String(chunk);
},
},
},
readStdout: () => stdout,
readStderr: () => stderr,
};
}
describe("plugin extension import boundary inventory", () => {
it("keeps dedicated web-search registry shims out of the remaining inventory", async () => {
const inventory = await collectPluginExtensionImportBoundaryInventory();
@@ -65,12 +85,12 @@ describe("plugin extension import boundary inventory", () => {
expect(diffInventory(expected, actual)).toEqual({ missing: [], unexpected: [] });
});
it("script json output matches the baseline exactly", () => {
const stdout = execFileSync(process.execPath, [scriptPath, "--json"], {
cwd: repoRoot,
encoding: "utf8",
});
it("script json output matches the baseline exactly", async () => {
const captured = createCapturedIo();
const exitCode = await main(["--json"], captured.io);
expect(JSON.parse(stdout)).toEqual(readBaseline());
expect(exitCode).toBe(0);
expect(captured.readStderr()).toBe("");
expect(JSON.parse(captured.readStdout())).toEqual(readBaseline());
});
});

View File

@@ -1,10 +1,29 @@
import { execFileSync } from "node:child_process";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { collectWebSearchProviderBoundaryInventory } from "../scripts/check-web-search-provider-boundaries.mjs";
import {
collectWebSearchProviderBoundaryInventory,
main,
} from "../scripts/check-web-search-provider-boundaries.mjs";
const repoRoot = process.cwd();
const scriptPath = path.join(repoRoot, "scripts", "check-web-search-provider-boundaries.mjs");
function createCapturedIo() {
let stdout = "";
let stderr = "";
return {
io: {
stdout: {
write(chunk) {
stdout += String(chunk);
},
},
stderr: {
write(chunk) {
stderr += String(chunk);
},
},
},
readStdout: () => stdout,
readStderr: () => stderr,
};
}
describe("web search provider boundary inventory", () => {
it("has no remaining production inventory in core", async () => {
@@ -35,12 +54,12 @@ describe("web search provider boundary inventory", () => {
).toEqual(first);
});
it("script json output is empty", () => {
const stdout = execFileSync(process.execPath, [scriptPath, "--json"], {
cwd: repoRoot,
encoding: "utf8",
});
it("script json output is empty", async () => {
const captured = createCapturedIo();
const exitCode = await main(["--json"], captured.io);
expect(JSON.parse(stdout)).toEqual([]);
expect(exitCode).toBe(0);
expect(captured.readStderr()).toBe("");
expect(JSON.parse(captured.readStdout())).toEqual([]);
});
});