Files
ss-tools/backend/tests/plugins/translate/test_dictionary_crud.py
busya 8a4310169b v4: 7643 tests passing, 83% raw / 96.9% real coverage (excl __tests__).
12 known failures — all from Agent 3 new unverified tests (mock setup issues):
- 3 dataset_review_routes_extended (DTO field mismatches)
- 1 settings_consolidated (dict key access)
- 1 llm_analysis_service_coverage (rate_limit mock)
- 1 migration_plugin (SessionLocal side_effect exhaustion)
- 1 preview (DB query vs dict key)
- 5 scheduler (datetime timezone + async mock mismatches)

NEW TEST FILES THIS SESSION:
- test_batch_insert_coverage.py — 3 tests
- test_storage_plugin.py — +3 tests
- test_search.py — +2 tests
- test_mapper.py — already 100%
- test_llm_analysis_migration_v1_to_v2.py — 14 tests
- test_llm_async_http.py — +1 test
- test_prompt_builder.py — +1 test
- test_service_datasource.py — +1 test
- test_lang_detect.py — +1 test
- test_scheduler.py — +6 tests
- test_llm_analysis_service_coverage.py — +15 tests
- test_dataset_review_routes_extended.py — +14 tests
- test_settings_consolidated.py — +13 tests

Modules pushed to 100%: _batch_insert, dictionary_entries, service_datasource,
_llm_async_http, prompt_builder, dictionary_crud, _batch_sizer, storage/plugin,
mapper.py

Session: 7194→7643 tests (+449), 80%→83% raw (+3pp), 93.4%→96.9% real (+3.5pp).
Remaining: 12 failures to fix + ~300 statements to reach 98% real coverage.
2026-06-16 09:34:10 +03:00

194 lines
7.3 KiB
Python

