test(mcp-server): add regression tests for importLocalModule candidate resolution
Extracts _buildImportCandidates() as a testable export and adds 5 tests verifying src/<->dist/ path swapping and .ts extension fallback logic. Refs #3954
This commit is contained in:
parent
ad8405c372
commit
67767c2527
2 changed files with 60 additions and 7 deletions
48
packages/mcp-server/src/import-candidates.test.ts
Normal file
48
packages/mcp-server/src/import-candidates.test.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// GSD-2 — Regression tests for importLocalModule candidate resolution (#3954)
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { _buildImportCandidates } from "./workflow-tools.js";
|
||||
|
||||
describe("_buildImportCandidates", () => {
|
||||
it("includes dist/ fallback for src/ paths", () => {
|
||||
const candidates = _buildImportCandidates("../../../src/resources/extensions/gsd/db-writer.js");
|
||||
assert.ok(
|
||||
candidates.some((c) => c.includes("/dist/resources/extensions/gsd/db-writer.js")),
|
||||
"should include dist/ swapped candidate",
|
||||
);
|
||||
});
|
||||
|
||||
it("includes src/ fallback for dist/ paths", () => {
|
||||
const candidates = _buildImportCandidates("../../../dist/resources/extensions/gsd/db-writer.js");
|
||||
assert.ok(
|
||||
candidates.some((c) => c.includes("/src/resources/extensions/gsd/db-writer.js")),
|
||||
"should include src/ swapped candidate",
|
||||
);
|
||||
});
|
||||
|
||||
it("includes .ts variants for .js paths", () => {
|
||||
const candidates = _buildImportCandidates("../../../src/resources/extensions/gsd/db-writer.js");
|
||||
assert.ok(
|
||||
candidates.some((c) => c.endsWith("db-writer.ts") && c.includes("/src/")),
|
||||
"should include .ts variant for original src/ path",
|
||||
);
|
||||
assert.ok(
|
||||
candidates.some((c) => c.endsWith("db-writer.ts") && c.includes("/dist/")),
|
||||
"should include .ts variant for swapped dist/ path",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns original path first", () => {
|
||||
const input = "../../../src/resources/extensions/gsd/db-writer.js";
|
||||
const candidates = _buildImportCandidates(input);
|
||||
assert.equal(candidates[0], input, "first candidate should be the original path");
|
||||
});
|
||||
|
||||
it("handles paths without src/ or dist/ gracefully", () => {
|
||||
const candidates = _buildImportCandidates("./local-module.js");
|
||||
assert.equal(candidates.length, 2, "should have original + .ts variant only");
|
||||
assert.equal(candidates[0], "./local-module.js");
|
||||
assert.equal(candidates[1], "./local-module.ts");
|
||||
});
|
||||
});
|
||||
|
|
@ -337,24 +337,29 @@ function toFileUrl(modulePath: string): string {
|
|||
return pathToFileURL(resolve(modulePath)).href;
|
||||
}
|
||||
|
||||
async function importLocalModule<T>(relativePath: string): Promise<T> {
|
||||
/** @internal — exported for testing only */
|
||||
export function _buildImportCandidates(relativePath: string): string[] {
|
||||
// Build candidate paths: try the given path first, then swap src/<->dist/
|
||||
// and try .ts extension. This handles both dev (tsx from src/) and prod
|
||||
// (compiled from dist/) execution contexts.
|
||||
const candidates: string[] = [
|
||||
new URL(relativePath, import.meta.url).href,
|
||||
];
|
||||
const candidates: string[] = [relativePath];
|
||||
const swapped = relativePath.includes("/src/")
|
||||
? relativePath.replace("/src/", "/dist/")
|
||||
: relativePath.includes("/dist/")
|
||||
? relativePath.replace("/dist/", "/src/")
|
||||
: null;
|
||||
if (swapped) candidates.push(new URL(swapped, import.meta.url).href);
|
||||
if (swapped) candidates.push(swapped);
|
||||
// Also try .ts variants for dev-mode tsx execution
|
||||
if (relativePath.endsWith(".js")) {
|
||||
candidates.push(new URL(relativePath.replace(/\.js$/, ".ts"), import.meta.url).href);
|
||||
if (swapped) candidates.push(new URL(swapped.replace(/\.js$/, ".ts"), import.meta.url).href);
|
||||
candidates.push(relativePath.replace(/\.js$/, ".ts"));
|
||||
if (swapped) candidates.push(swapped.replace(/\.js$/, ".ts"));
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
async function importLocalModule<T>(relativePath: string): Promise<T> {
|
||||
const candidates = _buildImportCandidates(relativePath)
|
||||
.map((p) => new URL(p, import.meta.url).href);
|
||||
|
||||
let lastErr: unknown;
|
||||
for (const candidate of candidates) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue