Files
office_translator/tests/test_translation_metadata_integration.py
sepehr 526c87348f
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m20s
feat(translation): quality pipeline overhaul + new features (audit 2026-08-29)
Translation quality & format preservation:
- Word: merge adjacent same-format runs into one unit (sentence-level
  coherence like inline-tag handling); translate comments/balloons;
  dedupe textbox collection (was translated twice); RTL no longer
  overrides center/justify alignment; CJK/Arabic font hints (eastAsia/cs)
- PPTX: chart translations now actually reach the output file
  (ChartPart.blob is read-only — rewrite chart XML in the saved ZIP);
  CJK typeface hints (a:ea)
- Excel: sheet renames no longer break references — rewrite cell
  formulas (3D/quoted), defined names, data validations, cond. formats
- PDF: bold/italic honored (hebo/heit/hebi); table cells never merge;
  unchanged blocks left untouched (typography preserved, fixes duplicate
  hyperlinks); attempted/changed stats + route gate now cover PDF;
  CJK font paths; scanned PDFs via Mistral OCR (detection + admin settings)

Features:
- formality param (formal/informal) + automatic regional-variant prompts
- output_mode=bilingual docx (source above translation)
- per-user translation memory on Redis (falls back to LRU), context-hashed
- QA report + 0-100 confidence score in job status; L0 on by default
- OpenAI-compatible providers: whole chunk in ONE numbered-JSON request
  (~15x fewer calls) with per-item fallback; base prompt always present
  (custom prompt no longer replaces translation instructions)

Infra & marketing alignment:
- plan-based engine gating + vision gating (closes paid-engine leak);
  /providers/available filtered per plan; 107 languages exposed
- zh-CN/zh-TW validation fixed; libmagic disabled on Windows (native crash)
- admin: Mistral OCR settings + engine status dashboard; httpx<0.28 pin
  (TestClient breakage); Prometheus test fixture fixed
- marketing docs aligned with code (PDF+OCR, retention, engines, pricing)
- security: .env.ionos/.env.production/provider_settings.json removed

Tests: 1173 passed / 0 failed (6 network tests deselected: free Google
endpoint temporarily blocked from this machine)
2026-08-29 18:38:09 +02:00

130 lines
5.1 KiB
Python

import pytest
import hashlib
from fastapi.testclient import TestClient
from unittest.mock import patch, AsyncMock, MagicMock
from main import app
from routes.translate_routes import get_authenticated_user
@pytest.fixture()
def client(monkeypatch):
"""TestClient with rate limiting and quota reservation bypassed for metadata tests."""
from middleware.rate_limiting import RateLimitMiddleware
async def _dispatch(self, request, call_next):
return await call_next(request)
monkeypatch.setattr(RateLimitMiddleware, "dispatch", _dispatch)
monkeypatch.setattr("routes.translate_routes.reserve_translation_quota", lambda user_id: True)
from main import app
return TestClient(app)
class MockUser:
def __init__(self, user_id="user_123"):
self.id = user_id
self.plan = "free"
self.docs_translated_this_month = 0
self.pages_translated_this_month = 0
self.extra_credits = 0
async def mock_auth():
return MockUser()
@pytest.mark.asyncio
async def test_translate_endpoint_triggers_tracking(client):
app.dependency_overrides[get_authenticated_user] = mock_auth
with patch(
"routes.translate_routes.storage_tracker.track_file", new_callable=AsyncMock
) as mock_track:
# The upload write is mocked below, so no real archive exists on
# disk: neutralize the zip safety check for this test.
with patch(
"routes.translate_routes.file_validator.validate_async"
) as mock_val, patch("routes.translate_routes.validate_zip_safety"):
mock_val.return_value.is_valid = True
mock_val.return_value.data = {"extension": ".docx", "size_bytes": 500}
file_content = b"PK\x03\x04fake_office_content_for_testing"
with patch(
"routes.translate_routes.file_handler_util.save_upload_file",
new_callable=AsyncMock,
) as mock_save:
with patch(
"routes.translate_routes.file_handler_util.calculate_sha256"
) as mock_hash:
with patch(
"routes.translate_routes.file_handler_util.cleanup_file"
) as mock_cleanup:
mock_save.return_value = None
expected_hash = hashlib.sha256(file_content).hexdigest()
mock_hash.return_value = expected_hash
files = {
"file": (
"test.docx",
file_content,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
}
response = client.post(
"/api/v1/translate", data={"target_lang": "fr"}, files=files
)
assert response.status_code == 202
job_id = response.json()["data"]["id"]
mock_track.assert_called_once()
args, kwargs = mock_track.call_args
assert kwargs["job_id"] == job_id
assert kwargs["metadata"]["original_filename"] == "test.docx"
assert kwargs["metadata"]["file_hash"] == expected_hash
assert kwargs["metadata"]["user_id"] == "user_123"
assert "timestamp" in kwargs["metadata"]
app.dependency_overrides.clear()
@pytest.mark.asyncio
async def test_translate_endpoint_handles_hash_failure(client):
app.dependency_overrides[get_authenticated_user] = mock_auth
# No real archive exists on disk (save is mocked): skip the zip check.
with patch(
"routes.translate_routes.file_validator.validate_async"
) as mock_val, patch("routes.translate_routes.validate_zip_safety"):
mock_val.return_value.is_valid = True
mock_val.return_value.data = {"extension": ".docx", "size_bytes": 500}
file_content = b"PK\x03\x04fake_office_content"
with patch(
"routes.translate_routes.file_handler_util.save_upload_file",
new_callable=AsyncMock,
):
with patch(
"routes.translate_routes.file_handler_util.calculate_sha256",
return_value=None,
):
with patch(
"routes.translate_routes.file_handler_util.cleanup_file"
) as mock_cleanup:
files = {
"file": (
"test.docx",
file_content,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
}
response = client.post(
"/api/v1/translate", data={"target_lang": "fr"}, files=files
)
assert response.status_code == 400
assert response.json()["error"] == "CORRUPTED_FILE"
mock_cleanup.assert_called_once()
app.dependency_overrides.clear()