"""Tests for the read-only inspection commands: topics, claims, show, why, evidence, and ask — both human-readable and ``--json`` modes.""" from __future__ import annotations import json from helpers import ( claim_inputs, get_claim, get_claims, run_cli, run_ok, ) # --------------------------------------------------------------------------- # # topics # --------------------------------------------------------------------------- # def test_topics_json_lists_topics_with_counts(home): data = json.loads(run_ok(home, "topics", "--json")) if isinstance(data, dict) and isinstance(data.get("topics"), list): data = data["topics"] assert isinstance(data, list) and data, "seeded node should have topics" for entry in data: assert entry.get("topic"), f"topic entry missing name: {entry!r}" if "count" in entry: assert isinstance(entry["count"], int) and entry["count"] >= 1 def test_topics_human_output_mentions_each_topic(home): out = run_ok(home, "topics") claims = get_claims(home) for topic in {str(c["topic"]) for c in claims}: assert topic in out, f"human topics output should mention {topic!r}" # --------------------------------------------------------------------------- # # claims # --------------------------------------------------------------------------- # def test_claims_json_shape(home): claims = get_claims(home) assert claims, "seeded node should have derived claims" ids = [str(c["id"]) for c in claims] assert len(ids) == len(set(ids)), "claim ids must be unique" for c in claims: assert c.get("topic"), f"claim missing topic: {c!r}" assert c.get("statement"), f"claim missing statement: {c!r}" conf = c.get("confidence") assert isinstance(conf, (int, float)), f"claim missing confidence: {c!r}" assert 0.0 <= float(conf) <= 1.0 assert str(c.get("status")) == "active", "default listing shows active claims" def test_claims_topic_filter_returns_subset(home): claims = get_claims(home) topic = str(claims[0]["topic"]) filtered = get_claims(home, "--topic", topic) assert filtered, f"filtering by an existing topic ({topic!r}) returns claims" assert all(str(c["topic"]) == topic for c in filtered) assert {str(c["id"]) for c in filtered} <= {str(c["id"]) for c in claims} def test_claims_all_flag_is_superset_of_default(home): default_ids = {str(c["id"]) for c in get_claims(home)} all_ids = {str(c["id"]) for c in get_claims(home, "--all")} assert default_ids <= all_ids def test_claims_human_output_lists_statements(home): out = run_ok(home, "claims") claims = get_claims(home) hits = sum(1 for c in claims if str(c["statement"]) in out) assert hits >= 1, "human claims listing should include claim statements" # --------------------------------------------------------------------------- # # show / why / evidence # --------------------------------------------------------------------------- # def test_show_json_roundtrips_claim_id(home): cid = str(get_claims(home)[0]["id"]) claim = get_claim(home, cid) assert str(claim["id"]) == cid def test_show_human_output_describes_claim(home): c = get_claims(home)[0] out = run_ok(home, "show", str(c["id"])) assert out.strip(), "show must produce output" assert str(c["statement"]) in out or str(c["id"])[:8] in out def test_why_explains_derivation(home): cid = str(get_claims(home)[0]["id"]) out = run_ok(home, "why", cid) assert out.strip(), "why must produce an explanation" lowered = out.lower() assert any( word in lowered for word in ("evidence", "derived", "input", "source", "because", "rule") ), f"why output should explain the derivation, got:\n{out}" def test_evidence_drilldown_from_claim_inputs(home): claims = get_claims(home, "--all") claim_ids = {str(c["id"]) for c in claims} candidates = [] for c in claims: for ref in claim_inputs(c): if ref not in claim_ids: candidates.append(ref) assert candidates, "seeded claims should reference raw evidence inputs" resolved = False for ref in dict.fromkeys(candidates): # de-dupe, keep order code, out, _err = run_cli(home, "evidence", ref) if code == 0 and out.strip(): resolved = True break assert resolved, "at least one claim input should resolve as raw evidence" # --------------------------------------------------------------------------- # # ask # --------------------------------------------------------------------------- # def test_ask_canonical_question_mentions_topics(home): out = run_ok(home, "ask", "what do you know about me and why?") assert out.strip(), "ask must answer" topics = {str(c["topic"]).lower() for c in get_claims(home)} assert any(t in out.lower() for t in topics), ( "the answer should be organized around the claim topics" ) # --------------------------------------------------------------------------- # # error paths # --------------------------------------------------------------------------- # def test_unknown_claim_id_fails_cleanly(home): code, _out, _err = run_cli(home, "show", "not-a-real-claim-id") assert code != 0 def test_unknown_evidence_id_fails_cleanly(home): code, _out, _err = run_cli(home, "evidence", "not-a-real-evidence-id") assert code != 0 def test_unseeded_node_has_no_claims(empty_home): claims = get_claims(empty_home) assert claims == []