"""Unit tests for app.services.i18n.""" from __future__ import annotations import pytest def test_languages_contains_all_four_plus_english(): from app.services.i18n import LANGUAGES assert set(LANGUAGES.keys()) == {"en", "it", "es", "fr", "de"} assert LANGUAGES["en"] == "English" assert LANGUAGES["it"] == "Italian" assert LANGUAGES["es"] == "Spanish" assert LANGUAGES["fr"] == "French" assert LANGUAGES["de"] == "German" def test_active_languages_is_en_and_it_only(): from app.services.i18n import ACTIVE_LANGUAGES assert ACTIVE_LANGUAGES == {"en", "it"} def test_respond_in_clause_empty_for_english(): from app.services.i18n import respond_in_clause assert respond_in_clause("en") == "" def test_respond_in_clause_empty_for_none_or_empty(): from app.services.i18n import respond_in_clause assert respond_in_clause("") == "" assert respond_in_clause(None) == "" def test_respond_in_clause_italian(): from app.services.i18n import respond_in_clause result = respond_in_clause("it") assert "Italian" in result assert result.startswith("\n\n") def test_respond_in_clause_unknown_lang_falls_back_to_english(): """Defensive: a raw POST or stale lang code should not crash the prompt assembly. Unknown codes map to no-suffix (English default).""" from app.services.i18n import respond_in_clause assert respond_in_clause("xx") == "" @pytest.mark.asyncio async def test_translate_happy_path(monkeypatch): from unittest.mock import AsyncMock, MagicMock from app.services import translation as mod from app.services.openrouter import LogResult monkeypatch.setattr(mod, "call_llm", AsyncMock(return_value=LogResult( content="# Apertura\n\nIl mercato รจ in calo dello 0,4%.", model="deepseek/deepseek-v4-flash", prompt_tokens=300, completion_tokens=80, cost_usd=0.00002, ))) client = MagicMock() translated, llm_log = await mod.translate( client, "# Open\n\nThe market is down 0.4%.", "it", ) assert "Apertura" in translated assert llm_log.model == "deepseek/deepseek-v4-flash" assert llm_log.cost_usd == pytest.approx(0.00002) @pytest.mark.asyncio async def test_translate_strips_code_fences(monkeypatch): """If the LLM wraps the output in ```markdown ... ```, strip it.""" from unittest.mock import AsyncMock, MagicMock from app.services import translation as mod from app.services.openrouter import LogResult fenced = "```markdown\n# Titolo\n\nCorpo.\n```" monkeypatch.setattr(mod, "call_llm", AsyncMock(return_value=LogResult( content=fenced, model="m", prompt_tokens=10, completion_tokens=20, cost_usd=0.0, ))) client = MagicMock() translated, _ = await mod.translate(client, "# Title\n\nBody.", "it") assert "```" not in translated assert translated.startswith("# Titolo") @pytest.mark.asyncio async def test_translate_provider_failure_propagates(monkeypatch): from unittest.mock import AsyncMock, MagicMock from app.services import translation as mod monkeypatch.setattr(mod, "call_llm", AsyncMock(side_effect=RuntimeError("upstream down"))) client = MagicMock() with pytest.raises(RuntimeError, match="upstream down"): await mod.translate(client, "# Title\n\nBody.", "it") @pytest.mark.asyncio async def test_translate_unknown_lang_returns_source_unchanged(monkeypatch): """Defensive: an unknown lang code (or 'en') short-circuits without calling the LLM. Callers shouldn't have to gate the call themselves.""" from unittest.mock import AsyncMock, MagicMock from app.services import translation as mod from app.services.openrouter import LogResult call_mock = AsyncMock(return_value=LogResult( content="should not be returned", model="m", prompt_tokens=0, completion_tokens=0, cost_usd=0.0, )) monkeypatch.setattr(mod, "call_llm", call_mock) client = MagicMock() out, _ = await mod.translate(client, "Hello world.", "en") assert out == "Hello world." call_mock.assert_not_awaited()