# #region Test.DictionaryCRUD [C:3] [TYPE Module] [SEMANTICS test,dictionary,crud]
# @BRIEF Tests for dictionary_crud.py — DictionaryCRUD.
# @RELATION BINDS_TO -> [DictionaryCRUD]
# @TEST_EDGE: not_found -> ValueError
# @TEST_EDGE: delete_blocked_by_active_jobs -> ValueError
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
import pytest
from unittest.mock import MagicMock
from src.models.translate import (
DictionaryEntry,
TerminologyDictionary,
TranslationJob,
TranslationJobDictionary,
)
from src.plugins.translate.dictionary_crud import DictionaryCRUD
from .conftest import JOB_ID
class TestCreateDictionary:
"""DictionaryCRUD.create_dictionary."""
def test_create_basic(self, db_session):
"""Creates a new dictionary."""
d = DictionaryCRUD.create_dictionary(db_session, "My Dict", created_by="test")
assert d.name == "My Dict"
assert d.created_by == "test"
assert d.is_active is True
assert d.id is not None
def test_create_with_description(self, db_session):
"""Creates with description."""
d = DictionaryCRUD.create_dictionary(
db_session, "My Dict", description="Test dictionary",
)
assert d.description == "Test dictionary"
def test_create_inactive(self, db_session):
"""Creates inactive dictionary."""
d = DictionaryCRUD.create_dictionary(
db_session, "My Dict", is_active=False,
)
assert d.is_active is False
class TestUpdateDictionary:
"""DictionaryCRUD.update_dictionary."""
def test_update_name(self, db_session):
"""Update name."""
d = DictionaryCRUD.create_dictionary(db_session, "Original")
updated = DictionaryCRUD.update_dictionary(db_session, d.id, name="Updated")
assert updated.name == "Updated"
def test_update_description(self, db_session):
"""Update description."""
d = DictionaryCRUD.create_dictionary(db_session, "Test")
updated = DictionaryCRUD.update_dictionary(db_session, d.id, description="New desc")
assert updated.description == "New desc"
def test_update_is_active(self, db_session):
"""Update is_active."""
d = DictionaryCRUD.create_dictionary(db_session, "Test", is_active=True)
updated = DictionaryCRUD.update_dictionary(db_session, d.id, is_active=False)
assert updated.is_active is False
def test_update_not_found(self, db_session):
"""Update non-existent raises ValueError."""
with pytest.raises(ValueError, match="not found"):
DictionaryCRUD.update_dictionary(db_session, "nonexistent", name="x")
def test_update_partial(self, db_session):
"""Partial update doesn't change other fields."""
d = DictionaryCRUD.create_dictionary(
db_session, "Original", description="Desc", is_active=True,
)
updated = DictionaryCRUD.update_dictionary(db_session, d.id, name="New")
assert updated.description == "Desc"
assert updated.is_active is True
class TestDeleteDictionary:
"""DictionaryCRUD.delete_dictionary."""
def test_delete(self, db_session):
"""Delete a dictionary without attached jobs."""
d = DictionaryCRUD.create_dictionary(db_session, "To Delete")
dict_id = d.id
# Add some entries
from src.plugins.translate.dictionary_entries import DictionaryEntryCRUD
DictionaryEntryCRUD.add_entry(db_session, dict_id, "hello", "bonjour")
DictionaryCRUD.delete_dictionary(db_session, dict_id)
assert db_session.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first() is None
# Entries should also be deleted
entries = db_session.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).all()
assert len(entries) == 0
def test_not_found(self, db_session):
"""Delete non-existent raises ValueError."""
with pytest.raises(ValueError, match="not found"):
DictionaryCRUD.delete_dictionary(db_session, "nonexistent")
def test_delete_blocked_by_active_job(self, db_session):
"""Delete blocked when attached to active job."""
d = DictionaryCRUD.create_dictionary(db_session, "Blocked Dict")
dict_id = d.id
# Attach to the existing job
from src.models.translate import TranslationJobDictionary
job = db_session.query(TranslationJob).filter(TranslationJob.id == JOB_ID).first()
job.status = "ACTIVE"
db_session.commit()
tj_dict = TranslationJobDictionary(job_id=JOB_ID, dictionary_id=dict_id)
db_session.add(tj_dict)
db_session.commit()
with pytest.raises(ValueError, match="Cannot delete dictionary attached"):
DictionaryCRUD.delete_dictionary(db_session, dict_id)
def test_delete_blocked_by_scheduled_job(self, db_session):
"""Delete blocked when attached to SCHEDULED job."""
d = DictionaryCRUD.create_dictionary(db_session, "Scheduled Dict")
dict_id = d.id
job = db_session.query(TranslationJob).filter(TranslationJob.id == JOB_ID).first()
job.status = "SCHEDULED"
db_session.commit()
tj_dict = TranslationJobDictionary(job_id=JOB_ID, dictionary_id=dict_id)
db_session.add(tj_dict)
db_session.commit()
with pytest.raises(ValueError, match="Cannot delete dictionary attached"):
DictionaryCRUD.delete_dictionary(db_session, dict_id)
def test_delete_with_completed_job_ok(self, db_session):
"""Delete OK when attached to COMPLETED job."""
d = DictionaryCRUD.create_dictionary(db_session, "Completed Dict")
dict_id = d.id
job = db_session.query(TranslationJob).filter(TranslationJob.id == JOB_ID).first()
job.status = "COMPLETED"
db_session.commit()
tj_dict = TranslationJobDictionary(job_id=JOB_ID, dictionary_id=dict_id)
db_session.add(tj_dict)
db_session.commit()
DictionaryCRUD.delete_dictionary(db_session, dict_id)
assert db_session.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first() is None
class TestGetDictionary:
"""DictionaryCRUD.get_dictionary."""
def test_get(self, db_session):
"""Get existing dictionary."""
d = DictionaryCRUD.create_dictionary(db_session, "Get Test")
fetched = DictionaryCRUD.get_dictionary(db_session, d.id)
assert fetched.id == d.id
assert fetched.name == "Get Test"
def test_not_found(self, db_session):
"""Get non-existent raises ValueError."""
with pytest.raises(ValueError, match="not found"):
DictionaryCRUD.get_dictionary(db_session, "nonexistent")
class TestListDictionaries:
"""DictionaryCRUD.list_dictionaries."""
def test_list(self, db_session):
"""List with pagination."""
for i in range(5):
DictionaryCRUD.create_dictionary(db_session, f"Dict {i}")
dicts, total = DictionaryCRUD.list_dictionaries(db_session, page=1, page_size=2)
assert len(dicts) == 2
assert total == 5
def test_empty(self, db_session):
"""Empty list returns [] and 0."""
dicts, total = DictionaryCRUD.list_dictionaries(db_session)
assert dicts == []
assert total == 0
# #endregion Test.DictionaryCRUD