CI: stabilize bundle hook and path tests
This commit is contained in:
parent
165deced34
commit
673b3f186c
@ -706,7 +706,6 @@
|
||||
"file-type": "21.3.3",
|
||||
"gaxios": "7.1.4",
|
||||
"gigachat": "^0.0.18",
|
||||
"grammy": "^1.41.1",
|
||||
"hono": "4.12.8",
|
||||
"ipaddr.js": "^2.3.0",
|
||||
"jiti": "^2.6.1",
|
||||
@ -741,6 +740,7 @@
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260317.1",
|
||||
"@vitest/coverage-v8": "^4.1.0",
|
||||
"grammy": "^1.41.1",
|
||||
"jscpd": "4.0.8",
|
||||
"jsdom": "^29.0.0",
|
||||
"lit": "^3.3.2",
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@ -101,9 +101,6 @@ importers:
|
||||
gigachat:
|
||||
specifier: ^0.0.18
|
||||
version: 0.0.18
|
||||
grammy:
|
||||
specifier: ^1.41.1
|
||||
version: 1.41.1
|
||||
hono:
|
||||
specifier: 4.12.8
|
||||
version: 4.12.8
|
||||
@ -204,6 +201,9 @@ importers:
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(@vitest/browser@4.1.0(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0)
|
||||
grammy:
|
||||
specifier: ^1.41.1
|
||||
version: 1.41.1
|
||||
jscpd:
|
||||
specifier: 4.0.8
|
||||
version: 4.0.8
|
||||
|
||||
@ -80,10 +80,17 @@ function loadHookFromDir(params: {
|
||||
pluginId?: string;
|
||||
nameHint?: string;
|
||||
}): Hook | null {
|
||||
const hookMdPath = path.join(params.hookDir, "HOOK.md");
|
||||
let canonicalHookDir = path.resolve(params.hookDir);
|
||||
try {
|
||||
canonicalHookDir = fs.realpathSync.native(params.hookDir);
|
||||
} catch {
|
||||
// Fall back to the discovered path when realpath is unavailable.
|
||||
}
|
||||
|
||||
const hookMdPath = path.join(canonicalHookDir, "HOOK.md");
|
||||
const content = readBoundaryFileUtf8({
|
||||
absolutePath: hookMdPath,
|
||||
rootPath: params.hookDir,
|
||||
rootPath: canonicalHookDir,
|
||||
boundaryLabel: "hook directory",
|
||||
});
|
||||
if (content === null) {
|
||||
@ -98,10 +105,10 @@ function loadHookFromDir(params: {
|
||||
const handlerCandidates = ["handler.ts", "handler.js", "index.ts", "index.js"];
|
||||
let handlerPath: string | undefined;
|
||||
for (const candidate of handlerCandidates) {
|
||||
const candidatePath = path.join(params.hookDir, candidate);
|
||||
const candidatePath = path.join(canonicalHookDir, candidate);
|
||||
const safeCandidatePath = resolveBoundaryFilePath({
|
||||
absolutePath: candidatePath,
|
||||
rootPath: params.hookDir,
|
||||
rootPath: canonicalHookDir,
|
||||
boundaryLabel: "hook directory",
|
||||
});
|
||||
if (safeCandidatePath) {
|
||||
@ -115,20 +122,13 @@ function loadHookFromDir(params: {
|
||||
return null;
|
||||
}
|
||||
|
||||
let baseDir = params.hookDir;
|
||||
try {
|
||||
baseDir = fs.realpathSync.native(params.hookDir);
|
||||
} catch {
|
||||
// keep the discovered path when realpath is unavailable
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
description,
|
||||
source: params.source,
|
||||
pluginId: params.pluginId,
|
||||
filePath: hookMdPath,
|
||||
baseDir,
|
||||
baseDir: canonicalHookDir,
|
||||
handlerPath,
|
||||
};
|
||||
} catch (err) {
|
||||
|
||||
@ -165,6 +165,10 @@ const LOCAL_EXTENSION_API_BARREL_EXCEPTIONS = [
|
||||
// accounts.ts -> runtime-api.ts -> src/plugin-sdk/matrix -> extensions/matrix/api.ts -> accounts.ts
|
||||
"extensions/matrix/src/matrix/accounts.ts",
|
||||
] as const;
|
||||
const CORE_GUARDRAIL_EXCEPTIONS = [
|
||||
// Contract registries are test support, even though they live under src for co-located fixtures.
|
||||
"src/channels/plugins/contracts/registry.ts",
|
||||
] as const;
|
||||
|
||||
function readSource(path: string): string {
|
||||
return readFileSync(resolve(ROOT_DIR, "..", path), "utf8");
|
||||
@ -267,6 +271,7 @@ function collectCoreSourceFiles(): string[] {
|
||||
normalizedFullPath.includes(".spec.") ||
|
||||
normalizedFullPath.includes(".fixture.") ||
|
||||
normalizedFullPath.includes(".snap") ||
|
||||
CORE_GUARDRAIL_EXCEPTIONS.some((suffix) => normalizedFullPath.endsWith(`/${suffix}`)) ||
|
||||
// src/plugin-sdk is the curated bridge layer; validate its contracts with dedicated
|
||||
// plugin-sdk guardrails instead of the generic "core should not touch extensions" rule.
|
||||
normalizedFullPath.includes(`${normalizedPluginSdkDir}/`)
|
||||
|
||||
@ -6,11 +6,16 @@ import { captureEnv } from "../test-utils/env.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { loadEnabledBundleMcpConfig } from "./bundle-mcp.js";
|
||||
import { createBundleMcpTempHarness, createBundleProbePlugin } from "./bundle-mcp.test-support.js";
|
||||
import { safeRealpathSync } from "./path-safety.js";
|
||||
|
||||
function getServerArgs(value: unknown): unknown[] | undefined {
|
||||
return isRecord(value) && Array.isArray(value.args) ? value.args : undefined;
|
||||
}
|
||||
|
||||
function canonicalizeExpectedBundlePath(value: string): string {
|
||||
return safeRealpathSync(value) ?? path.resolve(value);
|
||||
}
|
||||
|
||||
const tempHarness = createBundleMcpTempHarness();
|
||||
|
||||
afterEach(async () => {
|
||||
@ -46,7 +51,7 @@ describe("loadEnabledBundleMcpConfig", () => {
|
||||
const loadedServer = loaded.config.mcpServers.bundleProbe;
|
||||
const loadedArgs = getServerArgs(loadedServer);
|
||||
const loadedServerPath = typeof loadedArgs?.[0] === "string" ? loadedArgs[0] : undefined;
|
||||
const resolvedPluginRoot = await fs.realpath(pluginRoot);
|
||||
const resolvedPluginRoot = canonicalizeExpectedBundlePath(pluginRoot);
|
||||
|
||||
expect(loaded.diagnostics).toEqual([]);
|
||||
expect(isRecord(loadedServer) ? loadedServer.command : undefined).toBe("node");
|
||||
@ -178,7 +183,7 @@ describe("loadEnabledBundleMcpConfig", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const resolvedPluginRoot = await fs.realpath(pluginRoot);
|
||||
const resolvedPluginRoot = canonicalizeExpectedBundlePath(pluginRoot);
|
||||
|
||||
expect(loaded.diagnostics).toEqual([]);
|
||||
expect(loaded.config.mcpServers.inlineProbe).toEqual({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user