Ctrl K
ComunidadGuías…

backend/tests/test_context_integration.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Integration test — context_windower -> translate_with_fallback -> LLMBackend.

Verifies that when _translate_in_batches calls translate_with_fallback with
multi-chunk lines, each chunk gets appropriate lookback/lookahead context.

The tests use a stub ``_FakeManager`` instead of a real TranslationManager
so we can observe the exact lookback/lookahead values passed to each chunk
without standing up a full backend stack.
"""

from __future__ import annotations

import pytest

from translation.base import TranslationResult


class _FakeManager:
    """Records every translate_with_fallback call for assertions.

    Echoes back ``T_<line>`` per input line so the caller's line-count
    verification succeeds.
    """

    def __init__(self) -> None:
        self.calls: list[dict] = []

    def translate_with_fallback(
        self,
        lines,
        source_lang,
        target_lang,
        fallback_chain,
        glossary_entries=None,
        *,
        lookback=None,
        lookahead=None,
    ) -> TranslationResult:
        self.calls.append(
            {
                "lines": list(lines),
                "lookback": list(lookback) if lookback is not None else None,
                "lookahead": list(lookahead) if lookahead is not None else None,
            }
        )
        return TranslationResult(
            success=True,
            translated_lines=[f"T_{line}" for line in lines],
            backend_name="fake",
            response_time_ms=10,
            characters_used=sum(len(line) for line in lines),
            error=None,
        )


@pytest.fixture
def enable_context(monkeypatch):
    """Force translation_context_enabled=True with deterministic window sizes.

    ``_translate_in_batches`` reads these via ``get_settings()``. We patch the
    singleton directly so tests are hermetic (no env-var / DB state leak).
    """
    from config import get_settings

    s = get_settings()
    monkeypatch.setattr(s, "translation_context_enabled", True, raising=False)
    monkeypatch.setattr(s, "translation_context_lookback_lines", 10, raising=False)
    monkeypatch.setattr(s, "translation_context_lookahead_lines", 5, raising=False)
    return s


def test_translate_in_batches_passes_context(enable_context):
    """Multi-chunk call should pass lookback/lookahead per chunk."""
    from translator.manager import _translate_in_batches

    fake = _FakeManager()
    lines = [f"line{i}" for i in range(25)]

    translated, last = _translate_in_batches(
        fake,
        lines,
        "en",
        "de",
        fallback_chain=["fake"],
        glossary_entries=None,
        batch_size=10,
    )

    assert len(translated) == 25
    assert translated[0] == "T_line0"
    # 3 chunks (10 + 10 + 5)
    assert len(fake.calls) == 3
    # First chunk: empty lookback -> None; has lookahead
    assert fake.calls[0]["lookback"] is None  # empty list converted to None
    assert fake.calls[0]["lookahead"] is not None
    assert fake.calls[0]["lookahead"] == ["line10", "line11", "line12", "line13", "line14"]
    # Middle chunk: both lookback and lookahead present
    assert fake.calls[1]["lookback"] is not None
    assert fake.calls[1]["lookahead"] is not None
    assert fake.calls[1]["lookback"] == [f"line{i}" for i in range(10)]
    # Last chunk: has lookback, empty lookahead -> None
    assert fake.calls[2]["lookback"] is not None
    assert fake.calls[2]["lookahead"] is None
    # last TranslationResult returned
    assert last.success is True


def test_single_batch_no_context(enable_context):
    """Single-batch call bypasses chunking entirely -- no context passed."""
    from translator.manager import _translate_in_batches

    fake = _FakeManager()
    lines = ["a", "b", "c"]

    _translate_in_batches(
        fake,
        lines,
        "en",
        "de",
        fallback_chain=["fake"],
        glossary_entries=None,
        batch_size=10,
    )

    assert len(fake.calls) == 1
    # Single-batch path doesn't pass context (positional call only)
    assert fake.calls[0]["lookback"] is None
    assert fake.calls[0]["lookahead"] is None


def test_context_disabled_via_config(monkeypatch):
    """When translation_context_enabled=False, chunks receive no context."""
    from config import get_settings
    from translator.manager import _translate_in_batches

    s = get_settings()
    monkeypatch.setattr(s, "translation_context_enabled", False, raising=False)

    fake = _FakeManager()
    lines = [f"line{i}" for i in range(25)]

    _translate_in_batches(
        fake,
        lines,
        "en",
        "de",
        fallback_chain=["fake"],
        glossary_entries=None,
        batch_size=10,
    )

    # 3 chunks but all receive no context (0 + 0 = empty -> None)
    assert len(fake.calls) == 3
    for c in fake.calls:
        assert c["lookback"] is None
        assert c["lookahead"] is None