""" Tests for ConsistencyValidator - cross-agent contradiction detection. """ import pytest from app.core.consistency_validator import ConsistencyValidator from app.core.kg_schemas import ( Component, Requirement, ) from app.core.kg_service import KGService, reset_kg_service @pytest.fixture(autouse=True) def reset_service(): """Reset KGService singleton between tests.""" reset_kg_service() yield @pytest.fixture def empty_graph_dict() -> dict: return {"nodes": [], "edges": [], "kg_schema_version": 2} @pytest.fixture def validator() -> ConsistencyValidator: return ConsistencyValidator() def test_no_contradictions_empty_graph(validator): """Empty knowledge graph returns no contradictions.""" state = {"knowledge_graph": {}} assert validator.validate_all(state) == [] def test_no_contradictions_single_entity(validator, empty_graph_dict): """Single entity produces no contradictions.""" svc = KGService() state: dict = {"knowledge_graph": {}} g = empty_graph_dict g = svc.add_entity( g, Requirement(id="req-1", role="product_owner", content="Test requirement") ) state["knowledge_graph"] = svc.set_role_graph(state, "product_owner", g) result = validator.validate_all(state) assert result == [] def test_detects_property_conflict(validator, empty_graph_dict): """SA and DA write Component with same name but different technology.""" svc = KGService() state: dict = {"knowledge_graph": {}} # SA writes Component(name="AuthService", technology="FastAPI") g_sa = empty_graph_dict g_sa = svc.add_entity( g_sa, Component( id="comp-1", role="solution_architect", name="AuthService", technology="FastAPI", content="Auth service component", ), ) state["knowledge_graph"] = svc.set_role_graph(state, "solution_architect", g_sa) # DA writes Component(name="AuthService", technology="Express.js") g_da = empty_graph_dict g_da = svc.add_entity( g_da, Component( id="comp-2", role="data_architect", name="AuthService", technology="Express.js", content="Auth service data layer", ), ) state["knowledge_graph"] = svc.set_role_graph(state, "data_architect", g_da) result = validator.validate_all(state) assert len(result) >= 1 assert any(c["type"] == "property_conflict" for c in result) found = [c for c in result if c["type"] == "property_conflict"] conflict_detail = found[0].get("detail", "") assert "AuthService" in conflict_detail def test_detects_missing_dependency(validator, empty_graph_dict): """SA writes Component referencing PaymentService with no entity for it.""" svc = KGService() state: dict = {"knowledge_graph": {}} g_sa = empty_graph_dict g_sa = svc.add_entity( g_sa, Component( id="comp-1", role="solution_architect", name="OrderService", technology="FastAPI", content="Order service depends on PaymentService for processing", ), ) state["knowledge_graph"] = svc.set_role_graph(state, "solution_architect", g_sa) result = validator.validate_all(state) assert len(result) >= 1 assert any(c["type"] == "missing_dependency" for c in result) found = [c for c in result if c["type"] == "missing_dependency"] assert "PaymentService" in found[0].get("detail", "") def test_no_false_positive_for_same_role(validator, empty_graph_dict): """Two Requirements from same role are NOT contradictions.""" svc = KGService() state: dict = {"knowledge_graph": {}} g_po = empty_graph_dict g_po = svc.add_entity( g_po, Requirement(id="req-1", role="product_owner", content="Requirement one"), ) g_po = svc.add_entity( g_po, Requirement(id="req-2", role="product_owner", content="Requirement two"), ) state["knowledge_graph"] = svc.set_role_graph(state, "product_owner", g_po) result = validator.validate_all(state) # Same-role entities should not produce contradictions for c in result: roles = c.get("roles", []) if len(roles) == 1 and "product_owner" in roles: pytest.fail(f"Same-role contradiction detected: {c}") # This is fine - at minimum no property_conflicts should appear property_conflicts = [c for c in result if c["type"] == "property_conflict"] assert property_conflicts == [] def test_format_warnings_section(validator): """Verify formatted output contains expected content.""" contradictions = [ { "type": "property_conflict", "entity_ids": ["comp-1", "comp-2"], "roles": ["solution_architect", "data_architect"], "detail": "Conflicting properties for Component:AuthService", "severity": "high", "conflicts": [ { "property": "Technology stack", "values": { "solution_architect": "FastAPI", "data_architect": "Express.js", }, } ], } ] output = validator.format_warnings_section(contradictions) assert "Consistency Warnings" in output assert "AuthService" in output assert "FastAPI" in output assert "Express.js" in output assert "solution_architect" in output assert "data_architect" in output assert "high" in output def test_format_warnings_section_empty(validator): """Empty contradictions produce empty string.""" assert validator.format_warnings_section([]) == ""