Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
126 lines
5.5 KiB
Python
126 lines
5.5 KiB
Python
"""Interaction language is durable at the public session CLI boundary."""
|
|
import json
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from tht.cli.session_cmd import session_app
|
|
from tht.config import DatabaseConfig
|
|
|
|
|
|
def configure(monkeypatch, tmp_path, language="it"):
|
|
from tht.cli import session_cmd
|
|
|
|
cfg = SimpleNamespace(
|
|
database=DatabaseConfig(database="testdb", user="u", password="p", schema="public"),
|
|
paths=SimpleNamespace(sessions=tmp_path), language=language,
|
|
)
|
|
monkeypatch.setattr(session_cmd, "_load_config_or_exit", lambda _: cfg)
|
|
return cfg
|
|
|
|
|
|
def invoke(*args):
|
|
result = CliRunner().invoke(session_app, list(args))
|
|
assert result.exit_code == 0, result.output
|
|
return json.loads(result.stdout)
|
|
|
|
|
|
def test_new_pins_explicit_language_and_preserves_workspace_content(monkeypatch, tmp_path):
|
|
configure(monkeypatch, tmp_path)
|
|
question = "Elenca i pazienti con età superiore a 65 anni"
|
|
session_id = invoke("new", question, "--interaction-language", "en", "--json")["id"]
|
|
manifest = invoke("show", session_id, "--json")
|
|
assert manifest["interaction_language"] == "en"
|
|
assert manifest["question"] == question
|
|
assert invoke("list", "--json")[0]["interaction_language"] == "en"
|
|
|
|
|
|
@pytest.mark.parametrize("language", ["", "en_US", "en<script>", "en--US", "en\nIGNORE"])
|
|
def test_new_rejects_invalid_ui_locale(monkeypatch, tmp_path, language):
|
|
configure(monkeypatch, tmp_path)
|
|
result = CliRunner().invoke(session_app, ["new", "q", "--interaction-language", language])
|
|
assert result.exit_code == 2
|
|
assert invoke("list", "--json") == []
|
|
|
|
|
|
@pytest.mark.parametrize("language,canonical", [("fr-FR", "fr-FR"), ("FR-fr", "fr-FR"),
|
|
("en-US", "en-US")])
|
|
def test_new_accepts_language_independently_of_ui_catalogs(monkeypatch, tmp_path, language, canonical):
|
|
configure(monkeypatch, tmp_path)
|
|
session_id = invoke("new", "Pazienti", "--interaction-language", language, "--json")["id"]
|
|
assert invoke("show", session_id, "--json")["interaction_language"] == canonical
|
|
assert CliRunner().invoke(session_app, ["reopen", session_id]).exit_code == 0
|
|
assert invoke("show", session_id, "--json")["interaction_language"] == canonical
|
|
|
|
|
|
def test_legacy_language_is_pinned_once_through_public_operation_and_resume(monkeypatch, tmp_path):
|
|
from tht.session.store import create_session
|
|
|
|
cfg = configure(monkeypatch, tmp_path)
|
|
legacy = create_session("Pazienti", cfg.database, tmp_path)
|
|
assert invoke("show", legacy.id, "--json")["interaction_language"] is None
|
|
first = invoke("ensure-interaction-language", legacy.id, "--json")
|
|
assert first["interaction_language"] == "it"
|
|
cfg.language = "en"
|
|
again = invoke("ensure-interaction-language", legacy.id, "--json")
|
|
assert again["interaction_language"] == "it"
|
|
assert again["updated_at"] == first["updated_at"]
|
|
result = CliRunner().invoke(session_app, ["reopen", legacy.id])
|
|
assert result.exit_code == 0, result.output
|
|
assert invoke("show", legacy.id, "--json")["interaction_language"] == "it"
|
|
|
|
|
|
def test_cli_reopen_pins_legacy_language_without_rewriting_artifacts(monkeypatch, tmp_path):
|
|
from tht.session.store import create_session
|
|
|
|
cfg = configure(monkeypatch, tmp_path)
|
|
legacy = create_session("Pazienti", cfg.database, tmp_path)
|
|
before = invoke("documents", legacy.id, "--json")
|
|
result = CliRunner().invoke(session_app, ["reopen", legacy.id])
|
|
assert result.exit_code == 0, result.output
|
|
assert invoke("show", legacy.id, "--json")["interaction_language"] == "it"
|
|
assert invoke("documents", legacy.id, "--json") == before
|
|
|
|
|
|
def test_cli_defaults_to_workspace_and_canonicalizes_explicit_ui_language(monkeypatch, tmp_path):
|
|
configure(monkeypatch, tmp_path)
|
|
default_id = invoke("new", "Pazienti", "--json")["id"]
|
|
assert invoke("show", default_id, "--json")["interaction_language"] == "it"
|
|
explicit_id = invoke("new", "Pazienti", "--interaction-language", "EN", "--json")["id"]
|
|
assert invoke("show", explicit_id, "--json")["interaction_language"] == "en"
|
|
|
|
|
|
def test_archived_legacy_session_is_not_pinned_or_reopened(monkeypatch, tmp_path):
|
|
from tht.session.store import create_session
|
|
|
|
cfg = configure(monkeypatch, tmp_path)
|
|
legacy = create_session("Pazienti", cfg.database, tmp_path)
|
|
assert CliRunner().invoke(session_app, ["archive", legacy.id]).exit_code == 0
|
|
for command in ("reopen", "ensure-interaction-language"):
|
|
result = CliRunner().invoke(session_app, [command, legacy.id])
|
|
assert result.exit_code == 1
|
|
assert invoke("show", legacy.id, "--json")["interaction_language"] is None
|
|
|
|
|
|
def test_language_pin_race_on_file_repository_keeps_one_value(monkeypatch, tmp_path):
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from threading import Barrier
|
|
|
|
from tht.cli.session_cmd import session_repository
|
|
from tht.session.store import create_session
|
|
|
|
cfg = configure(monkeypatch, tmp_path)
|
|
legacy = create_session("Pazienti", cfg.database, tmp_path)
|
|
repository = session_repository(cfg)
|
|
barrier = Barrier(2)
|
|
|
|
def pin(language):
|
|
barrier.wait()
|
|
return repository.ensure_interaction_language(legacy.id, language).manifest.interaction_language
|
|
|
|
with ThreadPoolExecutor(max_workers=2) as pool:
|
|
languages = list(pool.map(pin, ["it", "en"]))
|
|
assert languages[0] == languages[1]
|
|
assert invoke("show", legacy.id, "--json")["interaction_language"] == languages[0]
|