feat(harness): tht session documents --json read command

This commit is contained in:
2026-06-29 12:25:40 +02:00
parent 38cd4889f4
commit 03053b0716
3 changed files with 100 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
"""Tests for `tht session documents --json` and build_documents."""
import json
from typer.testing import CliRunner
from tht.config import DatabaseConfig
from tht.session.store import build_documents, create_session
from tht.cli.session_cmd import session_app
def _db():
return DatabaseConfig(
database="testdb", user="u", password="p", # noqa: S106
**{"schema": "public"},
)
def test_build_documents_always_has_original_question(tmp_path):
m = create_session("quante ablazioni nel 2024", _db(), tmp_path)
docs = build_documents(m, tmp_path / m.id)
keys = [d["key"] for d in docs]
assert keys[0] == "question"
assert docs[0]["format"] == "text"
assert docs[0]["content"] == "quante ablazioni nel 2024"
# question.md is written by create_session -> revised_question present
assert "revised_question" in keys
def test_build_documents_includes_existing_artifacts_only(tmp_path):
m = create_session("q", _db(), tmp_path)
sdir = tmp_path / m.id
(sdir / "sql_final.sql").write_text("SELECT 1")
(sdir / "schema_linking.json").write_text('{"question":"q","candidates":[]}')
docs = {d["key"]: d for d in build_documents(m, sdir)}
assert docs["sql"]["format"] == "sql"
assert docs["sql"]["content"] == "SELECT 1"
assert docs["schema_linking"]["format"] == "schema-linking"
assert "validation_report" not in docs # not written
def test_cli_documents_json(tmp_path, monkeypatch):
m = create_session("q", _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())
res = CliRunner().invoke(session_app, ["documents", m.id, "--json"])
assert res.exit_code == 0, res.output
docs = json.loads(res.output)
assert isinstance(docs, list)
assert docs[0]["key"] == "question"
+19
View File
@@ -218,6 +218,25 @@ def delete_cmd(session_id: str = typer.Argument(...), config: Path = CONFIG_OPT)
typer.secho(f"OK: sessione {session_id} eliminata.", fg=typer.colors.GREEN)
@session_app.command("documents")
def documents_cmd(
session_id: str = typer.Argument(...),
json_out: bool = typer.Option(False, "--json", help="Emetti JSON su stdout (pristine)."),
config: Path = CONFIG_OPT,
) -> None:
"""Documenti di sola lettura della sessione (domanda, rivista, schema, SQL, report, decisioni)."""
from tht.session.store import build_documents
cfg = _load_config_or_exit(config)
manifest = load_session_or_exit(cfg, session_id)
docs = build_documents(manifest, session_dir(cfg, session_id))
if json_out:
typer.echo(json.dumps(docs, ensure_ascii=False, indent=2))
return
for d in docs:
typer.echo(f"[{d['phase']}] {d['title']} ({d['format']})")
def session_problems(cfg, session_id: str) -> list[str]:
"""Problemi del Blocco 3 (decisioni + schema_linking). Riusato da check e finalize."""
import json
+24
View File
@@ -164,3 +164,27 @@ def delete_session(session_id: str, sessions_root: Path) -> None:
"""Hard-delete the session directory. SessionError if it does not exist."""
load_session(session_id, sessions_root) # raises SessionError if absent
shutil.rmtree(sessions_root / session_id)
def build_documents(manifest: SessionManifest, session_dir: Path) -> list[dict]:
"""Ordered, read-only document bundle for the UI panel. Only documents that exist
on disk are returned. CTE artifacts (F6) are intentionally excluded (intermediate)."""
docs: list[dict] = [{
"phase": "—", "key": "question", "title": "Domanda originale",
"format": "text", "content": manifest.question,
}]
spec = [
("question.md", "F3", "revised_question", "Domanda rivista", "markdown"),
("schema_linking.json", "F4", "schema_linking", "Schema linking", "schema-linking"),
("sql_final.sql", "F7", "sql", "SQL finale", "sql"),
("validation_report.md", "finalize", "validation_report", "Report di validazione", "markdown"),
("review_decisions.jsonl", "—", "decisions", "Decisioni", "decisions"),
]
for filename, phase, key, title, fmt in spec:
path = session_dir / filename
if path.exists():
docs.append({
"phase": phase, "key": key, "title": title,
"format": fmt, "content": path.read_text(),
})
return docs