import json from pathlib import Path from app.services.code_kb.adapter import CodeKnowledgeBaseAdapter from app.services.code_kb.graph import CodeCallGraph from app.services.code_kb.schema import CodeFunctionEvidence, CodeSearchHit from app.services.consistency.comparator import ConsistencyComparator from app.services.consistency.schema import RequirementSnapshot from app.services.consistency.scorer import coverage_score def test_metadata_normalization_uses_aliases_and_graph_fields(): adapter = CodeKnowledgeBaseAdapter() graph_nodes = { "Function:Init": { "id": "Function:Init", "name": "Init", "file_path": "src/init.c", "start_line": 10, "end_line": 20, "signature": "void Init(void)", "logic_flow": "init flow", "raw_attributes": {"code_snippet": "void Init(void) {}", "includes": ["a.h"]}, } } evidence = adapter._normalize_metadata( {"node_id": "Function:Init", "function_name": "Init", "file_path": "src/init.c", "summary": "summary"}, graph_nodes, index_dimension=1024, ) assert evidence.name == "Init" assert evidence.file == "src/init.c" assert evidence.start_line == 10 assert evidence.code_snippet == "void Init(void) {}" assert evidence.embedding_dim == 1024 def test_call_graph_expands_metadata_edges(): center = CodeFunctionEvidence(node_id="Function:Center", name="Center", qualified_name="Center", file="a.c") caller = CodeFunctionEvidence( node_id="Function:Caller", name="Caller", qualified_name="Caller", file="a.c", calls=["Center"], ) callee = CodeFunctionEvidence(node_id="Function:Callee", name="Callee", qualified_name="Callee", file="a.c") center.calls = ["Callee"] graph = CodeCallGraph({}, [center, caller, callee]) context = graph.expand("Function:Center", max_hops=1) assert [item.name for item in context.callers] == ["Caller"] assert [item.name for item in context.callees] == ["Callee"] assert "Caller -> Center -> Callee" in context.call_chains def test_simple_vector_index_json_can_be_loaded(): vector_path = Path("uploads") / "test-simple-vector-index.faiss" vector_path.parent.mkdir(parents=True, exist_ok=True) try: vector_path.write_text( json.dumps( { "format": "simple_l2_vector_index", "dimension": 3, "vectors": [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]], } ), encoding="utf-8", ) adapter = CodeKnowledgeBaseAdapter() index = adapter._read_faiss_index(str(vector_path)) assert index.ntotal == 2 assert index.d == 3 finally: if vector_path.exists(): vector_path.unlink() def test_lexical_search_falls_back_to_function_metadata(): adapter = CodeKnowledgeBaseAdapter() adapter.functions = [ CodeFunctionEvidence( node_id="Function:InitAttEnv", name="InitAttEnv", qualified_name="InitAttEnv", file="AttEnvMod.c", summary="initializes attitude environment parameters", code_snippet="void InitAttEnv(void) {}", ), CodeFunctionEvidence( node_id="Function:MatrixProduct", name="MatrixProduct", qualified_name="MatrixProduct", file="AttMath.c", summary="matrix multiplication", ), ] hits = adapter._lexical_search_functions("InitAttEnv attitude environment", top_k=1, min_similarity=0.2) assert hits[0].evidence.name == "InitAttEnv" def test_parse_json_judgment_from_markdown_block(): raw = """```json {"verdict":"partial","confidence":0.6,"covered_points":["A"],"missing_points":["B"]} ```""" parsed = ConsistencyComparator.parse_json_judgment(raw) assert parsed["verdict"] == "partial" assert parsed["confidence"] == 0.6 def test_coverage_score_caps_missing_verdict(): requirement = RequirementSnapshot( requirement_uid="REQ-1", title="", description="init system", acceptance_criteria=["A", "B"], ) evidence = CodeFunctionEvidence( node_id="Function:Init", name="Init", qualified_name="Init", file="init.c", start_line=1, end_line=10, summary="init system", logic_flow="flow", code_snippet="code", ) score = coverage_score( requirement, [CodeSearchHit(evidence=evidence, similarity=0.95, distance=0.1, rank=1)], [], {"verdict": "missing", "missing_points": ["B"]}, ) assert score <= 0.25