Skip to content

test: patch seekSequence matching and Command.hints#435

Closed
anandgupta42 wants to merge 1 commit intomainfrom
test/hourly-20260324-0511
Closed

test: patch seekSequence matching and Command.hints#435
anandgupta42 wants to merge 1 commit intomainfrom
test/hourly-20260324-0511

Conversation

@anandgupta42
Copy link
Contributor

@anandgupta42 anandgupta42 commented Mar 24, 2026

What does this PR do?

Adds 26 new tests across 2 previously untested code paths, discovered via automated test-discovery.

1. Patch.deriveNewContentsFromChunks + seekSequencesrc/patch/index.ts (14 new tests)

This is the core function that applies LLM-generated patch chunks to files. seekSequence uses a 4-pass matching strategy (exact → trailing-whitespace-trimmed → both-end-trimmed → Unicode-normalized) to find old lines in the file before replacing them. Zero direct tests existed for deriveNewContentsFromChunks, seekSequence, normalizeUnicode, or stripHeredoc. New coverage includes:

  • Exact match: basic old_lines → new_lines replacement with unified diff output
  • Trailing whitespace tolerance (rstrip pass): file has trailing spaces, patch doesn't — still matches
  • Leading whitespace tolerance (trim pass): indentation mismatch between patch and file
  • Unicode smart quote normalization: file uses \u201C/\u201D curly quotes, patch uses ASCII " — still matches
  • Unicode em-dash normalization: file uses \u2014, patch uses -
  • is_end_of_file EOF anchoring: verifies the last occurrence is targeted, not the first
  • change_context seeking: disambiguates duplicate lines by seeking to a context line first
  • Error propagation: missing old_lines, nonexistent file
  • Multiple sequential chunks: two replacements applied in order
  • Pure addition: empty old_lines appends content
  • Heredoc stripping: cat <<'EOF' and <<HEREDOC wrappers are stripped before parsing

Why it matters: When an LLM generates a patch with minor whitespace or Unicode differences from the actual file, users see "Failed to find expected lines" errors. These tests encode the exact matching behavior that prevents those failures.

2. Command.hints()src/command/index.ts (12 new tests)

This pure function extracts argument placeholders ($1, $2, $ARGUMENTS) from command templates. These hints drive the TUI's argument prompt display when users invoke slash commands like /review, /init, or custom commands. Zero tests existed. New coverage includes:

  • No placeholders → empty array
  • Single and multiple numbered placeholders in sorted order
  • Deduplication of repeated placeholders
  • $ARGUMENTS extraction (always appended after numbered)
  • Multi-digit placeholders ($10) with lexicographic sort behavior documented
  • Edge cases: empty template, whitespace-only, $0, non-matching $FOO/$BAR, case sensitivity

Why it matters: If hints() breaks, the TUI shows wrong or missing argument suggestions for slash commands, confusing users about what arguments a command expects.

Type of change

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

Issue for this PR

N/A — proactive test coverage via automated test-discovery

How did you verify your code works?

bun test test/patch/seek-sequence.test.ts   # 14 pass
bun test test/command/hints.test.ts         # 12 pass

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_01Nvdry9ChLovXGjHNS8kR7R

Summary by CodeRabbit

  • Tests
    • Added comprehensive test coverage for command hints extraction, validating placeholder detection, deduplication, and edge cases.
    • Added extensive test coverage for patch application and parsing, including multiple matching strategies, error handling, and heredoc support.

…ction

Cover two previously untested code paths: (1) Patch.deriveNewContentsFromChunks
with seekSequence's 4-pass matching (exact, rstrip, trim, Unicode-normalized)
and EOF anchoring/change_context seeking; (2) Command.hints() argument placeholder
extraction that drives TUI argument prompts for slash commands.

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

https://claude.ai/code/session_01Nvdry9ChLovXGjHNS8kR7R
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 24, 2026

📝 Walkthrough

Walkthrough

Two new test files were added to the opencode package. The first tests the Command.hints() function for placeholder extraction and deduplication across various template formats. The second tests patch application behavior across multiple matching strategies and patch parsing from heredocs.

Changes

Cohort / File(s) Summary
Test Suites
packages/opencode/test/command/hints.test.ts, packages/opencode/test/patch/seek-sequence.test.ts
Added comprehensive test coverage for Command.hints() placeholder extraction (empty strings, numbered placeholders, $ARGUMENTS, deduplication, lexicographic sorting) and Patch operations (exact/whitespace/trim/Unicode-aware matching, end-of-file targeting, context seeking, pure additions, heredoc-wrapped parsing).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

contributor

Poem

