fix(FN-2360): harden CLI vitest workspace source aliasing

- Convert internal @fusion workspace aliases in packages/cli/vitest.config.ts to exact anchored regex entrypoint mappings
- Preserve subpath-before-root alias order so @fusion/core/gh-cli and @fusion/dashboard/planning resolve correctly
- Add vitest-workspace-resolution regression coverage for alias definitions and ordering
- Simulate clean worktrees by temporarily hiding internal dist/ directories and verify dynamic imports resolve from source
This commit is contained in:
Fusion
2026-04-23 15:02:07 -07:00
committed by gsxdsm
parent 5454408713
commit 54843012bc
4 changed files with 150 additions and 9 deletions

View File

@@ -182,7 +182,7 @@ describe("build-exe", () => {
reject(
new Error(`Server startup timeout\nOutput:\n${startupOutput}`),
);
}, 10_000);
}, 30_000);
const settle = (
result: "ready" | "sqlite-unsupported" | Error,
@@ -303,5 +303,5 @@ describe("build-exe", () => {
await stopChildProcess(child);
cleanup();
}
}, 20_000);
}, 60_000);
});

View File

@@ -0,0 +1,96 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { existsSync, renameSync } from "node:fs";
import { join } from "node:path";
import vitestConfig from "../../vitest.config";
const cliRoot = join(__dirname, "..", "..");
const workspaceRoot = join(cliRoot, "..", "..");
const internalPackages = ["core", "engine", "dashboard"] as const;
const movedDistDirs: Array<{ from: string; to: string }> = [];
function hideInternalPackageDistDirs() {
for (const pkg of internalPackages) {
const distPath = join(workspaceRoot, "packages", pkg, "dist");
if (!existsSync(distPath)) {
continue;
}
const hiddenPath = `${distPath}.__fn2360-hidden-${process.pid}`;
renameSync(distPath, hiddenPath);
movedDistDirs.push({ from: distPath, to: hiddenPath });
}
}
function restoreInternalPackageDistDirs() {
for (let i = movedDistDirs.length - 1; i >= 0; i--) {
const { from, to } = movedDistDirs[i];
if (existsSync(to)) {
renameSync(to, from);
}
}
movedDistDirs.length = 0;
}
describe("CLI Vitest workspace resolution", () => {
beforeAll(() => {
hideInternalPackageDistDirs();
});
afterAll(() => {
restoreInternalPackageDistDirs();
});
it("uses exact source-entry aliases for internal workspace packages", () => {
const aliases = vitestConfig.resolve?.alias;
expect(Array.isArray(aliases)).toBe(true);
const normalized = (aliases ?? []).map((entry) => ({
find: String(entry.find),
replacement: String(entry.replacement),
}));
expect(normalized).toEqual(
expect.arrayContaining([
{
find: String(/^@fusion\/core\/gh-cli$/),
replacement: join(workspaceRoot, "packages", "core", "src", "gh-cli.ts"),
},
{
find: String(/^@fusion\/core$/),
replacement: join(workspaceRoot, "packages", "core", "src", "index.ts"),
},
{
find: String(/^@fusion\/engine$/),
replacement: join(workspaceRoot, "packages", "engine", "src", "index.ts"),
},
{
find: String(/^@fusion\/dashboard$/),
replacement: join(workspaceRoot, "packages", "dashboard", "src", "index.ts"),
},
{
find: String(/^@fusion\/dashboard\/planning$/),
replacement: join(workspaceRoot, "packages", "dashboard", "src", "planning.ts"),
},
]),
);
const coreGhCliIndex = normalized.findIndex((entry) => entry.find === String(/^@fusion\/core\/gh-cli$/));
const coreIndex = normalized.findIndex((entry) => entry.find === String(/^@fusion\/core$/));
const planningIndex = normalized.findIndex((entry) => entry.find === String(/^@fusion\/dashboard\/planning$/));
const dashboardIndex = normalized.findIndex((entry) => entry.find === String(/^@fusion\/dashboard$/));
expect(coreGhCliIndex).toBeGreaterThanOrEqual(0);
expect(coreIndex).toBeGreaterThan(coreGhCliIndex);
expect(planningIndex).toBeGreaterThanOrEqual(0);
expect(dashboardIndex).toBeGreaterThan(planningIndex);
});
it("resolves internal workspace imports during CLI tests when dist outputs are absent", async () => {
await expect(import("@fusion/core")).resolves.toBeTruthy();
await expect(import("@fusion/core/gh-cli")).resolves.toBeTruthy();
await expect(import("@fusion/engine")).resolves.toBeTruthy();
await expect(import("@fusion/dashboard")).resolves.toBeTruthy();
await expect(import("@fusion/dashboard/planning")).resolves.toBeTruthy();
}, 30_000);
});

View File

@@ -8,14 +8,17 @@ process.env.VITEST_MAX_WORKERS = String(maxWorkers);
export default defineConfig({
resolve: {
// Use ordered aliases so the subpath match is resolved before @fusion/core.
// Keep these aliases exact and ordered (subpaths before package roots).
// In fresh worktrees, internal packages may not have dist/ built yet, and
// Vite otherwise resolves workspace package exports.import to dist/*.js.
// Anchored regex aliases force CLI tests to use source entrypoints instead.
alias: [
{ find: "@fusion/core/gh-cli", replacement: resolve(__dirname, "../core/src/gh-cli.ts") },
{ find: "@fusion/core", replacement: resolve(__dirname, "../core/src/index.ts") },
{ find: "@fusion/dashboard/planning", replacement: resolve(__dirname, "../dashboard/src/planning.ts") },
{ find: "@fusion/dashboard", replacement: resolve(__dirname, "../dashboard/src/index.ts") },
{ find: "@fusion/engine", replacement: resolve(__dirname, "../engine/src/index.ts") },
{ find: "@fusion/test-utils", replacement: resolve(__dirname, "../core/src/__test-utils__/workspace.ts") },
{ find: /^@fusion\/core\/gh-cli$/, replacement: resolve(__dirname, "../core/src/gh-cli.ts") },
{ find: /^@fusion\/core$/, replacement: resolve(__dirname, "../core/src/index.ts") },
{ find: /^@fusion\/dashboard\/planning$/, replacement: resolve(__dirname, "../dashboard/src/planning.ts") },
{ find: /^@fusion\/dashboard$/, replacement: resolve(__dirname, "../dashboard/src/index.ts") },
{ find: /^@fusion\/engine$/, replacement: resolve(__dirname, "../engine/src/index.ts") },
{ find: /^@fusion\/test-utils$/, replacement: resolve(__dirname, "../core/src/__test-utils__/workspace.ts") },
],
},
test: {