Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| def reset_service(): | |
| """Reset KGService singleton between tests.""" | |
| reset_kg_service() | |
| yield | |
| def empty_graph_dict() -> dict: | |
| return {"nodes": [], "edges": [], "kg_schema_version": 2} | |
| 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([]) == "" | |