test: remediate and stabilize auxiliary backend and frontend tests

- Standardized task log, LLM provider, and report profile tests.
- Relocated auxiliary tests into __tests__ directories for consistency.
- Updated git_service and defensive guards with minor stability fixes discovered during testing.
- Added UX integration tests for the reports list component.
This commit is contained in:
2026-03-04 13:54:06 +03:00
parent 0894254b98
commit f34f9c1b2e
8 changed files with 408 additions and 3 deletions

View File

@@ -0,0 +1,81 @@
# [DEF:__tests__/test_llm_provider:Module]
# @RELATION: VERIFIES -> ../llm_provider.py
# @PURPOSE: Contract testing for LLMProviderService and EncryptionManager
# [/DEF:__tests__/test_llm_provider:Module]
import pytest
import os
from unittest.mock import MagicMock
from sqlalchemy.orm import Session
from src.services.llm_provider import EncryptionManager, LLMProviderService
from src.models.llm import LLMProvider
from src.plugins.llm_analysis.models import LLMProviderConfig, ProviderType
# @TEST_CONTRACT: EncryptionManagerModel -> Invariants
# @TEST_INVARIANT: symmetric_encryption
def test_encryption_cycle():
"""Verify encrypted data can be decrypted back to original string."""
manager = EncryptionManager()
original = "secret_api_key_123"
encrypted = manager.encrypt(original)
assert encrypted != original
assert manager.decrypt(encrypted) == original
# @TEST_EDGE: empty_string_encryption
def test_empty_string_encryption():
manager = EncryptionManager()
original = ""
encrypted = manager.encrypt(original)
assert manager.decrypt(encrypted) == ""
# @TEST_EDGE: decrypt_invalid_data
def test_decrypt_invalid_data():
manager = EncryptionManager()
with pytest.raises(Exception):
manager.decrypt("not-encrypted-string")
# @TEST_FIXTURE: mock_db_session
@pytest.fixture
def mock_db():
return MagicMock(spec=Session)
@pytest.fixture
def service(mock_db):
return LLMProviderService(db=mock_db)
def test_get_all_providers(service, mock_db):
service.get_all_providers()
mock_db.query.assert_called()
mock_db.query().all.assert_called()
def test_create_provider(service, mock_db):
config = LLMProviderConfig(
provider_type=ProviderType.OPENAI,
name="Test OpenAI",
base_url="https://api.openai.com",
api_key="sk-test",
default_model="gpt-4",
is_active=True
)
provider = service.create_provider(config)
mock_db.add.assert_called()
mock_db.commit.assert_called()
# Verify API key was encrypted
assert provider.api_key != "sk-test"
# Decrypt to verify it matches
assert EncryptionManager().decrypt(provider.api_key) == "sk-test"
def test_get_decrypted_api_key(service, mock_db):
# Setup mock provider
encrypted_key = EncryptionManager().encrypt("secret-value")
mock_provider = LLMProvider(id="p1", api_key=encrypted_key)
mock_db.query().filter().first.return_value = mock_provider
key = service.get_decrypted_api_key("p1")
assert key == "secret-value"
def test_get_decrypted_api_key_not_found(service, mock_db):
mock_db.query().filter().first.return_value = None
assert service.get_decrypted_api_key("missing") is None

View File

@@ -46,10 +46,21 @@ class GitService:
backend_root = Path(__file__).parents[2]
self.legacy_base_path = str((backend_root / "git_repos").resolve())
self.base_path = self._resolve_base_path(base_path)
if not os.path.exists(self.base_path):
os.makedirs(self.base_path)
self._ensure_base_path_exists()
# [/DEF:__init__:Function]
# [DEF:_ensure_base_path_exists:Function]
# @PURPOSE: Ensure the repositories root directory exists and is a directory.
# @PRE: self.base_path is resolved to filesystem path.
# @POST: self.base_path exists as directory or raises ValueError.
# @RETURN: None
def _ensure_base_path_exists(self) -> None:
base = Path(self.base_path)
if base.exists() and not base.is_dir():
raise ValueError(f"Git repositories base path is not a directory: {self.base_path}")
base.mkdir(parents=True, exist_ok=True)
# [/DEF:_ensure_base_path_exists:Function]
# [DEF:_resolve_base_path:Function]
# @PURPOSE: Resolve base repository directory from explicit argument or global storage settings.
# @PRE: base_path is a string path.
@@ -167,6 +178,7 @@ class GitService:
with belief_scope("GitService._get_repo_path"):
if dashboard_id is None:
raise ValueError("dashboard_id cannot be None")
self._ensure_base_path_exists()
fallback_key = repo_key if repo_key is not None else str(dashboard_id)
normalized_key = self._normalize_repo_key(fallback_key)
target_path = os.path.join(self.base_path, normalized_key)
@@ -214,6 +226,7 @@ class GitService:
# @RETURN: Repo - GitPython Repo object.
def init_repo(self, dashboard_id: int, remote_url: str, pat: str, repo_key: Optional[str] = None) -> Repo:
with belief_scope("GitService.init_repo"):
self._ensure_base_path_exists()
repo_path = self._get_repo_path(dashboard_id, repo_key=repo_key or str(dashboard_id))
Path(repo_path).parent.mkdir(parents=True, exist_ok=True)

View File

@@ -0,0 +1,47 @@
# [DEF:__tests__/test_report_type_profiles:Module]
# @RELATION: VERIFIES -> ../type_profiles.py
# @PURPOSE: Contract testing for task type profiles and resolution logic.
# [/DEF:__tests__/test_report_type_profiles:Module]
import pytest
from src.models.report import TaskType
from src.services.reports.type_profiles import resolve_task_type, get_type_profile
# @TEST_CONTRACT: ResolveTaskType -> Invariants
# @TEST_INVARIANT: fallback_to_unknown
def test_resolve_task_type_fallbacks():
"""Verify missing/unmapped plugin_id returns TaskType.UNKNOWN."""
assert resolve_task_type(None) == TaskType.UNKNOWN
assert resolve_task_type("") == TaskType.UNKNOWN
assert resolve_task_type(" ") == TaskType.UNKNOWN
assert resolve_task_type("invalid_plugin") == TaskType.UNKNOWN
# @TEST_FIXTURE: valid_plugin
def test_resolve_task_type_valid():
"""Verify known plugin IDs map correctly."""
assert resolve_task_type("superset-migration") == TaskType.MIGRATION
assert resolve_task_type("llm_dashboard_validation") == TaskType.LLM_VERIFICATION
assert resolve_task_type("superset-backup") == TaskType.BACKUP
assert resolve_task_type("documentation") == TaskType.DOCUMENTATION
# @TEST_FIXTURE: valid_profile
def test_get_type_profile_valid():
"""Verify known task types return correct profile metadata."""
profile = get_type_profile(TaskType.MIGRATION)
assert profile["display_label"] == "Migration"
assert profile["visual_variant"] == "migration"
assert profile["fallback"] is False
# @TEST_INVARIANT: always_returns_dict
# @TEST_EDGE: missing_profile
def test_get_type_profile_fallback():
"""Verify unknown task type returns fallback profile."""
# Assuming TaskType.UNKNOWN or any non-mapped value
profile = get_type_profile(TaskType.UNKNOWN)
assert profile["display_label"] == "Other / Unknown"
assert profile["fallback"] is True
# Passing a value that might not be in the dict explicitly
profile_fallback = get_type_profile("non-enum-value")
assert profile_fallback["display_label"] == "Other / Unknown"
assert profile_fallback["fallback"] is True