Files
openclaw/test/scripts/check-deadcode-exports.test.ts
2026-07-15 18:18:23 -07:00

174 lines
5.8 KiB
TypeScript

// Check Deadcode Exports tests cover parsing and hard-zero enforcement.
import fs from "node:fs";
import { describe, expect, it } from "vitest";
import knipConfig from "../../config/knip.config.ts";
import {
checkUnusedExports,
parseKnipCompactUnusedExports,
parseKnipCompactUnusedExportsResult,
} from "../../scripts/check-deadcode-exports.mjs";
describe("check-deadcode-exports", () => {
it("requests every unused-export issue class from Knip", () => {
const script = fs.readFileSync(
new URL("../../scripts/check-deadcode-exports.mjs", import.meta.url),
"utf8",
);
expect(script).toContain('"exports,nsExports,types,nsTypes,enumMembers,namespaceMembers"');
});
it("excludes test support from every Knip issue type", () => {
expect(knipConfig.ignore).toContain("dist/**");
expect(knipConfig.ignore).toContain("**/test-helpers/**");
expect(knipConfig.ignore).toContain("**/*.test-utils.ts");
expect(knipConfig.ignoreFiles).not.toContain("**/test-helpers/**");
expect(knipConfig.ignoreFiles).toContain("scripts/**");
expect(knipConfig.ignoreFiles).toContain("dist/**");
expect(knipConfig.ignore).not.toContain("**/live-*.ts");
expect(knipConfig.ignoreFiles).toContain("**/live-*.ts");
});
it("tracks production script consumers of plugin exports", () => {
expect(knipConfig.workspaces["."].entry).toContain("scripts/qa/render-maturity-docs.ts!");
});
it("models the jiti virtual agent-sessions SDK entry", () => {
expect(knipConfig.workspaces["."].entry).toContain("src/agents/sessions/extension-sdk.ts!");
});
it("models the spawned system-agent MCP stdio entry", () => {
expect(knipConfig.workspaces["."].entry).toContain("src/mcp/openclaw-tools-serve.ts!");
});
it("keeps every migrated bundled-plugin workspace strict", () => {
const extensionWorkspaces = Object.entries(knipConfig.workspaces).filter(
([workspace]) =>
workspace.startsWith("extensions/") &&
!["extensions/*", "extensions/llama-cpp", "extensions/reef"].includes(workspace),
);
expect(extensionWorkspaces.length).toBeGreaterThan(1);
for (const [workspace, settings] of extensionWorkspaces) {
expect(settings.entry, workspace).not.toContain("*.ts!");
expect(settings.project, workspace).toContain("*.ts!");
expect(settings.entry, workspace).toEqual(
expect.arrayContaining([
"index.ts!",
"setup-entry.ts!",
"*-api.ts!",
"cli-metadata.ts!",
"channel-entry.ts!",
"provider-discovery.ts!",
"{web-search,web-fetch}-provider.ts!",
]),
);
}
expect(knipConfig.workspaces["extensions/*"].entry).toContain("*.ts!");
expect(knipConfig.workspaces["extensions/*"].project).toContain("*.ts!");
expect(knipConfig.workspaces["extensions/llama-cpp"].entry).toContain("*.ts!");
expect(knipConfig.workspaces["extensions/reef"].entry).toContain("*.ts!");
});
it("models the Browser facades loaded by basename", () => {
const workspace = knipConfig.workspaces["extensions/browser"];
expect(workspace.entry).toEqual(
expect.arrayContaining([
"browser-control-auth.ts!",
"browser-config.ts!",
"browser-doctor.ts!",
"browser-host-inspection.ts!",
"browser-maintenance.ts!",
"browser-profiles.ts!",
]),
);
});
it.each([
"packages/agent-core",
"packages/markdown-core",
"packages/media-core",
"packages/acp-core",
"packages/terminal-core",
] as const)("mirrors the published entry map for %s", (workspace) => {
const packageJson = JSON.parse(
fs.readFileSync(new URL(`../../${workspace}/package.json`, import.meta.url), "utf8"),
) as { exports: Record<string, unknown> };
const expected = Object.keys(packageJson.exports)
.map((subpath) =>
subpath === "." ? "src/index.ts!" : `src/${subpath.slice("./".length)}.ts!`,
)
.toSorted();
expect([...knipConfig.workspaces[workspace].entry].toSorted()).toEqual(expected);
});
it("parses all compact export sections and expands symbol lists", () => {
expect(
parseKnipCompactUnusedExports(`
Unused exports (2)
src/b.ts: beta, alpha
/tmp/outside.ts: noise
Unused exported types (1)
extensions/example/src/types.ts: ExampleType
Unused exported enum members (1)
packages/example/src/state.ts: Ready
Exports in used namespace (1)
src/namespace.ts: runtimeHelper
Exported types in used namespace (1)
src/namespace.ts: RuntimeType
Unused exported namespace members (1)
src/protocol.ts: Result (v2)
Unused files (1)
src/noise.ts: src/noise.ts
`),
).toEqual([
"extensions/example/src/types.ts: ExampleType",
"packages/example/src/state.ts: Ready",
"src/b.ts: alpha",
"src/b.ts: beta",
"src/namespace.ts: runtimeHelper",
"src/namespace.ts: RuntimeType",
"src/protocol.ts: Result (v2)",
]);
});
it("distinguishes a failed scan with no export sections from zero findings", () => {
expect(parseKnipCompactUnusedExportsResult("Configuration error: invalid project\n")).toEqual({
entries: [],
sawExportSection: false,
});
expect(parseKnipCompactUnusedExportsResult("Unused exports (0)\n")).toEqual({
entries: [],
sawExportSection: true,
});
});
it("accepts an empty compact report with zero unused exports", () => {
expect(checkUnusedExports("")).toEqual({
ok: true,
entries: [],
message: "",
});
});
it("rejects every unused export without an allowlist", () => {
expect(
checkUnusedExports(`Unused exports (2)
src/z.ts: zebra
src/a.ts: alpha
`),
).toEqual({
ok: false,
entries: ["src/a.ts: alpha", "src/z.ts: zebra"],
message: `Unused exports are not allowed:
src/a.ts: alpha
src/z.ts: zebra
Delete the exports or model their real production consumers in Knip.`,
});
});
});