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.
194 lines
7.3 KiB
Python
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
|