Files

87 lines
3.8 KiB
Python
Raw Permalink Normal View History

"""Email translation over the API, with LibreTranslate replaced by a stand-in."""
from __future__ import annotations
import pytest
from app import translation
from tests.api.conftest import MailCtx, raw_mail
TEXT = "Hello Lukas, I would like to ask about the project schedule for next month."
@pytest.fixture
def fake_service(monkeypatch: pytest.MonkeyPatch) -> dict[str, object]:
calls: dict[str, object] = {"detect": 0, "translate": 0}
async def detect(text: str) -> str:
calls["detect"] = int(calls["detect"]) + 1 # type: ignore[call-overload]
return "en"
async def translate_html(
html: str, source: str, target: str, keep: object = (), names: object = ()
) -> str:
calls["translate"] = int(calls["translate"]) + 1 # type: ignore[call-overload]
calls["keep"] = list(keep) # type: ignore[call-overload]
calls["names"] = list(names) # type: ignore[call-overload]
assert source == "en" and target == "pl"
return "<p>Cześć Łukasz</p><script>alert(1)</script>"
monkeypatch.setattr(translation, "enabled", lambda: True)
monkeypatch.setattr(translation, "detect", detect)
monkeypatch.setattr(translation, "translate_html", translate_html)
return calls
async def test_language_is_found_once_and_kept(
mail_ctx: MailCtx, fake_service: dict[str, object]
) -> None:
msg = await mail_ctx.deliver(raw_mail("Project", html=f"<p>{TEXT}</p>"), 1)
for _ in range(2):
resp = await mail_ctx.client.get(f"/api/messages/{msg.id}/html")
assert resp.headers["X-Content-Language"] == "en"
assert fake_service["detect"] == 1
(env,) = (await mail_ctx.client.get(f"/api/threads/{msg.thread_id}")).json()["messages"]
assert env["content_language"] == "en"
async def test_translation_is_cleaned_and_kept(
mail_ctx: MailCtx, fake_service: dict[str, object]
) -> None:
msg = await mail_ctx.deliver(raw_mail("Project", html=f"<p>{TEXT}</p>"), 1)
for _ in range(2):
resp = await mail_ctx.client.get(f"/api/messages/{msg.id}/html", params={"translate": "pl"})
assert resp.status_code == 200
assert "Cześć Łukasz" in resp.text and "alert(1)" not in resp.text
assert resp.headers["X-Translated-From"] == "en"
assert resp.headers["X-Translated-To"] == "pl"
assert fake_service["translate"] == 1 # the second came from the cache
# The people on the message are names to keep if the model would change them.
assert "News" in fake_service["names"] # type: ignore[operator]
async def test_service_trouble_is_a_clear_error(
mail_ctx: MailCtx, monkeypatch: pytest.MonkeyPatch, fake_service: dict[str, object]
) -> None:
async def broken(
html: str, source: str, target: str, keep: object = (), names: object = ()
) -> str:
raise translation.TranslationError("the translation service is unreachable")
monkeypatch.setattr(translation, "translate_html", broken)
msg = await mail_ctx.deliver(raw_mail("Project", html=f"<p>{TEXT}</p>"), 1)
resp = await mail_ctx.client.get(f"/api/messages/{msg.id}/html", params={"translate": "pl"})
assert resp.status_code == 503
assert resp.json()["error"]["code"] == "translation_failed"
# The message itself still shows.
assert (await mail_ctx.client.get(f"/api/messages/{msg.id}/html")).status_code == 200
async def test_off_without_a_service(mail_ctx: MailCtx, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(translation, "enabled", lambda: False)
msg = await mail_ctx.deliver(raw_mail("Project", html=f"<p>{TEXT}</p>"), 1)
resp = await mail_ctx.client.get(f"/api/messages/{msg.id}/html")
assert "X-Content-Language" not in resp.headers
info = (await mail_ctx.client.get("/api/translation/languages")).json()
assert info == {"available": False, "languages": []}