Skip to content

test: tools — isToolOnPath discovery and BatchTool validation#412

Closed
anandgupta42 wants to merge 2 commits intomainfrom
test/hourly-20260323-2111
Closed

test: tools — isToolOnPath discovery and BatchTool validation#412
anandgupta42 wants to merge 2 commits intomainfrom
test/hourly-20260323-2111

Conversation

@anandgupta42
Copy link
Contributor

@anandgupta42 anandgupta42 commented Mar 23, 2026

What does this PR do?

1. isToolOnPathsrc/cli/cmd/skill-helpers.ts (4 new tests)

This function checks whether a CLI tool referenced by a skill is available on the user's system — searching .opencode/tools/, ALTIMATE_BIN_DIR, and system PATH. Zero tests existed previously. If this function regresses, skills would falsely report tools as "missing" even when they're installed, confusing users into unnecessary reinstalls.

New coverage includes:

  • Tool found in .opencode/tools/ under cwd (primary lookup path)
  • Returns false when tool genuinely doesn't exist anywhere (prevents false positives)
  • Tool found via ALTIMATE_BIN_DIR environment variable (bespoke feature, most regression-prone)
  • Tool found via prepended PATH directory (system PATH scan)

2. BatchTool validation — src/tool/batch.ts (4 new tests)

The batch tool allows parallel tool execution and has validation logic including formatValidationError (user-facing error messages when the LLM sends malformed input) and anti-recursion guards. Zero tests existed previously. If formatValidationError regresses, models receive cryptic errors instead of actionable guidance and may loop.

New coverage includes:

  • formatValidationError produces user-friendly output for empty tool_calls array (most common bad payload)
  • formatValidationError includes nested field paths (e.g. tool_calls.0.tool) for precise error localization
  • Validation rejects missing tool_calls field entirely
  • Tool shape verification (id, description, parameters, execute, formatValidationError all present)

Type of change

  • New feature (non-breaking change which adds functionality)

Issue for this PR

N/A — proactive test coverage from /test-discovery

How did you verify your code works?

bun test test/cli/is-tool-on-path.test.ts   # 4 pass
bun test test/tool/batch.test.ts             # 4 pass

Marker check: bun run script/upstream/analyze.ts --markers --base main --strict — passed (no upstream files modified).

Checklist

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

https://claude.ai/code/session_01BJDADsQt5Q15LUSQgMgkas

Summary by CodeRabbit

  • Tests
    • Added comprehensive test suites covering tool discovery mechanisms and validation error formatting.

Cover untested tool infrastructure: isToolOnPath PATH/env resolution
and BatchTool's formatValidationError output formatting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

https://claude.ai/code/session_01BJDADsQt5Q15LUSQgMgkas
Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai
Copy link

coderabbitai bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

This pull request adds two new test suites to the opencode package. The first validates isToolOnPath behavior across multiple discovery scenarios including filesystem, environment variables, and system PATH. The second tests BatchTool's validation error formatting and tool registration metadata.

Changes

Cohort / File(s) Summary
isToolOnPath CLI Tests
packages/opencode/test/cli/is-tool-on-path.test.ts
New test suite validating tool discovery logic: filesystem-based tools, environment variable resolution, and system PATH lookups. Includes environment isolation via snapshot/restore pattern.
BatchTool Validation Tests
packages/opencode/test/tool/batch.test.ts
New test suite covering BatchTool.formatValidationError with invalid inputs (empty/missing/malformed tool_calls), plus verification of public module exports and tool metadata structure.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

contributor

Poem

🐰 A rabbit hops through test arrays so bright,
Checking if tools are found left and right,
BatchTool validation now runs like a dream,
Error messages flow in a perfect stream,
Coverage grows strong—what a wonderful sight! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: adding tests for isToolOnPath discovery and BatchTool validation.
Description check ✅ Passed The description follows the template with Summary, Test Plan, and Checklist sections, providing detailed context on test coverage and verification.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/hourly-20260323-2111

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/opencode/test/cli/is-tool-on-path.test.ts (1)

9-22: Consider using tmpdir({ git: true }) if it provides equivalent functionality.

The coding guidelines recommend using the git: true option for git-initialized temp directories. If the built-in option doesn't configure fsmonitor, gpgsign, and user identity, then this custom helper is justified. Otherwise, consolidating on the standard pattern improves consistency.

