Ctrl K
ComunidadGuías…

backend/tests/test_combine_config.py

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

Archivo original ↗Preguntar a KiBot
"""Tests for per-profile combined-subtitle config fields (V1.6 Feature #1, C1).

A LanguageProfile carries the combine rules: whether automatic combine is on,
the output format, which target languages to combine, and ASS positioning.
Unlike the provisional-MT fields (#8), combine IS exposed through profile CRUD
because the frontend profile editor edits it directly.
See docs/plans/2026-07-04-v1.6-combined-subtitles.md.
"""

from __future__ import annotations

import json

import pytest

from db.models.core import LanguageProfile
from db.repositories.profiles import ProfileRepository
from extensions import db

_DEFAULT_POSITION = {"primary": "bottom", "secondary": "top"}


def _make(repo: ProfileRepository, name: str, **extra) -> int:
    return repo.create_profile(
        name=name,
        source_lang="en",
        source_name="English",
        target_langs=["de", "en"],
        target_names=["German", "English"],
        **extra,
    )


def test_new_profile_has_combine_defaults(app_ctx):
    """A freshly created profile gets the combine defaults."""
    repo = ProfileRepository()
    pid = _make(repo, "Combine Defaults")

    p = db.session.get(LanguageProfile, pid)
    assert p.combine_enabled == 0
    assert p.combine_format == "ass"
    assert json.loads(p.combine_languages_json) == []
    assert json.loads(p.combine_position_json) == _DEFAULT_POSITION


def test_serialized_profile_exposes_parsed_combine_fields(app_ctx):
    """get_profile() returns parsed combine_* keys and strips the raw _json columns."""
    repo = ProfileRepository()
    pid = _make(repo, "Combine Serialize")

    d = repo.get_profile(pid)
    assert d["combine_enabled"] is False
    assert d["combine_format"] == "ass"
    assert d["combine_languages"] == []
    assert d["combine_position"] == _DEFAULT_POSITION
    assert "combine_languages_json" not in d
    assert "combine_position_json" not in d


def test_create_profile_accepts_combine_kwargs(app_ctx):
    repo = ProfileRepository()
    pid = _make(
        repo,
        "Combine Create",
        combine_enabled=True,
        combine_format="srt",
        combine_languages=["de", "en"],
        combine_position={"primary": "top", "secondary": "bottom"},
    )

    d = repo.get_profile(pid)
    assert d["combine_enabled"] is True
    assert d["combine_format"] == "srt"
    assert d["combine_languages"] == ["de", "en"]
    assert d["combine_position"] == {"primary": "top", "secondary": "bottom"}


def test_update_profile_persists_combine_fields(app_ctx):
    repo = ProfileRepository()
    pid = _make(repo, "Combine Update")

    ok = repo.update_profile(
        pid,
        combine_enabled=1,
        combine_format="srt",
        combine_languages=["de", "en"],
        combine_position={"primary": "top", "secondary": "bottom"},
    )
    assert ok

    d = repo.get_profile(pid)
    assert d["combine_enabled"] is True
    assert d["combine_format"] == "srt"
    assert d["combine_languages"] == ["de", "en"]
    assert d["combine_position"] == {"primary": "top", "secondary": "bottom"}


def test_create_profile_rejects_invalid_combine_format(app_ctx):
    repo = ProfileRepository()
    with pytest.raises(ValueError):
        _make(repo, "Combine Bad Fmt", combine_format="vtt")


def test_update_profile_rejects_invalid_combine_format(app_ctx):
    repo = ProfileRepository()
    pid = _make(repo, "Combine Update Bad Fmt")
    with pytest.raises(ValueError):
        repo.update_profile(pid, combine_format="mkv")


# --- Service layer (the mapping from route JSON body to repo kwargs) ---------


def test_service_create_and_update_round_trip_combine(app_ctx):
    """The profile_service maps combine_* JSON keys through to persistence."""
    from services import profile_service

    created = profile_service.create_profile(
        {
            "name": "Combine Service",
            "target_languages": ["de", "en"],
            "target_language_names": ["German", "English"],
            "combine_enabled": True,
            "combine_format": "ass",
            "combine_languages": ["de", "en"],
            "combine_position": {"primary": "bottom", "secondary": "top"},
        }
    )
    assert created["combine_enabled"] is True
    assert created["combine_languages"] == ["de", "en"]

    updated = profile_service.update_profile(
        created["id"],
        {"combine_format": "srt", "combine_position": {"primary": "top", "secondary": "bottom"}},
    )
    assert updated["combine_format"] == "srt"
    assert updated["combine_position"] == {"primary": "top", "secondary": "bottom"}


def test_service_create_rejects_bad_combine_format(app_ctx):
    from services import profile_service

    with pytest.raises(profile_service.ProfileValidationError):
        profile_service.create_profile({"name": "Combine Service Bad", "combine_format": "vtt"})


def test_service_rejects_non_code_combine_languages(app_ctx):
    """combine_languages must be plain lang codes — not path fragments or the
    hyphen-joined combined tag (which could match an existing combined sidecar)."""
    from services import profile_service

    for bad in (["../../etc", "en"], ["de-en", "fr"], ["deu", "deu"]):
        with pytest.raises(profile_service.ProfileValidationError):
            profile_service.create_profile({"name": f"Bad {bad}", "combine_languages": bad})