EuropaLex / tests /app_test.py
Takosaga's picture
fix: remove mock cards in app.py
80aa142
Raw
History Blame Contribute Delete
6.36 kB
"""Tests for app.py helper functions and async generator handlers."""
from pathlib import Path
import inspect
import pytest
from unittest.mock import patch, MagicMock
PROJECT_ROOT = Path(__file__).resolve().parent.parent
# ── _progress_pct ────────────────────────────────────────────────
def test_progress_pct_single_sentence():
"""Single sentence (total=1): returns end_pct with 'complete' label."""
from app import _progress_pct
pct, label = _progress_pct(0, total=1)
assert pct == 70.0
assert "complete" in label.lower()
def test_progress_pct_two_sentences():
"""Two sentences: step 0 β†’ ~42.5%, step 1 β†’ complete."""
from app import _progress_pct
pct0, _ = _progress_pct(0, total=2)
pct1, label1 = _progress_pct(1, total=2)
# Step 0: 15 + (1/2) * (70-15) = 15 + 27.5 = 42.5
assert abs(pct0 - 42.5) < 0.1
# Step 1: should be complete
assert "complete" in label1.lower()
def test_progress_pct_five_sentences():
"""Five sentences: all steps verified for percentage and remaining count."""
from app import _progress_pct
total = 5
start_pct, end_pct = 15.0, 70.0
expected_pcts = [
round(start_pct + (1/5) * (end_pct - start_pct), 1), # step 0
round(start_pct + (2/5) * (end_pct - start_pct), 1), # step 1
round(start_pct + (3/5) * (end_pct - start_pct), 1), # step 2
round(start_pct + (4/5) * (end_pct - start_pct), 1), # step 3
end_pct, # step 4
]
for i in range(total):
pct, label = _progress_pct(i, total=total)
assert abs(pct - expected_pcts[i]) < 0.1
if i < total - 1:
remaining = total - (i + 1)
assert f"{i + 1}/{total}" in label
assert f"{remaining} remaining" in label
# ── generate_text_async ──────────────────────────────────────────
def test_generate_text_async_is_generator():
"""generate_text_async is a generator function."""
from app import generate_text_async
assert inspect.isgeneratorfunction(generate_text_async)
def test_generate_text_async_yields_progress_and_cards(mock_llm_response_factory):
"""Generator yields progress updates then card HTML when engine succeeds."""
from unittest.mock import patch, MagicMock
from app import generate_text_async
mock_engine = MagicMock()
mock_engine.generate.return_value = MagicMock(
generated_texts=["Hello.", "World."]
)
mock_pool_instance = MagicMock()
mock_pool_instance.get_english_engine.return_value = mock_engine
with patch("core.engine.EnginePool") as mock_pool_class:
mock_pool_class.get.return_value = mock_pool_instance
with patch("core.types.EngineConfig.from_settings_yaml") as mock_config:
mock_config.return_value.batch_size = 2
yields = list(generate_text_async("test scenario", "A1", 2))
# Should yield at least 3 tuples: progress+empty, progress+empty, progress+cards_html
assert len(yields) >= 3
# Last yield should have card HTML (non-empty string)
last_progress, last_cards = yields[-1]
assert "Hello." in last_cards or "World." in last_cards
def test_generate_text_async_file_not_found_error():
"""FileNotFoundError path β†’ error message in output."""
from app import generate_text_async
with patch("core.engine.EnginePool") as mock_pool_class:
mock_pool_class.get.side_effect = FileNotFoundError("model.gguf not found")
yields = list(generate_text_async("test", "A1", 2))
assert len(yields) >= 1
_, output = yields[0]
assert "Model file not found" in output or "model" in output.lower()
def test_generate_text_async_general_exception():
"""General exception path β†’ error message in output."""
from app import generate_text_async
with patch("core.engine.EnginePool") as mock_pool_class:
mock_pool_class.get.side_effect = RuntimeError("GPU out of memory")
yields = list(generate_text_async("test", "A1", 2))
assert len(yields) >= 1
_, output = yields[0]
assert "Failed to initialize" in output or "Setup error" in output
# ── generate_media_async ─────────────────────────────────────────
def test_generate_media_async_is_generator():
"""generate_media_async is a generator function."""
from app import generate_media_async
assert inspect.isgeneratorfunction(generate_media_async)
def test_generate_media_async_yields_per_sentence(mock_english_texts):
"""Cards grow with each yield during translation phase."""
from unittest.mock import patch, MagicMock
from app import generate_media_async
# Set up Phase 1 texts
import app as app_module
original = list(app_module._phase1_texts)
app_module._phase1_texts = list(mock_english_texts)
try:
mock_engine = MagicMock()
mock_engine._translate_single.return_value = "Sveiki."
mock_pool_instance = MagicMock()
mock_pool_instance.get_translation_engine.return_value = mock_engine
with patch("core.engine.EnginePool") as mock_pool_class:
mock_pool_class.get.return_value = mock_pool_instance
yields = list(generate_media_async("test", "A1", 3))
# Each translation step yields (progress, cards)
# At least 3 yields for 3 sentences + final yield
card_yields = [(p, c) for p, c in yields if "translation" in c.lower() or "Sveiki" in c]
assert len(card_yields) >= 1
finally:
app_module._phase1_texts = original
def test_generate_media_async_missing_phase1_texts():
"""No Phase 1 texts β†’ error message."""
from app import generate_media_async
import app as app_module
original = list(app_module._phase1_texts)
app_module._phase1_texts = []
try:
yields = list(generate_media_async("test", "A1", 3))
assert len(yields) >= 1
_, output = yields[0]
assert "Phase 1" in output or "generate text first" in output.lower()
finally:
app_module._phase1_texts = original