Based on learnings: "Use the git: true option when initializing a temporary test directory that needs a git repository with a root commit."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/cli/is-tool-on-path.test.ts` around lines 9 - 22, The
tmpdirGit helper duplicates git setup that may be provided by tmpdir({ git: true
}); update the tmpdirGit function to call tmpdir({ git: true, init: async (dir)
=> { ... } }) or replace usages to use tmpdir({ git: true }) and only apply the
extra git config steps (core.fsmonitor, commit.gpgsign, user.email, user.name
and initial commit) when the built-in git option does not already perform them;
keep the existing optional init callback behavior intact so callers relying on
tmpdirGit(init) still run their init after the repo is created.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/test/cli/is-tool-on-path.test.ts`:
- Around line 101-102: The test hardcodes ":" when setting process.env.PATH
which breaks Windows; change the assignment to use Node's platform-aware
delimiter (e.g., path.delimiter) or the same conditional used in
skill-helpers.ts so that savedEnv.PATH and the new process.env.PATH assignment
use path.delimiter (or process.platform === "win32" ? ";" : ":") when building
path.join(tmp.path, "path-bin") + delimiter + (process.env.PATH ?? "").

In `@packages/opencode/test/tool/batch.test.ts`:
- Around line 56-80: The test currently only checks metadata but must also
assert the DISALLOWED guard; update the test that references BatchTool to call
the execute function and verify it rejects when given a call with call.tool ===
"batch" and that the thrown/error message includes the phrase "not allowed in
batch"; if execute requires Session.updatePart, supply a minimal stub/mock
session object or otherwise stub that dependency so execute can run and throw
the DISALLOWED error—target symbols: BatchTool, BatchTool.init, and the execute
function exported from "../../src/tool/batch".
- Around line 17-23: The test currently conditionally skips formatter assertions
when toolInfo.formatValidationError is missing; instead, assert that the
formatter exists before using it so the test fails fast. Update the test around
the result handling (the block using result, result.success, and
toolInfo.formatValidationError) to assert toolInfo.formatValidationError is
defined (or throw) when result.success is false, then call
toolInfo.formatValidationError(result.error) and perform the existing
expect(...).toContain checks for "Invalid parameters for tool 'batch'", "Provide
at least one tool call", and "Expected payload format".

---

Nitpick comments:
In `@packages/opencode/test/cli/is-tool-on-path.test.ts`:
- Around line 9-22: The tmpdirGit helper duplicates git setup that may be
provided by tmpdir({ git: true }); update the tmpdirGit function to call
tmpdir({ git: true, init: async (dir) => { ... } }) or replace usages to use
tmpdir({ git: true }) and only apply the extra git config steps (core.fsmonitor,
commit.gpgsign, user.email, user.name and initial commit) when the built-in git
option does not already perform them; keep the existing optional init callback
behavior intact so callers relying on tmpdirGit(init) still run their init after
the repo is created.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f986a5c1-deae-4846-adc9-2171fb2378c2

📥 Commits

Reviewing files that changed from the base of the PR and between 3b6d5d4 and 34bc257.

📒 Files selected for processing (2)
  • packages/opencode/test/cli/is-tool-on-path.test.ts
  • packages/opencode/test/tool/batch.test.ts

Comment on lines +101 to +102
savedEnv.PATH = process.env.PATH
process.env.PATH = path.join(tmp.path, "path-bin") + ":" + (process.env.PATH ?? "")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded : separator breaks Windows compatibility.

The implementation in skill-helpers.ts uses process.platform === "win32" ? ";" : ":" for the PATH separator. The test should mirror this to remain cross-platform.

🔧 Proposed fix
+    const sep = process.platform === "win32" ? ";" : ":"
     savedEnv.PATH = process.env.PATH
