96aad47eb6
Refactor: - session.ts: extract mapDebugpyMissingModule helper; replace the duplicated inline check in launch/attach catch blocks. Add jsdoc on DapStartRequestFailure.settled documenting per-call ownership and how throwPreferredDapStartError consumes it. - path-utils.ts: replace the no-op keepOpaqueResourceUri branch with an OPAQUE_RESOURCE_SCHEMES Set so the structure carries the intent. Functionally equivalent; new opaque schemes become a one-line Set change. Tests: - dap-launch-failures: cover the debugpy stderr -> 'pip install debugpy' rewrite for launch and attach, plus a negative case (non-debugpy adapter with the substring in stderr is left untouched). - dap-launch-failures: model the delayed-launch-failure case the new settled-race in throwPreferredDapStartError defends against. FakeDapClient gains optional launchErrorDelayMs/attachErrorDelayMs. - dap-launch-failures (DebugTool): assert adapter:'debugpy' early-throw surfaces 'python not found in PATH' on both launch and attach when selectLaunchAdapter/selectAttachAdapter return null, and the unspecified-adapter path still falls back to the generic 'No debugger adapter' error. - find.ts: export validateFindPathInputs and pin the new backslash-escape semantics (\, no longer trips the comma-joined heuristic) plus the existing brace-expansion and rejection paths. - patch.ts: cover the post-write verification error message. The user-facing ToolError must contain the caller-supplied relative path and not the absolute resolvedPath (which still lives in the structured context for log correlation). - split-internal-url-sel: reword two mcp:// test comments that described a 'peeler refuses' guard that doesn't exist; rename the tests to reflect the actual opaque-scheme rule.
43 lines
1.9 KiB
TypeScript
43 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import { validateFindPathInputs } from "../../src/tools/find";
|
|
|
|
describe("validateFindPathInputs", () => {
|
|
it("accepts a normal array of glob entries", () => {
|
|
expect(() => validateFindPathInputs(["src/**/*.ts", "test/**/*.ts"])).not.toThrow();
|
|
});
|
|
|
|
it('rejects comma-joined entries (the `["a,b"]` shape)', () => {
|
|
expect(() => validateFindPathInputs(["a.py,b.py"])).toThrow(/paths is an array/);
|
|
});
|
|
|
|
it("allows commas inside brace expansion", () => {
|
|
expect(() => validateFindPathInputs(["src/{a,b}/*.ts"])).not.toThrow();
|
|
expect(() => validateFindPathInputs(["{foo,bar,baz}.md"])).not.toThrow();
|
|
});
|
|
|
|
it("allows backslash-escaped commas at top level (matches search.ts:containsTopLevelComma)", () => {
|
|
// Backslash-escapes a literal comma in a filename — must not trip the
|
|
// array-vs-string heuristic.
|
|
expect(() => validateFindPathInputs(["weird\\,name.txt"])).not.toThrow();
|
|
expect(() => validateFindPathInputs(["a\\,b\\,c"])).not.toThrow();
|
|
});
|
|
|
|
it("still rejects unescaped top-level commas mixed with escaped ones", () => {
|
|
// `a\,b,c` — the second comma is unescaped, so the heuristic should fire.
|
|
expect(() => validateFindPathInputs(["a\\,b,c"])).toThrow(/paths is an array/);
|
|
});
|
|
|
|
it("allows a trailing backslash without crashing", () => {
|
|
// `foo\\` is a backslash at end-of-string; the i+1<length guard must hold.
|
|
expect(() => validateFindPathInputs(["foo\\"])).not.toThrow();
|
|
});
|
|
|
|
it("treats `\\{a,b}` as an escaped brace, so the inner comma is still top-level", () => {
|
|
// Skip-next semantics: the backslash consumes the `{`, so braceDepth stays 0
|
|
// and the unescaped `,` between `a` and `b` rejects. This pins the literal
|
|
// behavior of the new escape-skip, which intentionally does NOT model glob
|
|
// brace semantics — it only mirrors search.ts's containsTopLevelComma.
|
|
expect(() => validateFindPathInputs(["\\{a,b}"])).toThrow(/paths is an array/);
|
|
});
|
|
});
|