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:
Fahad
2025-06-15 07:02:27 +04:00
parent b5004b91fc
commit 99fab3e83d
27 changed files with 2511 additions and 143 deletions

View 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"

View 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

View 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"