mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 17:44:45 +00:00
fix(test): avoid walking channel import guardrails
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import { basename, dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { classifyBundledExtensionSourcePath } from "../../../../scripts/lib/extension-source-classifier.mjs";
|
||||
import { GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES } from "../../../plugin-sdk/test-helpers/public-artifacts.js";
|
||||
import { loadPluginManifestRegistry } from "../../../plugins/manifest-registry.js";
|
||||
@@ -48,7 +49,7 @@ const GUARDED_CHANNEL_EXTENSIONS = new Set([
|
||||
|
||||
function resolveBundledPluginSourceRoot(rootDir: string): string {
|
||||
const sourceRoot = resolve(REPO_ROOT, BUNDLED_PLUGIN_ROOT_DIR, basename(rootDir));
|
||||
return existsSync(sourceRoot) ? sourceRoot : rootDir;
|
||||
return fs.existsSync(sourceRoot) ? sourceRoot : rootDir;
|
||||
}
|
||||
|
||||
function bundledPluginFile(pluginId: string, relativePath: string): string {
|
||||
@@ -261,6 +262,7 @@ const RE_EXPORT_STAR_RE =
|
||||
const RE_EXPORT_NAMED_RE = /^\s*export\s+(?:type\s+)?\{[^}]*\}\s+from\s*["']([^"']+)["']/gmu;
|
||||
const DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/gmu;
|
||||
const REQUIRE_RE = /\brequire\s*\(\s*["']([^"']+)["']\s*\)/gmu;
|
||||
const trackedSourceFilesByRoot = new Map<string, readonly string[] | null>();
|
||||
|
||||
type SourceFileCollectorOptions = {
|
||||
rootDir: string;
|
||||
@@ -274,7 +276,7 @@ function readSource(path: string): string {
|
||||
if (cached !== undefined) {
|
||||
return cached;
|
||||
}
|
||||
const text = readFileSync(fullPath, "utf8");
|
||||
const text = fs.readFileSync(fullPath, "utf8");
|
||||
sourceTextCache.set(fullPath, text);
|
||||
return text;
|
||||
}
|
||||
@@ -283,6 +285,58 @@ function normalizePath(path: string): string {
|
||||
return path.replaceAll("\\", "/");
|
||||
}
|
||||
|
||||
function repoRelativePath(path: string): string {
|
||||
const normalizedRepoRoot = normalizePath(REPO_ROOT);
|
||||
const normalizedPath = normalizePath(path);
|
||||
return normalizedPath.startsWith(normalizedRepoRoot)
|
||||
? normalizedPath.slice(normalizedRepoRoot.length + 1)
|
||||
: normalizedPath;
|
||||
}
|
||||
|
||||
function listTrackedSourceFiles(options: SourceFileCollectorOptions): string[] | null {
|
||||
const relativeRoot = repoRelativePath(options.rootDir);
|
||||
if (!relativeRoot || relativeRoot.startsWith("..")) {
|
||||
return null;
|
||||
}
|
||||
if (trackedSourceFilesByRoot.has(relativeRoot)) {
|
||||
const files = trackedSourceFilesByRoot.get(relativeRoot);
|
||||
return files ? [...files] : null;
|
||||
}
|
||||
const result = spawnSync("git", ["ls-files", "--", relativeRoot], {
|
||||
cwd: REPO_ROOT,
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
trackedSourceFilesByRoot.set(relativeRoot, null);
|
||||
return null;
|
||||
}
|
||||
const files = result.stdout
|
||||
.split("\n")
|
||||
.map((line) => line.trim().replaceAll("\\", "/"))
|
||||
.filter((line) => {
|
||||
if (!/\.(?:[cm]?ts|[cm]?js|tsx|jsx)$/u.test(line) || line.endsWith(".d.ts")) {
|
||||
return false;
|
||||
}
|
||||
const parts = line.split("/");
|
||||
return !parts.some(
|
||||
(part) => part === "node_modules" || part === "dist" || part === "coverage",
|
||||
);
|
||||
})
|
||||
.map((line) => resolve(REPO_ROOT, line))
|
||||
.filter((fullPath) => {
|
||||
const normalizedFullPath = normalizePath(fullPath);
|
||||
const entryName = basename(fullPath);
|
||||
return !(
|
||||
options.shouldSkipPath?.(normalizedFullPath) ||
|
||||
options.shouldSkipEntry?.({ entryName, normalizedFullPath })
|
||||
);
|
||||
})
|
||||
.toSorted();
|
||||
trackedSourceFilesByRoot.set(relativeRoot, files);
|
||||
return [...files];
|
||||
}
|
||||
|
||||
function collectSourceFiles(
|
||||
cached: string[] | undefined | null,
|
||||
options: SourceFileCollectorOptions,
|
||||
@@ -290,6 +344,11 @@ function collectSourceFiles(
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const trackedFiles = listTrackedSourceFiles(options);
|
||||
if (trackedFiles) {
|
||||
return trackedFiles;
|
||||
}
|
||||
|
||||
const files: string[] = [];
|
||||
const stack = [options.rootDir];
|
||||
while (stack.length > 0) {
|
||||
@@ -297,7 +356,7 @@ function collectSourceFiles(
|
||||
if (!current) {
|
||||
continue;
|
||||
}
|
||||
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
||||
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
||||
const fullPath = resolve(current, entry.name);
|
||||
const normalizedFullPath = normalizePath(fullPath);
|
||||
if (entry.isDirectory()) {
|
||||
@@ -529,6 +588,22 @@ function expectCoreSourceStaysOffPluginSpecificSdkFacades(file: string, imports:
|
||||
}
|
||||
|
||||
describe("channel import guardrails", () => {
|
||||
it("lists channel import guardrail sources from git without walking roots", () => {
|
||||
const readDir = vi.spyOn(fs, "readdirSync");
|
||||
try {
|
||||
const extensionSources = collectExtensionSourceFiles();
|
||||
const coreSources = collectCoreSourceFiles();
|
||||
const telegramSources = collectExtensionFiles("telegram");
|
||||
|
||||
expect(extensionSources.length).toBeGreaterThan(0);
|
||||
expect(coreSources.length).toBeGreaterThan(0);
|
||||
expect(telegramSources.length).toBeGreaterThan(0);
|
||||
expect(readDir).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
readDir.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps channel helper modules off their own SDK barrels", () => {
|
||||
for (const source of SAME_CHANNEL_SDK_GUARDS) {
|
||||
const text = readSource(source.path);
|
||||
|
||||
Reference in New Issue
Block a user