146 lines
4.7 KiB
Python
146 lines
4.7 KiB
Python
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
|