"""Phase 5 tests — the 10 canonical engineering queries. Test fixtures seed a small p-test graph and exercise each query. The 3 killer queries (Q-006/009/011) get dedicated tests that verify they surface real gaps and DON'T false-positive on well-formed data. """ from __future__ import annotations import pytest from atocore.engineering.queries import ( all_gaps, decisions_affecting, evidence_chain, impact_analysis, orphan_requirements, recent_changes, requirements_for, risky_decisions, system_map, unsupported_claims, ) from atocore.engineering.service import ( create_entity, create_relationship, init_engineering_schema, ) from atocore.models.database import init_db @pytest.fixture def seeded_graph(tmp_data_dir): """Build a small engineering graph for query tests.""" init_db() init_engineering_schema() # Subsystem + components ss = create_entity("subsystem", "Optics", project="p-test") c1 = create_entity("component", "Primary Mirror", project="p-test") c2 = create_entity("component", "Diverger Lens", project="p-test") c_orphan = create_entity("component", "Unparented", project="p-test") create_relationship(c1.id, ss.id, "part_of") create_relationship(c2.id, ss.id, "part_of") # Requirements — one satisfied, one orphan r_ok = create_entity("requirement", "Surface figure < 25nm RMS", project="p-test") r_orphan = create_entity("requirement", "Measurement lambda/20", project="p-test") create_relationship(c1.id, r_ok.id, "satisfies") # Decisions d_ok = create_entity("decision", "Use Zerodur blank", project="p-test") d_risky = create_entity("decision", "Use external CGH", project="p-test") create_relationship(d_ok.id, ss.id, "affected_by_decision") # Assumption (flagged) — d_risky depends on it a_flagged = create_entity( "parameter", "Vendor lead time 6 weeks", project="p-test", properties={"flagged": True}, ) create_relationship(d_risky.id, a_flagged.id, "based_on_assumption") # Validation claim — one supported, one not v_ok = create_entity("validation_claim", "Margin is adequate", project="p-test") v_orphan = create_entity("validation_claim", "Thermal stability OK", project="p-test") result = create_entity("result", "FEA thermal sweep 2026-03", project="p-test") create_relationship(result.id, v_ok.id, "supports") # Material mat = create_entity("material", "Zerodur", project="p-test") create_relationship(c1.id, mat.id, "uses_material") return { "subsystem": ss, "component_1": c1, "component_2": c2, "orphan_component": c_orphan, "req_ok": r_ok, "req_orphan": r_orphan, "decision_ok": d_ok, "decision_risky": d_risky, "assumption_flagged": a_flagged, "claim_supported": v_ok, "claim_orphan": v_orphan, "result": result, "material": mat, } # --- Structure queries --- def test_system_map_returns_subsystem_with_components(seeded_graph): result = system_map("p-test") assert result["project"] == "p-test" assert len(result["subsystems"]) == 1 optics = result["subsystems"][0] assert optics["name"] == "Optics" comp_names = {c["name"] for c in optics["components"]} assert "Primary Mirror" in comp_names assert "Diverger Lens" in comp_names def test_system_map_reports_orphan_components(seeded_graph): result = system_map("p-test") names = {c["name"] for c in result["orphan_components"]} assert "Unparented" in names def test_system_map_includes_materials(seeded_graph): result = system_map("p-test") primary = next( c for s in result["subsystems"] for c in s["components"] if c["name"] == "Primary Mirror" ) assert "Zerodur" in primary["materials"] def test_decisions_affecting_whole_project(seeded_graph): result = decisions_affecting("p-test") names = {d["name"] for d in result["decisions"]} assert "Use Zerodur blank" in names assert "Use external CGH" in names def test_decisions_affecting_specific_subsystem(seeded_graph): ss_id = seeded_graph["subsystem"].id result = decisions_affecting("p-test", subsystem_id=ss_id) names = {d["name"] for d in result["decisions"]} # d_ok has edge to subsystem directly assert "Use Zerodur blank" in names def test_requirements_for_component(seeded_graph): c_id = seeded_graph["component_1"].id result = requirements_for(c_id) assert result["count"] == 1 assert result["requirements"][0]["name"] == "Surface figure < 25nm RMS" def test_recent_changes_includes_created_entities(seeded_graph): result = recent_changes("p-test", limit=100) actions = [c["action"] for c in result["changes"]] assert "created" in actions assert result["count"] > 0 # --- Killer queries --- def test_orphan_requirements_finds_unsatisfied(seeded_graph): result = orphan_requirements("p-test") names = {r["name"] for r in result["gaps"]} assert "Measurement lambda/20" in names # orphan assert "Surface figure < 25nm RMS" not in names # has SATISFIES edge def test_orphan_requirements_empty_when_all_satisfied(tmp_data_dir): init_db() init_engineering_schema() c = create_entity("component", "C", project="p-clean") r = create_entity("requirement", "R", project="p-clean") create_relationship(c.id, r.id, "satisfies") result = orphan_requirements("p-clean") assert result["count"] == 0 def test_risky_decisions_finds_flagged_assumptions(seeded_graph): result = risky_decisions("p-test") names = {d["decision_name"] for d in result["gaps"]} assert "Use external CGH" in names assert "Use Zerodur blank" not in names # has no flagged assumption def test_unsupported_claims_finds_orphan_claims(seeded_graph): result = unsupported_claims("p-test") names = {c["name"] for c in result["gaps"]} assert "Thermal stability OK" in names assert "Margin is adequate" not in names # has SUPPORTS edge def test_all_gaps_combines_the_three_killers(seeded_graph): result = all_gaps("p-test") assert result["orphan_requirements"]["count"] == 1 assert result["risky_decisions"]["count"] == 1 assert result["unsupported_claims"]["count"] == 1 def test_all_gaps_clean_project_reports_zero(tmp_data_dir): init_db() init_engineering_schema() create_entity("component", "alone", project="p-empty") result = all_gaps("p-empty") assert result["orphan_requirements"]["count"] == 0 assert result["risky_decisions"]["count"] == 0 assert result["unsupported_claims"]["count"] == 0 # --- Impact + evidence --- def test_impact_analysis_walks_outbound_edges(seeded_graph): c_id = seeded_graph["component_1"].id result = impact_analysis(c_id, max_depth=2) # Primary Mirror → SATISFIES → Requirement, → USES_MATERIAL → Material rel_types = {i["relationship"] for i in result["impacted"]} assert "satisfies" in rel_types assert "uses_material" in rel_types def test_evidence_chain_walks_inbound_provenance(seeded_graph): v_ok_id = seeded_graph["claim_supported"].id result = evidence_chain(v_ok_id) # The Result entity supports the claim via_types = {e["via"] for e in result["evidence_chain"]} assert "supports" in via_types source_names = {e["source_name"] for e in result["evidence_chain"]} assert "FEA thermal sweep 2026-03" in source_names