-    process.env.PATH = path.join(tmp.path, "path-bin") + ":" + (process.env.PATH ?? "")
+    process.env.PATH = path.join(tmp.path, "path-bin") + sep + (process.env.PATH ?? "")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
savedEnv.PATH = process.env.PATH
process.env.PATH = path.join(tmp.path, "path-bin") + ":" + (process.env.PATH ?? "")
const sep = process.platform === "win32" ? ";" : ":"
savedEnv.PATH = process.env.PATH
process.env.PATH = path.join(tmp.path, "path-bin") + sep + (process.env.PATH ?? "")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/cli/is-tool-on-path.test.ts` around lines 101 - 102,
The test hardcodes ":" when setting process.env.PATH which breaks Windows;
change the assignment to use Node's platform-aware delimiter (e.g.,
path.delimiter) or the same conditional used in skill-helpers.ts so that
savedEnv.PATH and the new process.env.PATH assignment use path.delimiter (or
process.platform === "win32" ? ";" : ":") when building path.join(tmp.path,
"path-bin") + delimiter + (process.env.PATH ?? "").

Comment on lines +17 to +23
if (!result.success && toolInfo.formatValidationError) {
const msg = toolInfo.formatValidationError(result.error)
expect(msg).toContain("Invalid parameters for tool 'batch'")
expect(msg).toContain("Provide at least one tool call")
// Should include the expected payload format hint
expect(msg).toContain("Expected payload format")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fail fast instead of conditionally skipping formatter assertions.

These branches silently skip message assertions when formatValidationError is missing, so tests can pass without validating the formatter contract.

🔧 Suggested tightening
-    if (!result.success && toolInfo.formatValidationError) {
-      const msg = toolInfo.formatValidationError(result.error)
+    const formatValidationError = toolInfo.formatValidationError
+    expect(typeof formatValidationError).toBe("function")
+    if (!formatValidationError) {
+      throw new Error("BatchTool.formatValidationError must be defined")
+    }
+    if (!result.success) {
+      const msg = formatValidationError(result.error)
       expect(msg).toContain("Invalid parameters for tool 'batch'")
       expect(msg).toContain("Provide at least one tool call")
       // Should include the expected payload format hint
       expect(msg).toContain("Expected payload format")
     }

Also applies to: 35-40, 49-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/tool/batch.test.ts` around lines 17 - 23, The test
currently conditionally skips formatter assertions when
toolInfo.formatValidationError is missing; instead, assert that the formatter
exists before using it so the test fails fast. Update the test around the result
handling (the block using result, result.success, and
toolInfo.formatValidationError) to assert toolInfo.formatValidationError is
defined (or throw) when result.success is false, then call
toolInfo.formatValidationError(result.error) and perform the existing
expect(...).toContain checks for "Invalid parameters for tool 'batch'", "Provide
at least one tool call", and "Expected payload format".

Comment on lines +56 to +80
describe("BatchTool: DISALLOWED guard", () => {
test("batch tool cannot be called recursively — error message is clear", async () => {
// The DISALLOWED set and the error message are defined in the execute path.
// We can verify by importing and inspecting the tool's behavior:
// The execute function checks `if (DISALLOWED.has(call.tool))` and throws
// with a message mentioning "not allowed in batch".
//
// Since execute requires Session.updatePart (heavy dependency), we verify
// the public contract by importing the source and checking the constants directly
// plus verifying the error string template.

// Import the DISALLOWED set via the module
const batchModule = await import("../../src/tool/batch")

// The module exports BatchTool but DISALLOWED is module-scoped.
// We can at minimum verify the tool registers with id "batch"
expect(batchModule.BatchTool.id).toBe("batch")

// Verify that the init function returns a valid tool shape
const toolInfo = await batchModule.BatchTool.init()
expect(toolInfo.description).toBeTruthy()
expect(toolInfo.parameters).toBeDefined()
expect(typeof toolInfo.execute).toBe("function")
expect(typeof toolInfo.formatValidationError).toBe("function")
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

DISALLOWED guard test never exercises the guard path.

The test currently validates tool metadata only (id, shape) and would still pass if the recursive-call restriction were removed. Please either rename this test to match what it checks, or add an assertion that execute rejects a recursive batch call with the expected message.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/tool/batch.test.ts` around lines 56 - 80, The test
currently only checks metadata but must also assert the DISALLOWED guard; update
the test that references BatchTool to call the execute function and verify it
rejects when given a call with call.tool === "batch" and that the thrown/error
message includes the phrase "not allowed in batch"; if execute requires
Session.updatePart, supply a minimal stub/mock session object or otherwise stub
that dependency so execute can run and throw the DISALLOWED error—target
symbols: BatchTool, BatchTool.init, and the execute function exported from
"../../src/tool/batch".

@anandgupta42
Copy link
Contributor Author

Superseded by #439 which consolidates all 12 test PRs into one, deduplicates overlapping tests, and fixes bugs found during review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants