mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:50:42 +00:00
test: guard broad plugin resolver fixtures
This commit is contained in:
@@ -391,6 +391,10 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
|||||||
- Runs in CI
|
- Runs in CI
|
||||||
- No real keys required
|
- No real keys required
|
||||||
- Should be fast and stable
|
- Should be fast and stable
|
||||||
|
- Resolver and public-surface loader tests must prove broad `api.js` and
|
||||||
|
`runtime-api.js` fallback behavior with generated tiny plugin fixtures, not
|
||||||
|
real bundled plugin source APIs. Real plugin API loads belong in
|
||||||
|
plugin-owned contract/integration suites.
|
||||||
|
|
||||||
<AccordionGroup>
|
<AccordionGroup>
|
||||||
<Accordion title="Projects, shards, and scoped lanes">
|
<Accordion title="Projects, shards, and scoped lanes">
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ can affect bundled plugins and third-party plugins.
|
|||||||
`api.ts` or `runtime-api.ts` plus generic SDK capabilities. Do not add a
|
`api.ts` or `runtime-api.ts` plus generic SDK capabilities. Do not add a
|
||||||
provider-named `src/plugin-sdk/<id>.ts` seam just to make core aware of a
|
provider-named `src/plugin-sdk/<id>.ts` seam just to make core aware of a
|
||||||
bundled channel's private helpers.
|
bundled channel's private helpers.
|
||||||
|
- Resolver/facade loader tests are the exception to broad source API coverage:
|
||||||
|
use generated tiny plugin fixtures for `api.js` / `runtime-api.js` fallback
|
||||||
|
behavior. Do not point those tests at real bundled plugin source APIs.
|
||||||
- For provider work, prefer family-level seams over provider-specific seams.
|
- For provider work, prefer family-level seams over provider-specific seams.
|
||||||
Shared helpers should describe a reusable behavior such as replay policy,
|
Shared helpers should describe a reusable behavior such as replay policy,
|
||||||
tool-schema compat, payload normalization, stream-wrapper composition, or
|
tool-schema compat, payload normalization, stream-wrapper composition, or
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ assembly, and contract enforcement.
|
|||||||
- If setup, discovery, or doctor flows need plugin runtime, make that need
|
- If setup, discovery, or doctor flows need plugin runtime, make that need
|
||||||
explicit and narrow. Do not let cold control-plane paths quietly import broad
|
explicit and narrow. Do not let cold control-plane paths quietly import broad
|
||||||
runtime surfaces.
|
runtime surfaces.
|
||||||
|
- Resolver and public-surface loader tests must use generated tiny plugin
|
||||||
|
fixtures for broad `api.js` / `runtime-api.js` fallback behavior. Do not point
|
||||||
|
those tests at real bundled plugin source APIs just to prove path resolution.
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ const ALLOWED_EXTENSION_PUBLIC_SURFACE_BASENAMES = new Set(
|
|||||||
GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES,
|
GUARDED_EXTENSION_PUBLIC_SURFACE_BASENAMES,
|
||||||
);
|
);
|
||||||
const CHANNEL_CONTRACT_TEST_HELPERS_PREFIX = "src/channels/plugins/contracts/test-helpers/";
|
const CHANNEL_CONTRACT_TEST_HELPERS_PREFIX = "src/channels/plugins/contracts/test-helpers/";
|
||||||
|
const BUNDLED_PLUGIN_RESOLVER_TEST_FILES = [
|
||||||
|
"src/plugin-sdk/facade-loader.test.ts",
|
||||||
|
"src/plugins/public-surface-loader.test.ts",
|
||||||
|
"src/plugins/public-surface-runtime.test.ts",
|
||||||
|
] as const;
|
||||||
|
const BROAD_PUBLIC_SOURCE_ARTIFACT_BASENAMES = new Set(["api.js", "runtime-api.js"]);
|
||||||
const ROOTDIR_BOUNDARY_CANARY_RE =
|
const ROOTDIR_BOUNDARY_CANARY_RE =
|
||||||
/(^|\/)__rootdir_boundary_canary__\.(?:[cm]?ts|[cm]?js|tsx|jsx)$/u;
|
/(^|\/)__rootdir_boundary_canary__\.(?:[cm]?ts|[cm]?js|tsx|jsx)$/u;
|
||||||
|
|
||||||
@@ -93,6 +99,41 @@ function getImportBasename(importPath: string): string {
|
|||||||
return importPath.split("/").at(-1) ?? importPath;
|
return importPath.split("/").at(-1) ?? importPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectBundledPluginIds(): Set<string> {
|
||||||
|
return new Set(
|
||||||
|
fs
|
||||||
|
.readdirSync(path.join(repoRoot, "extensions"), { withFileTypes: true })
|
||||||
|
.filter((entry) => entry.isDirectory())
|
||||||
|
.map((entry) => entry.name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLineNumber(source: string, index: number): number {
|
||||||
|
return source.slice(0, index).split("\n").length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findRealBroadSourceApiResolverReferences(
|
||||||
|
source: string,
|
||||||
|
pluginIds: Set<string>,
|
||||||
|
): string[] {
|
||||||
|
const offenders: string[] = [];
|
||||||
|
for (const match of source.matchAll(/\{[^{}]*\bdirName:\s*["'][^"']+["'][^{}]*\}/g)) {
|
||||||
|
const objectLiteral = match[0];
|
||||||
|
const dirName = objectLiteral.match(/\bdirName:\s*["']([^"']+)["']/)?.[1];
|
||||||
|
const artifactBasename = objectLiteral.match(/\bartifactBasename:\s*["']([^"']+)["']/)?.[1];
|
||||||
|
if (
|
||||||
|
dirName &&
|
||||||
|
artifactBasename &&
|
||||||
|
pluginIds.has(dirName) &&
|
||||||
|
BROAD_PUBLIC_SOURCE_ARTIFACT_BASENAMES.has(artifactBasename)
|
||||||
|
) {
|
||||||
|
offenders.push(`${dirName}/${artifactBasename}:${getLineNumber(source, match.index ?? 0)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return offenders;
|
||||||
|
}
|
||||||
|
|
||||||
function isAllowedCoreContractSuite(file: string, imports: readonly string[]): boolean {
|
function isAllowedCoreContractSuite(file: string, imports: readonly string[]): boolean {
|
||||||
return (
|
return (
|
||||||
file.startsWith("src/channels/plugins/contracts/") &&
|
file.startsWith("src/channels/plugins/contracts/") &&
|
||||||
@@ -190,6 +231,18 @@ describe("non-extension test boundaries", () => {
|
|||||||
expect(offenders).toEqual([]);
|
expect(offenders).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps resolver tests on generated fixtures for broad bundled plugin source APIs", () => {
|
||||||
|
const bundledPluginIds = collectBundledPluginIds();
|
||||||
|
const offenders = BUNDLED_PLUGIN_RESOLVER_TEST_FILES.flatMap((file) => {
|
||||||
|
const source = fs.readFileSync(path.join(repoRoot, file), "utf8");
|
||||||
|
return findRealBroadSourceApiResolverReferences(source, bundledPluginIds).map(
|
||||||
|
(reference) => `${file}: ${reference}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(offenders).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps bundled channel security collector coverage under extension tests", () => {
|
it("keeps bundled channel security collector coverage under extension tests", () => {
|
||||||
const files = [...walk(path.join(repoRoot, "src")), ...walk(path.join(repoRoot, "test"))]
|
const files = [...walk(path.join(repoRoot, "src")), ...walk(path.join(repoRoot, "test"))]
|
||||||
.filter((file) => !file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX))
|
.filter((file) => !file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX))
|
||||||
|
|||||||
Reference in New Issue
Block a user