Skip to content

test(lib): add comprehensive unit tests for resolve-subagent-path (#328) - #628

Open
AVPthegreat wants to merge 1 commit into
FailproofAI:mainfrom
AVPthegreat:test/resolve-subagent-path-unit-tests
Open

test(lib): add comprehensive unit tests for resolve-subagent-path (#328)#628
AVPthegreat wants to merge 1 commit into
FailproofAI:mainfrom
AVPthegreat:test/resolve-subagent-path-unit-tests

Conversation

@AVPthegreat

@AVPthegreat AVPthegreat commented Jul 30, 2026

Copy link
Copy Markdown

Summary

Fixes #328 by adding comprehensive unit tests for lib/resolve-subagent-path.ts in __tests__/lib/resolve-subagent-path.test.ts, covering candidate fallback order, path traversal guards (projectName, sessionId, agentId), and non-ENOENT error handling (e.g. EACCES).


🧪 Test Coverage Added

Added test scenarios in __tests__/lib/resolve-subagent-path.test.ts:

  1. Candidate Priority Order:
    • Project-level log: {projectsPath}/{projectName}/agent-{agentId}.jsonl
    • Session-level log: {projectsPath}/{projectName}/{sessionId}/agent-{agentId}.jsonl
    • Subagents-dir log: {projectsPath}/{projectName}/{sessionId}/subagents/agent-{agentId}.jsonl
  2. Path Traversal Security Guards:
    • Verifies path traversal attempts (../../outside or ../../../../escape) in projectName, sessionId, or agentId are rejected and return null.
  3. Error Handling:
    • Verifies unexpected access errors (e.g. EACCES) break the search loop and return null.

🏁 Verification Results

  • Ran vitest run __tests__/lib/resolve-subagent-path.test.ts
  • Result: ✓ 8 passed (8)

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of unexpected filesystem access errors during subagent path resolution.
    • Prevented project path traversal through crafted project names.
  • Tests

    • Added regression coverage for filesystem failures and path traversal scenarios.

Copilot AI review requested due to automatic review settings July 30, 2026 14:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Your PR is awaiting review by a reviewer. Till then you can join the Discord for conversation: https://discord.befailproof.ai

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The test suite now simulates filesystem access errors, resets mock state between tests, and verifies project-name path traversal is rejected while unexpected access errors return null without continuing candidate searches.

Changes

Resolver test coverage

Layer / File(s) Summary
Filesystem error and traversal tests
__tests__/lib/resolve-subagent-path.test.ts
Mocks fs/promises.access, isolates mock errors across tests, and adds coverage for project-name traversal prevention and EACCES handling.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: copilot

Poem

I’m a bunny testing paths in the night,
Blocking escape routes left and right.
When access errors start to appear,
We stop the search and keep things clear.
Hop, hop—safe resolution is here!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately describes the main change: adding unit tests for resolve-subagent-path.
Description check ✅ Passed The PR describes the change, scope, and test verification clearly, though it does not use the template headings exactly.
Linked Issues check ✅ Passed The added tests cover candidate priority, null on misses, traversal rejection, and non-ENOENT handling as requested.
Out of Scope Changes check ✅ Passed All changes are confined to tests and match the linked testing objectives; no unrelated implementation changes were added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

__tests__/lib/resolve-subagent-path.test.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@__tests__/lib/resolve-subagent-path.test.ts`:
- Around line 95-101: Update the traversalProject fixture in the
resolveSubagentPath traversal test to use ../outside or another path that
remains beneath the per-test fixtureRoot while still escaping projectsPath. Keep
the escaped-candidate setup and expected null result unchanged.
- Around line 33-40: Update the test setup in beforeEach to clear the access
mock’s call history, then strengthen the EACCES test assertions to verify access
is called exactly once with the project-level candidate path, ensuring candidate
search stops on unexpected errors. Apply the same assertion update to the
related test around the second referenced range.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 29b4e022-ac71-4767-a30c-c26d61136f26

📥 Commits

Reviewing files that changed from the base of the PR and between debb1fd and b533903.

📒 Files selected for processing (1)
  • __tests__/lib/resolve-subagent-path.test.ts

Comment on lines +33 to +40
mockAccessError = null;
fixtureRoot = await mkdtemp(join(tmpdir(), "failproofai-subagents-"));
projectsPath = join(fixtureRoot, "projects");
await mkdir(join(projectsPath, projectName, sessionId, "subagents"), { recursive: true });
});

afterEach(async () => {
mockAccessError = null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file and related symbols =="
git ls-files | rg '(^|/)resolve-subagent-path\.(ts|js)$|resolve-subagent-path.test.ts' || true

echo
echo "== excerpt test file around mentioned lines =="
if [ -f "__tests__/lib/resolve-subagent-path.test.ts" ]; then
  wc -l "__tests__/lib/resolve-subagent-path.test.ts"
  sed -n '1,150p' "__tests__/lib/resolve-subagent-path.test.ts" | nl -ba -v1
fi

echo
echo "== find source file(s) =="
fd -a 'resolve-subagent-path' . | sed 's#^\./##'

Repository: FailproofAI/failproofai

Length of output: 409


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== test file =="
cat -n "__tests__/lib/resolve-subagent-path.test.ts"

echo
echo "== source file =="
cat -n "lib/resolve-subagent-path.ts"

Repository: FailproofAI/failproofai

Length of output: 6744


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package vitest availability =="
if [ -f package.json ]; then
  cat package.json | sed -n '1,160p'
fi

echo
echo "== deterministic call-history behavior for the mocked fs/promises access =="
node - <<'JS'
const calls = [];
const original = async (path) => {
  if (path.endsWith("candidate2.jsonl")) throw Object.assign(new Error("EACCES"), { code: "EACCES" });
};
async function resolveSubagentPath() {
  const candidatePaths = ["candidate1.jsonl", "candidate2.jsonl", "candidate3.jsonl"];
  for (const candidatePath of candidatePaths) {
    try {
      await original(candidatePath);
    } catch (e) {
      if ((e).code === "ENOENT") continue;
      break;
    }
  }
  return null;
}

(async () => {
  const result = await resolveSubagentPath();
  console.log(JSON.stringify({ result, totalCalls: calls.length }));
})();
JS

echo
echo "== test assertions for access.calls/callCount =="
rg -n "access\\.calls|\.calls\\.length|callCount|mocked\(" "__tests__" "lib" || true

Repository: FailproofAI/failproofai

Length of output: 48531


Assert that unexpected errors stop candidate search.

The EACCES expectation only checks result === null; it would still pass if the loop checked the remaining candidates. Clear the mock call history in beforeEach, then assert that access was called exactly once with the project-level candidate in the error case.

Also applies to: 103-110

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@__tests__/lib/resolve-subagent-path.test.ts` around lines 33 - 40, Update the
test setup in beforeEach to clear the access mock’s call history, then
strengthen the EACCES test assertions to verify access is called exactly once
with the project-level candidate path, ensuring candidate search stops on
unexpected errors. Apply the same assertion update to the related test around
the second referenced range.

Comment on lines +95 to +101
it("prevents path traversal when projectName attempts to escape projectsPath", async () => {
const traversalProject = "../../outside";
const escapedCandidate = join(projectsPath, traversalProject, `agent-${agentId}.jsonl`);
await touch(escapedCandidate);

await expect(resolveSubagentPath(projectsPath, traversalProject, sessionId, agentId)).resolves.toBeNull();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Keep traversal fixtures inside the per-test temporary directory.

../../outside escapes above fixtureRoot, so touch() creates an artifact that the test cleanup will not remove. Use ../outside (or another path beneath fixtureRoot) while remaining outside projectsPath.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@__tests__/lib/resolve-subagent-path.test.ts` around lines 95 - 101, Update
the traversalProject fixture in the resolveSubagentPath traversal test to use
../outside or another path that remains beneath the per-test fixtureRoot while
still escaping projectsPath. Keep the escaped-candidate setup and expected null
result unchanged.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add unit tests for lib/resolve-subagent-path.ts

3 participants