Files
ss-tools/backend/tests/test_translate_corrections.py
busya 50d6b9226e test(translate): add tests for ConnectionService, DbExecutor, orchestrator direct DB dispatch
- 9 new enhancement test files: test_connection_service.py,
  test_db_executor.py, test_orchestrator_direct_db.py, test_batch_insert.py,
  test_lang_stats.py, test_response_field_coverage.py, test_retry.py,
  test_run_service.py, test_sql_insert_service.py
- 5 new integration tests: test_superset_sqllab_e2e.py,
  test_translate_clickhouse.py, test_translate_corrections.py,
  test_translate_schedules.py, test_translate_status_fk.py
- Updated existing tests for insert_method/connection_id fields
2026-06-11 19:12:45 +03:00

250 lines
8.2 KiB
Python

# #region TranslateCorrectionTests [C:2] [TYPE Module]
# @SEMANTICS: tests, translate, corrections, dictionary
# @BRIEF Tests for term correction API endpoints and DictionaryManager correction methods.
# @LAYER Tests
# @RELATION BINDS_TO -> [DictionaryManagerModule]
# @RELATION BINDS_TO -> [TranslateRoutes]
#
# @TEST_CONTRACT: CorrectionFlow ->
# {
# required_fields: {source_term, incorrect_target_term, corrected_target_term, dictionary_id},
# invariants: [
# "POST /api/translate/corrections creates or updates entry",
# "Conflict detection works for existing entries",
# "POST /api/translate/corrections/bulk atomic all-or-nothing",
# "Origin tracking fields are populated"
# ]
# }
# @TEST_FIXTURE: valid_dictionary -> created via DictionaryManager
# @TEST_EDGE: missing_dictionary_id -> 422
# @TEST_EDGE: conflict_detected -> conflict response returned
# @TEST_EDGE: bulk_correction_all_succeed -> atomic commit
# @TEST_EDGE: bulk_correction_with_conflicts -> conflicts listed
# @TEST_INVARIANT: correction_flow -> VERIFIED_BY: [valid_dictionary, conflict_detected]
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
import pytest
from unittest.mock import MagicMock
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from src.app import app
from src.core.database import Base
from src.dependencies import get_config_manager, get_current_user, get_db
from src.plugins.translate.dictionary import DictionaryManager
# In-memory SQLite
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base.metadata.create_all(bind=engine)
mock_user = MagicMock()
mock_user.username = "testuser"
mock_user.roles = []
admin_role = MagicMock()
admin_role.name = "Admin"
mock_user.roles.append(admin_role)
@pytest.fixture
def db_session():
connection = engine.connect()
transaction = connection.begin()
session = TestingSessionLocal(bind=connection)
yield session
session.close()
transaction.rollback()
connection.close()
@pytest.fixture
def mock_api_deps():
config_manager = MagicMock()
config_manager.get_environments.return_value = []
connection = engine.connect()
transaction = connection.begin()
session = TestingSessionLocal(bind=connection)
def override_get_db():
try:
yield session
finally:
pass
overrides = {
get_current_user: lambda: mock_user,
get_config_manager: lambda: config_manager,
get_db: override_get_db,
}
for dep, override in overrides.items():
app.dependency_overrides[dep] = override
yield {"config_manager": config_manager, "session": session}
app.dependency_overrides.clear()
session.close()
transaction.rollback()
connection.close()
@pytest.fixture
def client(mock_api_deps):
return TestClient(app)
# #region test_submit_correction_creates_entry [C:2] [TYPE Function]
# @BRIEF Verify that a correction creates a new dictionary entry.
def test_submit_correction_creates_entry(db_session):
"""Test that submitting a correction creates a new entry."""
dict_obj = DictionaryManager.create_dictionary(
db_session, name="Test Dict",
created_by="testuser",
)
result = DictionaryManager.submit_correction(
db_session,
dict_id=dict_obj.id,
source_term="hello",
incorrect_target_term="privet",
corrected_target_term="zdravstvuyte",
origin_run_id="run-123",
origin_row_key="row-1",
origin_user_id="testuser",
)
assert result["action"] == "created"
assert result["entry_id"] is not None
entries, total = DictionaryManager.list_entries(db_session, dict_obj.id)
assert total == 1
assert entries[0].source_term == "hello"
assert entries[0].target_term == "zdravstvuyte"
assert entries[0].origin_run_id == "run-123"
assert entries[0].origin_row_key == "row-1"
assert entries[0].origin_user_id == "testuser"
# #endregion test_submit_correction_creates_entry
# #region test_submit_correction_conflict_detected [C:2] [TYPE Function]
# @BRIEF Verify conflict detection when entry already exists.
def test_submit_correction_conflict_detected(db_session):
"""Test that conflict is detected when entry already exists."""
dict_obj = DictionaryManager.create_dictionary(
db_session, name="Dict",
)
# Create initial entry
DictionaryManager.add_entry(db_session, dict_obj.id, "hello", "privet")
# Submit correction with conflict
result = DictionaryManager.submit_correction(
db_session,
dict_id=dict_obj.id,
source_term="hello",
incorrect_target_term="privet",
corrected_target_term="zdravstvuyte",
on_conflict="keep_existing",
)
assert result["action"] == "conflict_detected"
assert result["conflict"] is not None
assert result["conflict"]["existing_target_term"] == "privet"
# #endregion test_submit_correction_conflict_detected
# #region test_submit_correction_overwrite [C:2] [TYPE Function]
# @BRIEF Verify that correction overwrites existing entry.
def test_submit_correction_overwrite(db_session):
"""Test that correction overwrites existing entry."""
dict_obj = DictionaryManager.create_dictionary(
db_session, name="Dict",
)
DictionaryManager.add_entry(db_session, dict_obj.id, "hello", "privet")
result = DictionaryManager.submit_correction(
db_session,
dict_id=dict_obj.id,
source_term="hello",
incorrect_target_term="privet",
corrected_target_term="hi_there",
on_conflict="overwrite",
)
assert result["action"] == "updated"
entries, _ = DictionaryManager.list_entries(db_session, dict_obj.id)
assert entries[0].target_term == "hi_there"
# #endregion test_submit_correction_overwrite
# #region test_bulk_corrections_atomic [C:2] [TYPE Function]
# @BRIEF Verify bulk corrections are applied atomically.
def test_bulk_corrections_atomic(db_session):
"""Test that bulk corrections are applied atomically."""
dict_obj = DictionaryManager.create_dictionary(
db_session, name="Bulk Dict",
)
corrections = [
{"source_term": "cat", "incorrect_target_term": "kot", "corrected_target_term": "koshka"},
{"source_term": "dog", "incorrect_target_term": "sobaka", "corrected_target_term": "pyos"},
]
result = DictionaryManager.submit_bulk_corrections(
db_session,
dict_id=dict_obj.id,
corrections=corrections,
origin_user_id="testuser",
)
assert result["status"] == "completed"
assert result["applied"] == 2
assert len(result["conflicts"]) == 0
entries, total = DictionaryManager.list_entries(db_session, dict_obj.id)
assert total == 2
# #endregion test_bulk_corrections_atomic
# #region test_api_correction_missing_dict [C:2] [TYPE Function]
# @BRIEF Verify POST corrections without dictionary_id returns 422.
def test_api_correction_missing_dict(client):
"""Test correction without dict_id returns 422."""
response = client.post("/api/translate/corrections", json={
"source_term": "hello",
"incorrect_target_term": "privet",
"corrected_target_term": "zdravstvuyte",
})
assert response.status_code == 422
# #endregion test_api_correction_missing_dict
# #region test_api_bulk_corrections [C:2] [TYPE Function]
# @BRIEF Verify POST /corrections/bulk works.
def test_api_bulk_corrections(client, mock_api_deps):
"""Test bulk corrections endpoint."""
session = mock_api_deps["session"]
dict_obj = DictionaryManager.create_dictionary(
session, name="API Dict",
)
response = client.post("/api/translate/corrections/bulk", json={
"dictionary_id": dict_obj.id,
"corrections": [
{"source_term": "hello", "incorrect_target_term": "privet", "corrected_target_term": "hi"},
],
})
assert response.status_code == 200
data = response.json()
assert data["status"] == "completed"
# #endregion test_api_bulk_corrections
# #endregion TranslateCorrectionTests