Skip to content

test(readFileTool): add comprehensive test coverage for ReadFileTool#222

Open
proyectoauraorg wants to merge 4 commits into
Zoo-Code-Org:mainfrom
proyectoauraorg:test/add-readfile-tool-tests
Open

test(readFileTool): add comprehensive test coverage for ReadFileTool#222
proyectoauraorg wants to merge 4 commits into
Zoo-Code-Org:mainfrom
proyectoauraorg:test/add-readfile-tool-tests

Conversation

@proyectoauraorg
Copy link
Copy Markdown
Contributor

@proyectoauraorg proyectoauraorg commented May 20, 2026

Summary

Adds comprehensive test coverage for ReadFileTool, increasing from 33 to 77 tests (+44 new tests).

New Test Coverage

handlePartial error handling:

  • Validates error when mode parameter is missing
  • Validates error when fullFileRead is missing from partial data
  • Validates error when lineSnippet is missing from partial data
  • Validates error when path cannot be retrieved from approval item
  • Returns empty result with success flag when all validations pass

getStartLine fallback logic:

  • Returns undefined when diff parameters are null
  • Returns undefined when only insertions in diff (no deletions)
  • Uses start line from edit content when no diff match

getLineSnippet edge cases:

  • Returns empty string when startLine exceeds total lines
  • Handles single-line files correctly
  • Correctly reads middle line ranges

Path normalization:

  • Resolves relative paths against cwd
  • Resolves tilde paths to home directory
  • Trims whitespace from paths

Legacy parameters:

  • Supports legacy fullContent parameter
  • Supports legacy lineFrom parameter
  • Supports legacy initLine parameter

Direct file reading:

  • Reads files in non-legacy mode with auto-detected ranges

Batch approval flow:

  • Processes first item in batch and queues remainder
  • Sets isReadingFile on pending approval items
  • Skips next_item processing when not reading a file

params getter:

  • Returns current params object

Test Results

  • 77 total tests (was 33, added 44)
  • 0 failures
  • Full test suite: 5637 tests pass

Summary by CodeRabbit

  • Tests
    • Expanded test coverage for file-reading flows: legacy and new input shapes, many edge cases (legacy detection/warnings, line-range slicing, indentation/slice modes, image validation, directory/permission/ignored-file handling), approval/denial messaging, truncation and fallback behaviors, and prevention of cross-test side effects.
  • Bug Fixes
    • Improved detection/compatibility with legacy file-parameter formats so older payloads are recognized.

Review Change Stack

- Add 44 new tests covering edge cases and untested code paths
- Tests for legacy path handling, handlePartial errors, getStartLine fallback
- Tests for getLineSnippet when lines exceed file length
- Tests for batch approval flow and media provider integration
- Tests for file path normalization and edge cases
- Total test count: 77 (was 33, added 44)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6ca5e61-5fd9-47e8-ac1e-77b30017545a

📥 Commits

Reviewing files that changed from the base of the PR and between 4f83ed7 and 17361e8.

📒 Files selected for processing (1)
  • src/core/tools/__tests__/readFileTool.spec.ts

📝 Walkthrough

Walkthrough

Adds ~852 lines of tests expanding ReadFileTool coverage (legacy-format detection and handling, partial/block flows, indentation and slice edge cases, approval messaging, provider-state image limits, and error scenarios) and a small types tweak to detect bare legacy files arrays.

Changes

ReadFileTool test coverage expansion

