"""Tests for `tht session list --json` and `tht session show --json` (Task 7).""" import json import pytest from typer.testing import CliRunner from tht.cli.session_cmd import _list_sessions, session_app from tht.session.store import create_session def _make_db(): from tht.config import DatabaseConfig return DatabaseConfig( database="testdb", user="testuser", password="testpass", # noqa: S106 **{"schema": "public"}, ) # --------------------------------------------------------------------------- # Unit: _list_sessions helper # --------------------------------------------------------------------------- def test_list_sessions_returns_all_created(tmp_path): db = _make_db() m1 = create_session("prima domanda", db, tmp_path) m2 = create_session("seconda domanda", db, tmp_path) rows = _list_sessions(tmp_path) ids = [r["id"] for r in rows] assert m1.id in ids assert m2.id in ids def test_list_sessions_required_keys(tmp_path): db = _make_db() create_session("qualsiasi domanda", db, tmp_path) rows = _list_sessions(tmp_path) assert len(rows) >= 1 required = {"id", "status", "question", "summary", "created_at", "updated_at", "author"} assert required.issubset(rows[0].keys()) def test_list_sessions_sorted_desc(tmp_path): db = _make_db() create_session("prima", db, tmp_path) create_session("seconda", db, tmp_path) rows = _list_sessions(tmp_path) dates = [r["created_at"] for r in rows] assert dates == sorted(dates, reverse=True) def test_list_sessions_empty_dir(tmp_path): rows = _list_sessions(tmp_path) assert rows == [] # --------------------------------------------------------------------------- # CLI: tht session list --json # --------------------------------------------------------------------------- def test_cli_list_json_valid(tmp_path, monkeypatch): db = _make_db() m = create_session("test question", db, tmp_path) # Patch _load_config_or_exit so we don't need a real config file from tht.cli import session_cmd class FakePaths: sessions = tmp_path class FakeCfg: paths = FakePaths() monkeypatch.setattr(session_cmd, "_load_config_or_exit", lambda _: FakeCfg()) runner = CliRunner() result = runner.invoke(session_app, ["list", "--json"]) assert result.exit_code == 0, result.output rows = json.loads(result.output) assert isinstance(rows, list) ids = [r["id"] for r in rows] assert m.id in ids def test_cli_list_human_no_json_noise(tmp_path, monkeypatch): """Human mode must not emit JSON.""" db = _make_db() create_session("human question", db, tmp_path) from tht.cli import session_cmd class FakePaths: sessions = tmp_path class FakeCfg: paths = FakePaths() monkeypatch.setattr(session_cmd, "_load_config_or_exit", lambda _: FakeCfg()) runner = CliRunner() result = runner.invoke(session_app, ["list"]) assert result.exit_code == 0 # Must not be parseable as JSON array with pytest.raises(json.JSONDecodeError): json.loads(result.output) # --------------------------------------------------------------------------- # CLI: tht session show --json # --------------------------------------------------------------------------- def test_cli_show_json_valid(tmp_path, monkeypatch): db = _make_db() m = create_session("domanda show json", db, tmp_path) from tht.cli import session_cmd class FakePaths: sessions = tmp_path class FakeCfg: paths = FakePaths() database = db monkeypatch.setattr(session_cmd, "_load_config_or_exit", lambda _: FakeCfg()) runner = CliRunner() result = runner.invoke(session_app, ["show", m.id, "--json"]) assert result.exit_code == 0, result.output data = json.loads(result.output) assert data["id"] == m.id assert "phase" in data assert "has_schema_linking" in data # Pin by_alias=True: the alias-sensitive field must serialize as "schema", not "db_schema". assert "schema" in data assert "db_schema" not in data def test_cli_show_json_stdout_pristine(tmp_path, monkeypatch): """--json must produce only valid JSON on stdout (no ANSI color codes or extra lines).""" db = _make_db() m = create_session("pristine stdout test", db, tmp_path) from tht.cli import session_cmd class FakePaths: sessions = tmp_path class FakeCfg: paths = FakePaths() database = db monkeypatch.setattr(session_cmd, "_load_config_or_exit", lambda _: FakeCfg()) runner = CliRunner() result = runner.invoke(session_app, ["show", m.id, "--json"]) assert result.exit_code == 0, result.output # stdout must parse cleanly as JSON data = json.loads(result.output) assert isinstance(data, dict) def test_cli_retrieval_pack_emits_persisted_content(tmp_path, monkeypatch): db = _make_db() manifest = create_session("test question", db, tmp_path) expected = "# Retrieval pack\n\ncontenuto giĆ  pronto\n" (tmp_path / manifest.id / "retrieval_pack.md").write_text(expected) from tht.cli import session_cmd class FakePaths: sessions = tmp_path class FakeCfg: paths = FakePaths() monkeypatch.setattr(session_cmd, "_load_config_or_exit", lambda _: FakeCfg()) result = CliRunner().invoke(session_app, ["retrieval-pack", manifest.id]) assert result.exit_code == 0, result.output assert result.output == expected def test_cli_retrieval_pack_missing_is_clear_error(tmp_path, monkeypatch): db = _make_db() manifest = create_session("test question", db, tmp_path) from tht.cli import session_cmd class FakePaths: sessions = tmp_path class FakeCfg: paths = FakePaths() monkeypatch.setattr(session_cmd, "_load_config_or_exit", lambda _: FakeCfg()) result = CliRunner().invoke(session_app, ["retrieval-pack", manifest.id]) assert result.exit_code == 1 assert "retrieval pack non disponibile" in result.output