Docs added to show how a new provider is added
Docs added to show how a new tool is created All tools should add numbers to code for models to be able to reference if needed Enabled line numbering for code for all tools to use Additional tests to validate line numbering is not added to git diffs
This commit is contained in:
49
tests/test_line_numbers_integration.py
Normal file
49
tests/test_line_numbers_integration.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Integration test demonstrating that all tools get line numbers by default.
|
||||
"""
|
||||
|
||||
from tools.analyze import AnalyzeTool
|
||||
from tools.chat import ChatTool
|
||||
from tools.codereview import CodeReviewTool
|
||||
from tools.debug import DebugIssueTool
|
||||
from tools.precommit import Precommit
|
||||
from tools.refactor import RefactorTool
|
||||
from tools.testgen import TestGenTool
|
||||
|
||||
|
||||
class TestLineNumbersIntegration:
|
||||
"""Test that all tools inherit line number behavior correctly."""
|
||||
|
||||
def test_all_tools_want_line_numbers(self):
|
||||
"""Verify that all tools want line numbers by default."""
|
||||
tools = [
|
||||
ChatTool(),
|
||||
AnalyzeTool(),
|
||||
CodeReviewTool(),
|
||||
DebugIssueTool(),
|
||||
RefactorTool(),
|
||||
TestGenTool(),
|
||||
Precommit(),
|
||||
]
|
||||
|
||||
for tool in tools:
|
||||
assert tool.wants_line_numbers_by_default(), f"{tool.get_name()} should want line numbers by default"
|
||||
|
||||
def test_no_tools_override_line_numbers(self):
|
||||
"""Verify that no tools override the base class line number behavior."""
|
||||
# Check that tools don't have their own wants_line_numbers_by_default method
|
||||
tools_classes = [
|
||||
ChatTool,
|
||||
AnalyzeTool,
|
||||
CodeReviewTool,
|
||||
DebugIssueTool,
|
||||
RefactorTool,
|
||||
TestGenTool,
|
||||
Precommit,
|
||||
]
|
||||
|
||||
for tool_class in tools_classes:
|
||||
# Check if the method is defined in the tool class itself
|
||||
# (not inherited from base)
|
||||
has_override = "wants_line_numbers_by_default" in tool_class.__dict__
|
||||
assert not has_override, f"{tool_class.__name__} should not override wants_line_numbers_by_default"
|
||||
163
tests/test_precommit_diff_formatting.py
Normal file
163
tests/test_precommit_diff_formatting.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
Test to verify that precommit tool formats diffs correctly without line numbers.
|
||||
This test focuses on the diff formatting logic rather than full integration.
|
||||
"""
|
||||
|
||||
from tools.precommit import Precommit
|
||||
|
||||
|
||||
class TestPrecommitDiffFormatting:
|
||||
"""Test that precommit correctly formats diffs without line numbers."""
|
||||
|
||||
def test_git_diff_formatting_has_no_line_numbers(self):
|
||||
"""Test that git diff output is preserved without line number additions."""
|
||||
# Sample git diff output
|
||||
git_diff = """diff --git a/example.py b/example.py
|
||||
index 1234567..abcdefg 100644
|
||||
--- a/example.py
|
||||
+++ b/example.py
|
||||
@@ -1,5 +1,8 @@
|
||||
def hello():
|
||||
- print("Hello, World!")
|
||||
+ print("Hello, Universe!") # Changed this line
|
||||
|
||||
def goodbye():
|
||||
print("Goodbye!")
|
||||
+
|
||||
+def new_function():
|
||||
+ print("This is new")
|
||||
"""
|
||||
|
||||
# Simulate how precommit formats a diff
|
||||
repo_name = "test_repo"
|
||||
file_path = "example.py"
|
||||
diff_header = f"\n--- BEGIN DIFF: {repo_name} / {file_path} (unstaged) ---\n"
|
||||
diff_footer = f"\n--- END DIFF: {repo_name} / {file_path} ---\n"
|
||||
formatted_diff = diff_header + git_diff + diff_footer
|
||||
|
||||
# Verify the diff doesn't contain line number markers (│)
|
||||
assert "│" not in formatted_diff, "Git diffs should NOT have line number markers"
|
||||
|
||||
# Verify the diff preserves git's own line markers
|
||||
assert "@@ -1,5 +1,8 @@" in formatted_diff
|
||||
assert '- print("Hello, World!")' in formatted_diff
|
||||
assert '+ print("Hello, Universe!")' in formatted_diff
|
||||
|
||||
def test_untracked_file_diff_formatting(self):
|
||||
"""Test that untracked files formatted as diffs don't have line numbers."""
|
||||
# Simulate untracked file content
|
||||
file_content = """def new_function():
|
||||
return "I am new"
|
||||
|
||||
class NewClass:
|
||||
pass
|
||||
"""
|
||||
|
||||
# Simulate how precommit formats untracked files as diffs
|
||||
repo_name = "test_repo"
|
||||
file_path = "new_file.py"
|
||||
|
||||
diff_header = f"\n--- BEGIN DIFF: {repo_name} / {file_path} (untracked - new file) ---\n"
|
||||
diff_content = f"+++ b/{file_path}\n"
|
||||
|
||||
# Add each line with + prefix (simulating new file diff)
|
||||
for _line_num, line in enumerate(file_content.splitlines(), 1):
|
||||
diff_content += f"+{line}\n"
|
||||
|
||||
diff_footer = f"\n--- END DIFF: {repo_name} / {file_path} ---\n"
|
||||
formatted_diff = diff_header + diff_content + diff_footer
|
||||
|
||||
# Verify no line number markers
|
||||
assert "│" not in formatted_diff, "Untracked file diffs should NOT have line number markers"
|
||||
|
||||
# Verify diff format
|
||||
assert "+++ b/new_file.py" in formatted_diff
|
||||
assert "+def new_function():" in formatted_diff
|
||||
assert '+ return "I am new"' in formatted_diff
|
||||
|
||||
def test_compare_to_diff_formatting(self):
|
||||
"""Test that compare_to mode diffs don't have line numbers."""
|
||||
# Sample git diff for compare_to mode
|
||||
git_diff = """diff --git a/config.py b/config.py
|
||||
index abc123..def456 100644
|
||||
--- a/config.py
|
||||
+++ b/config.py
|
||||
@@ -10,7 +10,7 @@ class Config:
|
||||
def __init__(self):
|
||||
self.debug = False
|
||||
- self.timeout = 30
|
||||
+ self.timeout = 60 # Increased timeout
|
||||
self.retries = 3
|
||||
"""
|
||||
|
||||
# Format as compare_to diff
|
||||
repo_name = "test_repo"
|
||||
file_path = "config.py"
|
||||
compare_ref = "v1.0"
|
||||
|
||||
diff_header = f"\n--- BEGIN DIFF: {repo_name} / {file_path} (compare to {compare_ref}) ---\n"
|
||||
diff_footer = f"\n--- END DIFF: {repo_name} / {file_path} ---\n"
|
||||
formatted_diff = diff_header + git_diff + diff_footer
|
||||
|
||||
# Verify no line number markers
|
||||
assert "│" not in formatted_diff, "Compare-to diffs should NOT have line number markers"
|
||||
|
||||
# Verify diff markers
|
||||
assert "@@ -10,7 +10,7 @@ class Config:" in formatted_diff
|
||||
assert "- self.timeout = 30" in formatted_diff
|
||||
assert "+ self.timeout = 60 # Increased timeout" in formatted_diff
|
||||
|
||||
def test_base_tool_default_line_numbers(self):
|
||||
"""Test that the base tool wants line numbers by default."""
|
||||
tool = Precommit()
|
||||
assert tool.wants_line_numbers_by_default(), "Base tool should want line numbers by default"
|
||||
|
||||
def test_context_files_want_line_numbers(self):
|
||||
"""Test that precommit tool inherits base class behavior for line numbers."""
|
||||
tool = Precommit()
|
||||
|
||||
# The precommit tool should want line numbers by default (inherited from base)
|
||||
assert tool.wants_line_numbers_by_default()
|
||||
|
||||
# This means when it calls read_files for context files,
|
||||
# it will pass include_line_numbers=True
|
||||
|
||||
def test_diff_sections_in_prompt(self):
|
||||
"""Test the structure of diff sections in the final prompt."""
|
||||
# Create sample prompt sections
|
||||
diff_section = """
|
||||
## Git Diffs
|
||||
|
||||
--- BEGIN DIFF: repo / file.py (staged) ---
|
||||
diff --git a/file.py b/file.py
|
||||
index 123..456 100644
|
||||
--- a/file.py
|
||||
+++ b/file.py
|
||||
@@ -1,3 +1,4 @@
|
||||
def main():
|
||||
print("Hello")
|
||||
+ print("World")
|
||||
--- END DIFF: repo / file.py ---
|
||||
"""
|
||||
|
||||
context_section = """
|
||||
## Additional Context Files
|
||||
The following files are provided for additional context. They have NOT been modified.
|
||||
|
||||
--- BEGIN FILE: /path/to/context.py ---
|
||||
1│ # Context file
|
||||
2│ def helper():
|
||||
3│ pass
|
||||
--- END FILE: /path/to/context.py ---
|
||||
"""
|
||||
|
||||
# Verify diff section has no line numbers
|
||||
assert "│" not in diff_section, "Diff section should not have line number markers"
|
||||
|
||||
# Verify context section has line numbers
|
||||
assert "│" in context_section, "Context section should have line number markers"
|
||||
|
||||
# Verify the sections are clearly separated
|
||||
assert "## Git Diffs" in diff_section
|
||||
assert "## Additional Context Files" in context_section
|
||||
assert "have NOT been modified" in context_section
|
||||
165
tests/test_precommit_line_numbers.py
Normal file
165
tests/test_precommit_line_numbers.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""
|
||||
Test to verify that precommit tool handles line numbers correctly:
|
||||
- Diffs should NOT have line numbers (they have their own diff markers)
|
||||
- Additional context files SHOULD have line numbers
|
||||
"""
|
||||
|
||||
import os
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from tools.precommit import Precommit, PrecommitRequest
|
||||
|
||||
|
||||
class TestPrecommitLineNumbers:
|
||||
"""Test that precommit correctly handles line numbers for diffs vs context files."""
|
||||
|
||||
@pytest.fixture
|
||||
def tool(self):
|
||||
"""Create a Precommit tool instance."""
|
||||
return Precommit()
|
||||
|
||||
@pytest.fixture
|
||||
def mock_provider(self):
|
||||
"""Create a mock provider."""
|
||||
provider = MagicMock()
|
||||
provider.get_provider_type.return_value.value = "test"
|
||||
|
||||
# Mock the model response
|
||||
model_response = MagicMock()
|
||||
model_response.content = "Test review response"
|
||||
model_response.usage = {"total_tokens": 100}
|
||||
model_response.metadata = {"finish_reason": "stop"}
|
||||
model_response.friendly_name = "test-model"
|
||||
|
||||
provider.generate_content = AsyncMock(return_value=model_response)
|
||||
provider.get_capabilities.return_value = MagicMock(
|
||||
context_window=200000,
|
||||
temperature_constraint=MagicMock(
|
||||
validate=lambda x: True, get_corrected_value=lambda x: x, get_description=lambda: "0.0 to 1.0"
|
||||
),
|
||||
)
|
||||
provider.supports_thinking_mode.return_value = False
|
||||
|
||||
return provider
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_diffs_have_no_line_numbers_but_context_files_do(self, tool, mock_provider, tmp_path):
|
||||
"""Test that git diffs don't have line numbers but context files do."""
|
||||
# Use the workspace root for test files
|
||||
import tempfile
|
||||
|
||||
test_workspace = tempfile.mkdtemp(prefix="test_precommit_")
|
||||
|
||||
# Create a context file in the workspace
|
||||
context_file = os.path.join(test_workspace, "context.py")
|
||||
with open(context_file, "w") as f:
|
||||
f.write(
|
||||
"""# This is a context file
|
||||
def context_function():
|
||||
return "This should have line numbers"
|
||||
"""
|
||||
)
|
||||
|
||||
# Mock git commands to return predictable output
|
||||
def mock_run_git_command(repo_path, command):
|
||||
if command == ["status", "--porcelain"]:
|
||||
return True, " M example.py"
|
||||
elif command == ["diff", "--name-only"]:
|
||||
return True, "example.py"
|
||||
elif command == ["diff", "--", "example.py"]:
|
||||
# Return a sample diff - this should NOT have line numbers added
|
||||
return (
|
||||
True,
|
||||
"""diff --git a/example.py b/example.py
|
||||
index 1234567..abcdefg 100644
|
||||
--- a/example.py
|
||||
+++ b/example.py
|
||||
@@ -1,5 +1,8 @@
|
||||
def hello():
|
||||
- print("Hello, World!")
|
||||
+ print("Hello, Universe!") # Changed this line
|
||||
|
||||
def goodbye():
|
||||
print("Goodbye!")
|
||||
+
|
||||
+def new_function():
|
||||
+ print("This is new")
|
||||
""",
|
||||
)
|
||||
else:
|
||||
return True, ""
|
||||
|
||||
# Create request with context file
|
||||
request = PrecommitRequest(
|
||||
path=test_workspace,
|
||||
prompt="Review my changes",
|
||||
files=[context_file], # This should get line numbers
|
||||
include_staged=False,
|
||||
include_unstaged=True,
|
||||
)
|
||||
|
||||
# Mock the tool's provider and git functions
|
||||
with (
|
||||
patch.object(tool, "get_model_provider", return_value=mock_provider),
|
||||
patch("tools.precommit.run_git_command", side_effect=mock_run_git_command),
|
||||
patch("tools.precommit.find_git_repositories", return_value=[test_workspace]),
|
||||
patch(
|
||||
"tools.precommit.get_git_status",
|
||||
return_value={
|
||||
"branch": "main",
|
||||
"ahead": 0,
|
||||
"behind": 0,
|
||||
"staged_files": [],
|
||||
"unstaged_files": ["example.py"],
|
||||
"untracked_files": [],
|
||||
},
|
||||
),
|
||||
):
|
||||
|
||||
# Prepare the prompt
|
||||
prompt = await tool.prepare_prompt(request)
|
||||
|
||||
# Print prompt sections for debugging if test fails
|
||||
# print("\n=== PROMPT OUTPUT ===")
|
||||
# print(prompt)
|
||||
# print("=== END PROMPT ===\n")
|
||||
|
||||
# Verify that diffs don't have line numbers
|
||||
assert "--- BEGIN DIFF:" in prompt
|
||||
assert "--- END DIFF:" in prompt
|
||||
|
||||
# Check that the diff content doesn't have line number markers (│)
|
||||
# Find diff section
|
||||
diff_start = prompt.find("--- BEGIN DIFF:")
|
||||
diff_end = prompt.find("--- END DIFF:", diff_start) + len("--- END DIFF:")
|
||||
if diff_start != -1 and diff_end > diff_start:
|
||||
diff_section = prompt[diff_start:diff_end]
|
||||
assert "│" not in diff_section, "Diff section should NOT have line number markers"
|
||||
|
||||
# Verify the diff has its own line markers
|
||||
assert "@@ -1,5 +1,8 @@" in diff_section
|
||||
assert '- print("Hello, World!")' in diff_section
|
||||
assert '+ print("Hello, Universe!") # Changed this line' in diff_section
|
||||
|
||||
# Verify that context files DO have line numbers
|
||||
if "--- BEGIN FILE:" in prompt:
|
||||
# Extract context file section
|
||||
file_start = prompt.find("--- BEGIN FILE:")
|
||||
file_end = prompt.find("--- END FILE:", file_start) + len("--- END FILE:")
|
||||
if file_start != -1 and file_end > file_start:
|
||||
context_section = prompt[file_start:file_end]
|
||||
|
||||
# Context files should have line number markers
|
||||
assert "│" in context_section, "Context file section SHOULD have line number markers"
|
||||
|
||||
# Verify specific line numbers in context file
|
||||
assert "1│ # This is a context file" in context_section
|
||||
assert "2│ def context_function():" in context_section
|
||||
assert '3│ return "This should have line numbers"' in context_section
|
||||
|
||||
def test_base_tool_wants_line_numbers_by_default(self, tool):
|
||||
"""Verify that the base tool configuration wants line numbers by default."""
|
||||
# The precommit tool should inherit the base behavior
|
||||
assert tool.wants_line_numbers_by_default(), "Base tool should want line numbers by default"
|
||||
Reference in New Issue
Block a user