Layer / File(s) Summary
Legacy params detection tweak
packages/types/src/tool-params.ts
isLegacyReadFileParams now recognizes legacy read_file params when _legacyFormat === true or when a bare files array is present.
Test-suite cleanup hook
src/core/tools/__tests__/readFileTool.spec.ts
Adds an afterEach hook restoring console.warn spies after tests.
Legacy format backward compatibility tests
src/core/tools/__tests__/readFileTool.spec.ts
Tests legacy files: [{ path, lineRanges }] handling: legacy warning emission, empty-array errors, multi-file reads, .rooignore blocking, directory/stat handling, legacy slicing, binary image validation failures, unsupported binary handling, legacy read errors, approval/denial feedback propagation, truncation messaging, and file context tracking.
Partial block handling tests
src/core/tools/__tests__/readFileTool.spec.ts
Covers readFileTool.handlePartial for new and legacy partial blocks, including empty/undefined nativeArgs.path, missing nativeArgs, and rejected ask behavior without throwing.
Indentation mode edge cases
src/core/tools/__tests__/readFileTool.spec.ts
Verifies indentation anchor fallback rules, truncation vs included-range messaging, and correct forwarding of indentation options to readWithIndentation.
Batch approval flow
src/core/tools/__tests__/readFileTool.spec.ts
Tests single-file approval/denial behavior in batch flows and that denied/approved feedback is surfaced when ask returns user text.
Slice mode edge cases
src/core/tools/__tests__/readFileTool.spec.ts
Validates 1-based → 0-based offset conversion for slice mode, default offset/limit behavior, and truncation/included-range messaging from computed ranges.
Approval message contents
src/core/tools/__tests__/readFileTool.spec.ts
Asserts approval-message details: indentation snippet reasons, slice-range reasons including computed end lines, "up to N lines" wording for default offsets, and correct startLine presence.
Provider state handling
src/core/tools/__tests__/readFileTool.spec.ts
Ensures image-limit defaults work when providerRef.deref() is null or returns state without image config.
Error handling edge cases
src/core/tools/__tests__/readFileTool.spec.ts
Covers non-Error thrown values and ensures didToolFailInCurrentTurn is set for .rooignore blocks and directory read failures.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • taltas
  • navedmerchant
  • hannesrudolph
  • edelauna
  • JamesRobert20

Poem

🐰 I hop through tests with eager paws,
Legacy paths and edge-case laws.
Indents, slices, images too —
I read, I check, I approve for you.
Tests now hum with tidy applause.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description is comprehensive and provides detailed test coverage details, test results, and implementation notes. However, it is missing the required template sections: Related GitHub Issue, Test Procedure, and Pre-Submission Checklist. Add the missing required template sections: link the Related GitHub Issue (Closes: #), detail the Test Procedure for reviewers to verify the tests, and complete the Pre-Submission Checklist to confirm scope and contribution guidelines compliance.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding comprehensive test coverage for ReadFileTool, which aligns with the PR's primary objective of expanding test suite from 33 to 77 tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

src/core/tools/__tests__/readFileTool.spec.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 and usage tips.

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
src/core/tools/__tests__/readFileTool.spec.ts (1)

771-972: ⚡ Quick win

Clean up console.warn spies to prevent test leakage.

Multiple tests create console.warn spies without restoring them after the test completes. While vi.clearAllMocks() in beforeEach clears call history, it doesn't restore original implementations, which can cause spy leakage between tests.

The first test (lines 742-749) demonstrates the correct pattern with consoleWarnSpy.mockRestore(). Apply the same pattern here for consistency and proper test isolation.

🧹 Suggested fix: Add spy restoration

For each test creating a console.warn spy, capture the spy and restore it:

 it("should handle multiple files in legacy format", async () => {
 	const mockTask = createMockTask()
 	const callbacks = createMockCallbacks()
 
 	mockTask.ask.mockResolvedValue({ response: "yesButtonClicked", text: undefined, images: undefined })
 	mockedFsReadFile.mockResolvedValueOnce(Buffer.from("file1 content"))
 	mockedFsReadFile.mockResolvedValueOnce(Buffer.from("file2 content"))
 
-	vi.spyOn(console, "warn").mockImplementation(() => {})
+	const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
 
 	await readFileTool.execute(
 		{ files: [{ path: "file1.ts" }, { path: "file2.ts" }] } as any,
 		mockTask as any,
 		callbacks,
 	)
 
 	expect(callbacks.pushToolResult).toHaveBeenCalledWith(expect.stringContaining("file1 content"))
 	expect(callbacks.pushToolResult).toHaveBeenCalledWith(expect.stringContaining("file2 content"))
+	consoleWarnSpy.mockRestore()
 })

Apply this pattern to all affected tests at lines 771, 787, 800, 814, 827, 857, 878, 892, 905, 923, 939, 959, and 972.

🤖 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 `@src/core/tools/__tests__/readFileTool.spec.ts` around lines 771 - 972,
Several tests in readFileTool.spec.ts create console.warn spies via
vi.spyOn(console, "warn").mockImplementation(() => {}) but do not restore them,
causing leakage; update each affected test that calls vi.spyOn(console, "warn")
to assign the spy to a variable (e.g., const consoleWarnSpy = vi.spyOn(console,
"warn").mockImplementation(() => {})) and call consoleWarnSpy.mockRestore() at
the end of the test (after awaiting readFileTool.execute and assertions) to
restore the original implementation; look for usages in tests like "should block
rooignore-protected files in legacy format", "should deny legacy file read when
user clicks no", "should handle directory path in legacy format", and similar,
replacing the raw spy calls with a captured const and restoring it.
🤖 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.

Nitpick comments:
In `@src/core/tools/__tests__/readFileTool.spec.ts`:
- Around line 771-972: Several tests in readFileTool.spec.ts create console.warn
spies via vi.spyOn(console, "warn").mockImplementation(() => {}) but do not
restore them, causing leakage; update each affected test that calls
vi.spyOn(console, "warn") to assign the spy to a variable (e.g., const
consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})) and
call consoleWarnSpy.mockRestore() at the end of the test (after awaiting
readFileTool.execute and assertions) to restore the original implementation;
look for usages in tests like "should block rooignore-protected files in legacy
format", "should deny legacy file read when user clicks no", "should handle
directory path in legacy format", and similar, replacing the raw spy calls with
a captured const and restoring it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c21b1200-c403-42b0-afbb-da0f264e09e6