🐰 A rabbit hops through tests so fine,
With hints extracted, patches align,
From placeholders parsed with care,
To matching logic beyond compare,
This test suite ensures all works fair! 🌟

🚥 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 patch seekSequence matching and Command.hints functionality.
Description check ✅ Passed The description is comprehensive and well-structured, covering what changed, why it matters, test verification, and a detailed checklist. However, it does not follow the template structure exactly.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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-20260324-0511

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: 1

🤖 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/patch/seek-sequence.test.ts`:
- Around line 1-32: The test currently manually creates and removes a temp
directory using beforeEach/afterEach and a tempDir variable; replace that
pattern by using the tmpdir fixture with await using for automatic cleanup:
remove the beforeEach/afterEach blocks and tempDir variable, and instead declare
a scoped resource via "await using tmp = await tmpdir()" inside the
describe/test scope (or at the top of each test) so tests use tmp.path (or
similar) for file paths; update any references to tempDir to use the tmp fixture
and ensure the tests in this file follow the same await-using tmpdir() pattern
as required by the repo conventions.
🪄 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: 9f93c373-b170-4dce-9e8d-1f951a60bdba

📥 Commits

Reviewing files that changed from the base of the PR and between 544903f and 98d33f5.

📒 Files selected for processing (2)
  • packages/opencode/test/command/hints.test.ts
  • packages/opencode/test/patch/seek-sequence.test.ts

Comment on lines +1 to +32
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
import { Patch } from "../../src/patch"
import * as fs from "fs/promises"
import * as path from "path"
import { tmpdir } from "os"

/**
* Tests for Patch.deriveNewContentsFromChunks — the core function that applies
* update chunks to file content using seekSequence's multi-pass matching.
*
* seekSequence tries 4 comparison strategies in order:
* 1. Exact match
* 2. Trailing whitespace trimmed (rstrip)
* 3. Both-end whitespace trimmed (trim)
* 4. Unicode-normalized + trimmed
*
* These tests verify that real-world patch application succeeds even when the
* LLM-generated patch text has minor whitespace or Unicode differences from
* the actual file content — a common source of "Failed to find expected lines"
* errors for users.
*/

describe("Patch.deriveNewContentsFromChunks — seekSequence matching", () => {
let tempDir: string

beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(tmpdir(), "seek-test-"))
})

afterEach(async () => {
await fs.rm(tempDir, { recursive: true, force: true })
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use fixture tmpdir() with await using instead of manual mkdtemp/rm.

The current before/after lifecycle bypasses the required test fixture pattern and manual cleanup can drift from repo conventions.

🔧 Suggested refactor pattern
 import { describe, test, expect, beforeEach, afterEach } from "bun:test"
 import { Patch } from "../../src/patch"
 import * as fs from "fs/promises"
 import * as path from "path"
-import { tmpdir } from "os"
+import { tmpdir } from "../fixture/fixture"

 describe("Patch.deriveNewContentsFromChunks — seekSequence matching", () => {
-  let tempDir: string
-
-  beforeEach(async () => {
-    tempDir = await fs.mkdtemp(path.join(tmpdir(), "seek-test-"))
-  })
-
-  afterEach(async () => {
-    await fs.rm(tempDir, { recursive: true, force: true })
-  })
-
-  test("exact match: replaces old_lines with new_lines", () => {
-    const filePath = path.join(tempDir, "exact.txt")
+  test("exact match: replaces old_lines with new_lines", async () => {
+    await using tmp = await tmpdir()
+    const filePath = path.join(tmp.path, "exact.txt")
     const content = "line1\nline2\nline3\n"
-    require("fs").writeFileSync(filePath, content)
+    await fs.writeFile(filePath, content)

Apply the same await using tmp = await tmpdir() pattern to the other tests in this file.

As per coding guidelines, "Use the tmpdir function from fixture/fixture.ts to create temporary directories for tests with automatic cleanup in test files" and "Always use await using syntax with tmpdir() for automatic cleanup when the variable goes out of scope".

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

In `@packages/opencode/test/patch/seek-sequence.test.ts` around lines 1 - 32, The
test currently manually creates and removes a temp directory using
beforeEach/afterEach and a tempDir variable; replace that pattern by using the
tmpdir fixture with await using for automatic cleanup: remove the
beforeEach/afterEach blocks and tempDir variable, and instead declare a scoped
resource via "await using tmp = await tmpdir()" inside the describe/test scope
(or at the top of each test) so tests use tmp.path (or similar) for file paths;
update any references to tempDir to use the tmp fixture and ensure the tests in
this file follow the same await-using tmpdir() pattern as required by the repo
conventions.

@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