- 10 translate plugin test files (100% coverage on 12 modules) - assistant/handler tools: 85+ tests covering dispatch, registry, resolvers, routes, llm_planner, 13 tool handlers - clean release: artifact_catalog_loader, mappers, approval, publication tests - API routes: translate_helpers, validation_service extensions, datasets to 100% - notifications: providers/service tests - services: profile_preference_service - docs/orthogonal-test-report.md — full speckit.tests audit - Fixes: 3 git_base async mock failures, 4 assistant handler permission-check patches - .gitignore: coverage artifacts
159 lines
6.1 KiB
Python
159 lines
6.1 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 -> [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
|