import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { collectSourceFiles, findForbiddenPluginImports, scanForbiddenPluginImports, } from "./verify-core-build-without-plugins-smoke.mjs"; const tempRoots = []; async function createFixtureRoot() { const root = await mkdtemp(path.join(os.tmpdir(), "featureManifest-core-smoke-")); tempRoots.push(root); return root; } async function writeFixtureFile(root, relativePath, content) { const absolutePath = path.join(root, relativePath); await mkdir(path.dirname(absolutePath), { recursive: true }); await writeFile(absolutePath, content, "utf8"); return absolutePath; } function relativeFiles(root, files) { return files.map((file) => path.relative(root, file).split(path.sep).join("/")); } afterEach(async () => { await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true }))); }); describe("Feature Manifest core build smoke verifier helpers", () => { it("reports zero forbidden imports for clean app and package sources", async () => { const root = await createFixtureRoot(); await writeFixtureFile(root, "app/root.tsx", "import { Link } from 'react-router';\nexport const Root = Link;\n"); await writeFixtureFile(root, "packages/core/index.ts", "export const coreOnly = true;\n"); const result = await scanForbiddenPluginImports(root, ["app", "packages"]); expect(result.missingRoots).toEqual([]); expect(relativeFiles(root, result.files)).toEqual(["app/root.tsx", "packages/core/index.ts"]); expect(result.diagnostics).toEqual([]); }); it("reports non-test runtime package imports and plugin path references with file and line metadata", async () => { const root = await createFixtureRoot(); await writeFixtureFile( root, "app/runtime.ts", [ "import { loadPlugins } from '@toolctrl/plugins';", "const allowed = 'not a plugin reference';", "const manifestPath = 'packages/plugins/manifest.json';", ].join("\n"), ); const result = await scanForbiddenPluginImports(root, ["app"]); expect(result.diagnostics).toEqual([ { file: "app/runtime.ts", line: 1, id: "toolctrl-plugins-package", text: "import { loadPlugins } from '@toolctrl/plugins';", }, { file: "app/runtime.ts", line: 3, id: "packages-plugins-path", text: "const manifestPath = 'packages/plugins/manifest.json';", }, ]); }); it("scans package manifests and Dockerfile runtime copy paths", async () => { const root = await createFixtureRoot(); await writeFixtureFile(root, "package.json", '{ "dependencies": { "@toolctrl/plugins": "workspace:*" } }\n'); await writeFixtureFile(root, "Dockerfile", "COPY --from=build /myapp/packages/plugins /myapp/packages/plugins\n"); const result = await scanForbiddenPluginImports(root, ["package.json", "Dockerfile"]); expect(result.diagnostics).toEqual([ { file: "Dockerfile", line: 1, id: "packages-plugins-path", text: "COPY --from=build /myapp/packages/plugins /myapp/packages/plugins", }, { file: "package.json", line: 1, id: "toolctrl-plugins-package", text: '{ "dependencies": { "@toolctrl/plugins": "workspace:*" } }', }, ]); }); it("allows plugin package metadata that is not copied into runtime images", async () => { const root = await createFixtureRoot(); await writeFixtureFile(root, "Dockerfile", "COPY packages/plugins/package.json packages/plugins/package.json\n"); await writeFixtureFile( root, "scripts/coverage-targets.json", '{"name":"packages/plugins","pathPrefixes":["packages/plugins/src/"],"includeGlobs":["packages/plugins/src"]}\n', ); const result = await scanForbiddenPluginImports(root, ["Dockerfile", "scripts"]); expect(result.diagnostics).toEqual([]); }); it("ignores tests, docs, plugin-package files, dependency folders, TC metadata, and build output", async () => { const root = await createFixtureRoot(); await writeFixtureFile(root, "app/entry.ts", "export const entry = true;\n"); await writeFixtureFile(root, "app/entry.test.ts", "import '@toolctrl/plugins';\n"); await writeFixtureFile(root, "app/readme.md", "packages/plugins docs reference\n"); await writeFixtureFile(root, "packages/plugins/index.ts", "import '@toolctrl/plugins';\n"); await writeFixtureFile(root, "app/node_modules/example/index.ts", "import '@toolctrl/plugins';\n"); await writeFixtureFile(root, "app/.tc/tasks/plan.ts", "import '@toolctrl/plugins';\n"); await writeFixtureFile(root, "app/build/server.js", "import '@toolctrl/plugins';\n"); const result = await scanForbiddenPluginImports(root, ["app", "packages"]); expect(relativeFiles(root, result.files)).toEqual(["app/entry.ts"]); expect(result.diagnostics).toEqual([]); }); it("records absent roots without failing collection", async () => { const root = await createFixtureRoot(); await writeFixtureFile(root, "app/entry.ts", "export const entry = true;\n"); const result = await collectSourceFiles(root, ["app", "apps", "scripts"]); expect(result.roots).toEqual(["app"]); expect(result.missingRoots).toEqual(["apps", "scripts"]); expect(relativeFiles(root, result.files)).toEqual(["app/entry.ts"]); }); it("detects forbidden lines directly from source text", () => { const diagnostics = findForbiddenPluginImports( "const pkg = '@toolctrl/plugins';\nconst path = 'packages/plugins/feature';\n", "app/direct.ts", ); expect(diagnostics).toEqual([ { file: "app/direct.ts", line: 1, id: "toolctrl-plugins-package", text: "const pkg = '@toolctrl/plugins';", }, { file: "app/direct.ts", line: 2, id: "packages-plugins-path", text: "const path = 'packages/plugins/feature';", }, ]); }); });