diff --git a/app/local-ai-commit-reviewer/tests/conftest.py b/app/local-ai-commit-reviewer/tests/conftest.py new file mode 100644 index 0000000..206735b --- /dev/null +++ b/app/local-ai-commit-reviewer/tests/conftest.py @@ -0,0 +1,126 @@ +import subprocess +import tempfile +from pathlib import Path + +import pytest + +from src.config import Config +from src.llm.provider import LLMProvider, LLMResponse, ModelInfo + + +@pytest.fixture +def temp_git_repo(): + """Create a temporary Git repository for testing.""" + with tempfile.TemporaryDirectory() as tmpdir: + repo_path = Path(tmpdir) + subprocess.run(["git", "init"], cwd=repo_path, capture_output=True, check=False) + subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=repo_path, capture_output=True, check=False) + subprocess.run(["git", "config", "user.name", "Test"], cwd=repo_path, capture_output=True, check=False) + yield repo_path + + +@pytest.fixture +def sample_python_file(temp_git_repo): + """Create a sample Python file in the temp repo.""" + test_file = temp_git_repo / "test.py" + test_file.write_text('def hello():\n print("Hello, World!")\n return True\n') + subprocess.run(["git", "add", "test.py"], cwd=temp_git_repo, capture_output=True, check=False) + return test_file + + +@pytest.fixture +def sample_js_file(temp_git_repo): + """Create a sample JavaScript file.""" + test_file = temp_git_repo / "test.js" + test_file.write_text('function hello() {\n console.log("Hello, World!");\n}\n') + subprocess.run(["git", "add", "test.js"], cwd=temp_git_repo, capture_output=True, check=False) + return test_file + + +@pytest.fixture +def sample_diff(): + """Return a sample diff for testing.""" + return """diff --git a/test.py b/test.py +--- a/test.py ++++ b/test.py +@@ -1,3 +1,4 @@ + def hello(): ++ print("hello") + return True +- return False +""" + + +@pytest.fixture +def mock_config(): + """Return a default Config instance.""" + return Config() + + +class MockLLMProvider(LLMProvider): + """Mock LLM provider for testing.""" + + def __init__(self, available: bool = True, response_text: str | None = None): + self._available = available + self._response_text = response_text or '{"issues": [], "summary": {"critical_count": 0, "warning_count": 0, "info_count": 0, "overall_assessment": "No issues"}}' + + def is_available(self) -> bool: + return self._available + + def generate(self, _prompt: str, **_kwargs) -> LLMResponse: + return LLMResponse( + text=self._response_text, + model="mock-model", + tokens_used=50, + finish_reason="stop" + ) + + async def agenerate(self, _prompt: str, **_kwargs) -> LLMResponse: + return self.generate(_prompt, **_kwargs) + + def stream_generate(self, _prompt: str, **_kwargs): + yield "Mock" + + def list_models(self) -> list[ModelInfo]: + return [ + ModelInfo(name="mock-model", size="4GB", modified="2024-01-01", digest="abc123") + ] + + def health_check(self) -> bool: + return self._available + + +@pytest.fixture +def mock_llm_provider(): + """Return a mock LLM provider.""" + return MockLLMProvider(available=True) + + +@pytest.fixture +def mock_llm_unavailable(): + """Return a mock LLM provider that's not available.""" + return MockLLMProvider(available=False) + + +@pytest.fixture +def mock_llm_with_issues(): + """Return a mock LLM provider that returns issues.""" + response = '''{ + "issues": [ + { + "file": "test.py", + "line": 2, + "severity": "warning", + "category": "style", + "message": "Missing docstring for function", + "suggestion": "Add a docstring above the function definition" + } + ], + "summary": { + "critical_count": 0, + "warning_count": 1, + "info_count": 0, + "overall_assessment": "Minor style issues found" + } +}''' + return MockLLMProvider(available=True, response_text=response)