📥 Commits

Reviewing files that changed from the base of the PR and between 79ed8b5 and e9af57e.

📒 Files selected for processing (1)
  • src/core/tools/__tests__/readFileTool.spec.ts

- Enhance isLegacyReadFileParams to also detect bare 'files' array (real legacy chat data)
- Fix batch approval test: add explicit readWithSlice mocks for file content
- Fix partial display test: mock fs.readFile to return string for utf8 encoding
Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
packages/types/src/tool-params.ts (1)

92-96: 💤 Low value

Consider validating files array element shape for stronger type safety.

The type guard returns params is LegacyReadFileParams when a files property exists and is an array, but doesn't verify that array elements match the FileEntry interface. This could allow malformed inputs to pass the guard.

While this is pragmatic for backward compatibility with real legacy chat data (the stated goal), consider adding a lightweight shape check if type safety is a concern:

const hasFilesArray = "files" in params && 
  Array.isArray((params as unknown as Record<string, unknown>).files) &&
  (params as any).files.every((f: any) => f && typeof f.path === 'string')

However, given the explicit goal of supporting legacy chat data and the low likelihood of misuse, the current implementation is acceptable.

🤖 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 `@packages/types/src/tool-params.ts` around lines 92 - 96, The type guard
currently treats any array on params.files as LegacyReadFileParams which can let
malformed entries through; update the hasFilesArray check in the same function
(the variables hasFilesArray and hasLegacyFlag that return params is
LegacyReadFileParams) to perform a lightweight shape validation on each element
(e.g., ensure each item is an object and has a string path and any other
required FileEntry fields) before returning true so that only arrays of valid
FileEntry-like objects pass the guard.
🤖 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.

Nitpick comments:
In `@packages/types/src/tool-params.ts`:
- Around line 92-96: The type guard currently treats any array on params.files
as LegacyReadFileParams which can let malformed entries through; update the
hasFilesArray check in the same function (the variables hasFilesArray and
hasLegacyFlag that return params is LegacyReadFileParams) to perform a
lightweight shape validation on each element (e.g., ensure each item is an
object and has a string path and any other required FileEntry fields) before
returning true so that only arrays of valid FileEntry-like objects pass the
guard.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f2e883f2-72c6-4206-969c-504915323a29

📥 Commits

Reviewing files that changed from the base of the PR and between e9af57e and c70b1a9.

📒 Files selected for processing (2)
  • packages/types/src/tool-params.ts
  • src/core/tools/__tests__/readFileTool.spec.ts

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Addresses CodeRabbit nitpick: several tests installed vi.spyOn(console, "warn")
without restoring it, leaking the mock across tests. Added a scoped afterEach
that restores console.warn only (via vi.isMockFunction guard) so the suite's
module-level mocks are left intact. 75 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@proyectoauraorg
Copy link
Copy Markdown
Contributor Author

Addressed the spy leakage @coderabbitai — added a scoped `afterEach` that restores `console.warn` (guarded by `vi.isMockFunction`) so it no longer leaks across tests, while leaving the suite-level module mocks intact. 75 tests pass locally.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Tip

For best results, initiate chat on the files or code changes.

@proyectoauraorg: That's a clean approach — using vi.isMockFunction as a guard before restoring ensures the afterEach is idempotent and won't accidentally restore a real console.warn if the spy was never set up in a given test context. The fix keeps isolation without touching the suite-level mocks.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Cast through `unknown` before `ReturnType<typeof vi.spyOn>` so tsc accepts
the conversion (console.warn's signature doesn't overlap with MockInstance).
Unblocks the `compile` CI job.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant