Files
ThothII/harness/tests/test_session_interaction_language.py
Codex bd416f7327
Publish documentation / publish (push) Successful in 34s
Fix new-question landing and question-language HITL
Reset the activity panel when starting a new question so the landing navigation is restored. Detect and persist the original question language, pass it through runtime and widget descriptors, and scope HITL controls to that language.

Validated with gate, session, backend and frontend tests, TypeScript checks, Ruff and strict docs build. Rebuilt and restarted local core/frontend; both healthy and serving HTTP successfully.
2026-09-21 19:47:22 +02:00

153 lines
6.8 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_question_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"] == "it"
assert manifest["question"] == question
assert invoke("list", "--json")[0]["interaction_language"] == "it"
@pytest.mark.parametrize("fallback", ["en", "it", None])
def test_english_question_controls_interaction_in_italian_workspace(monkeypatch, tmp_path, fallback):
configure(monkeypatch, tmp_path)
question = "How many patients were admitted to the hospital last year?"
args = ["--interaction-language", fallback] if fallback else []
created = invoke("new", question, *args, "--json")
assert created["interaction_language"] == "en"
assert invoke("show", created["id"], "--json")["interaction_language"] == "en"
@pytest.mark.parametrize("question", ["", "q", "123", "HbA1c", "SELECT * FROM patients"])
def test_ambiguous_or_code_question_keeps_fallback(monkeypatch, tmp_path, question):
configure(monkeypatch, tmp_path)
created = invoke("new", question, "--interaction-language", "fr-FR", "--json")
assert invoke("show", created["id"], "--json")["interaction_language"] == "fr-FR"
@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_legacy_english_question_is_pinned_before_resume(monkeypatch, tmp_path):
from tht.session.store import create_session
cfg = configure(monkeypatch, tmp_path)
legacy = create_session("How many patients were admitted last year?", cfg.database, tmp_path)
result = CliRunner().invoke(session_app, ["reopen", legacy.id])
assert result.exit_code == 0, result.output
assert invoke("show", legacy.id, "--json")["interaction_language"] == "en"
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]