Files
ss-tools/backend/tests/plugins/test_git_llm_extension.py
root 632b730fff chore: migrate GRACE-Poly anchors to hierarchical dotted naming
Systematic rename of all semantic anchors (#region, [DEF], @RELATION)
across 1400+ files — backend Python, frontend Svelte/TS, specs, docs:
- Flat anchors become Namespace.Module.Entity
- @RELATION references updated to match new anchor paths
- Zero business logic changes
2026-07-22 11:48:15 +03:00

159 lines
6.2 KiB
Python

# #region Test.GitLLMExtension [C:3] [TYPE Module] [SEMANTICS test, git, llm, commit, message, generation]
# @BRIEF Tests for git/llm_extension.py — GitLLMExtension.suggest_commit_message.
# @RELATION BINDS_TO -> [Plugin.LlmExtension.GitLLMExtensionModule]
# @TEST_CONTRACT: suggest_commit_message -> str | LLM-based commit message
# @TEST_EDGE: empty_response -> fallback message
# @TEST_EDGE: missing_choices -> fallback message
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
import os
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from src.plugins.git.llm_extension import GitLLMExtension
class TestGitLLMExtension:
"""GitLLMExtension — commit message generation via LLM."""
def setup_method(self):
self.mock_client = MagicMock()
self.mock_client.default_model = "gpt-4o-mini"
self.extension = GitLLMExtension(self.mock_client)
def test_init_stores_client(self):
"""Client stored on init."""
assert self.extension.client is self.mock_client
@pytest.mark.asyncio
async def test_suggest_commit_message_success(self):
"""Successful LLM response returns stripped content."""
self.mock_client.client.chat.completions.create = AsyncMock()
mock_choice = MagicMock()
mock_choice.message.content = " feat: add new feature "
mock_response = MagicMock()
mock_response.choices = [mock_choice]
self.mock_client.client.chat.completions.create.return_value = mock_response
result = await self.extension.suggest_commit_message(
diff="+ new feature",
history=["feat: old feature"],
)
assert result == "feat: add new feature"
@pytest.mark.asyncio
async def test_suggest_commit_message_empty_choices(self):
"""Response with no choices returns fallback message."""
self.mock_client.client.chat.completions.create = AsyncMock()
mock_response = MagicMock()
mock_response.choices = []
mock_response.error = None
self.mock_client.client.chat.completions.create.return_value = mock_response
result = await self.extension.suggest_commit_message(
diff="+ new feature",
history=[],
)
assert result == "Update dashboard configurations (LLM generation failed)"
@pytest.mark.asyncio
async def test_suggest_commit_message_no_response(self):
"""None response returns fallback message."""
self.mock_client.client.chat.completions.create = AsyncMock()
self.mock_client.client.chat.completions.create.return_value = None
result = await self.extension.suggest_commit_message(
diff="+ new feature",
history=[],
)
assert result == "Update dashboard configurations (LLM generation failed)"
@pytest.mark.asyncio
async def test_suggest_commit_message_empty_history(self):
"""Empty history list is joined to empty string."""
self.mock_client.client.chat.completions.create = AsyncMock()
mock_choice = MagicMock()
mock_choice.message.content = "Initial commit"
mock_response = MagicMock()
mock_response.choices = [mock_choice]
self.mock_client.client.chat.completions.create.return_value = mock_response
result = await self.extension.suggest_commit_message(
diff="+ initial",
history=[],
)
assert result == "Initial commit"
@pytest.mark.asyncio
async def test_suggest_commit_message_with_history(self):
"""Multiple history entries joined by newline."""
self.mock_client.client.chat.completions.create = AsyncMock()
mock_choice = MagicMock()
mock_choice.message.content = "fix: bug"
mock_response = MagicMock()
mock_response.choices = [mock_choice]
self.mock_client.client.chat.completions.create.return_value = mock_response
result = await self.extension.suggest_commit_message(
diff="- bug line",
history=["feat: a", "fix: b"],
)
assert result == "fix: bug"
@pytest.mark.asyncio
async def test_suggest_commit_message_custom_prompt(self):
"""Custom prompt template is rendered and passed to LLM."""
self.mock_client.client.chat.completions.create = AsyncMock()
mock_choice = MagicMock()
mock_choice.message.content = "custom message"
mock_response = MagicMock()
mock_response.choices = [mock_choice]
self.mock_client.client.chat.completions.create.return_value = mock_response
with patch("src.plugins.git.llm_extension.render_prompt") as mock_render:
mock_render.return_value = "Custom template output"
await self.extension.suggest_commit_message(
diff="+ change",
history=["prev"],
prompt_template="Custom template with {history} and {diff}",
)
mock_render.assert_called_once()
call_args = mock_render.call_args[0]
assert "Custom template" in call_args[0]
@pytest.mark.asyncio
async def test_suggest_commit_message_model_and_temp(self):
"""Correct model and temperature passed to LLM."""
self.mock_client.client.chat.completions.create = AsyncMock()
mock_choice = MagicMock()
mock_choice.message.content = "msg"
mock_response = MagicMock()
mock_response.choices = [mock_choice]
self.mock_client.client.chat.completions.create.return_value = mock_response
await self.extension.suggest_commit_message(
diff="+ change",
history=["prev"],
)
call_kwargs = self.mock_client.client.chat.completions.create.call_args[1]
assert call_kwargs["model"] == "gpt-4o-mini"
assert call_kwargs["temperature"] == 0.7
assert len(call_kwargs["messages"]) == 1
assert call_kwargs["messages"][0]["role"] == "user"
# #endregion Test.GitLLMExtension