164 lines
6.0 KiB
JavaScript
164 lines
6.0 KiB
JavaScript
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';",
|
|
},
|
|
]);
|
|
});
|
|
});
|