Files
openclaw/test/scripts/ts-topology.test.ts
2026-04-27 09:46:06 +01:00

135 lines
4.8 KiB
TypeScript

import path from "node:path";
import { describe, expect, it } from "vitest";
import { analyzeTopology, filterRecordsForReport } from "../../scripts/lib/ts-topology/analyze.js";
import { renderTextReport } from "../../scripts/lib/ts-topology/reports.js";
import { createFilesystemPublicSurfaceScope } from "../../scripts/lib/ts-topology/scope.js";
import { main } from "../../scripts/ts-topology.ts";
import { createCapturedIo } from "../helpers/captured-io.js";
const repoRoot = path.join(process.cwd(), "test", "fixtures", "ts-topology", "basic");
function buildFixtureScope() {
return createFilesystemPublicSurfaceScope(repoRoot, {
id: "custom",
entrypointRoot: "src/public",
importPrefix: "fixture-sdk",
});
}
const fixtureScope = buildFixtureScope();
const publicSurfaceEnvelope = analyzeTopology({
repoRoot,
scope: fixtureScope,
report: "public-surface-usage",
});
function deriveReportEnvelope(report: Parameters<typeof filterRecordsForReport>[1]) {
return {
...publicSurfaceEnvelope,
report,
records: filterRecordsForReport(publicSurfaceEnvelope.records, report),
};
}
const singleOwnerEnvelope = deriveReportEnvelope("single-owner-shared");
const unusedEnvelope = deriveReportEnvelope("unused-public-surface");
describe("ts-topology", () => {
it("collapses canonical symbols exported by multiple public subpaths", () => {
const sharedThing = publicSurfaceEnvelope.records.find((record) =>
record.exportNames.includes("sharedThing"),
);
expect(sharedThing).toMatchObject({
declarationPath: "src/lib/shared.ts",
declarationLine: 1,
productionExtensions: ["alpha", "beta"],
productionPackages: ["src"],
productionOwners: ["extension:alpha", "extension:beta", "src"],
});
expect(sharedThing?.publicSpecifiers).toEqual(["fixture-sdk", "fixture-sdk/extra"]);
});
it("counts renamed imports, namespace imports, type-only imports, and test-only consumers", () => {
const aliasedThing = publicSurfaceEnvelope.records.find((record) =>
record.exportNames.includes("aliasedThing"),
);
const sharedType = publicSurfaceEnvelope.records.find((record) =>
record.exportNames.includes("SharedType"),
);
const testOnlyThing = publicSurfaceEnvelope.records.find((record) =>
record.exportNames.includes("testOnlyThing"),
);
expect(aliasedThing?.productionRefCount).toBe(1);
expect(sharedType).toMatchObject({
isTypeOnlyCandidate: true,
productionExtensions: ["alpha", "beta"],
productionRefCount: 2,
});
expect(testOnlyThing).toMatchObject({
productionRefCount: 0,
testRefCount: 1,
testConsumers: ["tests/public.test.ts"],
});
});
it("surfaces single-owner shared and unused reports correctly", () => {
expect(singleOwnerEnvelope.records.map((record) => record.exportNames[0])).toContain(
"singleOwnerHelper",
);
expect(singleOwnerEnvelope.records.map((record) => record.exportNames[0])).not.toContain(
"sharedThing",
);
expect(unusedEnvelope.records.map((record) => record.exportNames[0])).toEqual(["unusedThing"]);
});
it("renders stable text summaries for the public-surface report", () => {
expect(renderTextReport({ ...publicSurfaceEnvelope, limit: 3 }, 3)).toMatchInlineSnapshot(`
"Scope: custom
Public exports analyzed: 6
Production-used exports: 4
Single-owner shared exports: 2
Unused public exports: 1
Top 2 candidate-to-move exports:
- fixture-sdk:aliasedThing -> src/lib/shared.ts:9 (prodRefs=1, owners=extension:alpha, sharedness=35, move=85)
- fixture-sdk:singleOwnerHelper -> src/lib/shared.ts:5 (prodRefs=1, owners=extension:alpha, sharedness=35, move=85)
Top 1 duplicated public exports:
- fixture-sdk:sharedThing via fixture-sdk, fixture-sdk/extra (src/lib/shared.ts:1)"
`);
});
it("emits stable JSON through the CLI and filtered report output", async () => {
const captured = createCapturedIo();
const jsonExit = await main(
[
"--scope=custom",
"--entrypoint-root=src/public",
"--import-prefix=fixture-sdk",
"--repo-root=test/fixtures/ts-topology/basic",
"--report=single-owner-shared",
"--json",
],
captured.io,
);
expect(jsonExit).toBe(0);
const payload = JSON.parse(captured.readStdout());
expect(payload.report).toBe("single-owner-shared");
expect(
payload.records.map((record: { exportNames: string[] }) => record.exportNames[0]),
).toEqual(["aliasedThing", "singleOwnerHelper"]);
expect(renderTextReport(deriveReportEnvelope("consumer-topology"), 2)).toMatchInlineSnapshot(`
"Scope: custom
Records with consumers: 5
Top 2 consumer-topology records:
- fixture-sdk:sharedThing prod=3 test=0 internal=0
- fixture-sdk:SharedType prod=2 test=0 internal=0"
`);
});
});