File size: 5,979 Bytes
8904f7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994a04c
 
 
8904f7b
994a04c
 
 
8904f7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994a04c
 
 
 
 
 
 
 
 
8904f7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994a04c
8904f7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994a04c
 
 
 
8904f7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994a04c
 
 
8904f7b
 
 
 
994a04c
 
 
8904f7b
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
"""Unit tests for the Repository Knowledge Graph architecture, builder, navigator, and endpoints."""

import sys
import os
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient

# Ensure root directory is in sys.path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from backend.api import app
from models.knowledge_graph import (
    KnowledgeGraph,
    KnowledgeGraphNode,
    KnowledgeGraphEdge,
    KnowledgeGraphSummary,
)
from services.knowledge_graph_builder import RepositoryKnowledgeGraphBuilder
from services.knowledge_graph_navigator import RepositoryKnowledgeGraphNavigator

client = TestClient(app)


def test_knowledge_graph_models() -> None:
    """Verifies that Knowledge Graph Pydantic models can be instantiated and validated."""
    node = KnowledgeGraphNode(
        id="repo1", type="repository", properties={"name": "test-repo"}
    )
    edge = KnowledgeGraphEdge(source="repo1", target="repo1::health", type="HAS_HEALTH")
    kg = KnowledgeGraph(
        repository_name="test-owner/test-repo", nodes=[node], edges=[edge]
    )

    assert kg.repository_name == "test-owner/test-repo"
    assert len(kg.nodes) == 1
    assert kg.nodes[0].id == "repo1"
    assert kg.edges[0].type == "HAS_HEALTH"


@patch("backend.dependencies.symbol_service")
@patch("backend.dependencies.graph_service")
@patch("backend.dependencies.repository_twin_builder")
def test_knowledge_graph_builder_and_navigator(mock_tb, mock_gs, mock_ss) -> None:
    """Verifies that KnowledgeGraphBuilder correctly composes state and navigator navigates correctly."""
    repo_name = "test-owner/test-repo"

    # Mock Twin
    mock_twin = MagicMock()
    mock_twin.metadata = {"tech_stack": ["Python"], "total_loc": 100}
    mock_twin.files = ["main.py", "utils.py"]
    mock_twin.health_summary = {"overall_score": 90.0, "grade": "A", "breakdown": {}}
    mock_twin.compliance_summary = {
        "status": "compliant",
        "reasons": [],
        "dead_code_ratio": 0.0,
    }
    mock_twin.architecture_summary = {
        "cycles_count": 0,
        "strongly_connected_components": 1,
    }
    mock_twin.symbols_summary = {"total_symbols": 0}
    mock_tb.build_twin.return_value = mock_twin

    # Mock Symbol Service
    mock_ss.load.return_value = MagicMock(symbols=[])

    # Mock Dependency Graph Service
    mock_dep_graph = MagicMock()
    mock_dep_graph.edges.return_value = [("main.py", "utils.py")]
    mock_gs.load_graph.return_value = mock_dep_graph

    # 1. Test Builder
    mock_cache = MagicMock()
    mock_cache.get.return_value = None
    builder = RepositoryKnowledgeGraphBuilder(
        twin_builder=mock_tb,
        cache=mock_cache,
    )

    kg = builder.build_graph(repo_name)
    assert kg.repository_name == repo_name

    # Assert nodes presence
    node_ids = {node.id for node in kg.nodes}
    assert repo_name in node_ids
    assert f"{repo_name}::health" in node_ids
    assert f"{repo_name}::compliance" in node_ids
    assert f"{repo_name}::main.py" in node_ids
    assert f"{repo_name}::utils.py" in node_ids

    # Assert edges presence
    edge_types = {edge.type for edge in kg.edges}
    assert "HAS_HEALTH" in edge_types
    assert "HAS_COMPLIANCE" in edge_types
    assert "CONTAINS" in edge_types
    assert "IMPORTS" in edge_types

    summary = builder.build_graph_summary(repo_name)
    assert summary.repository_name == repo_name
    assert summary.nodes_count > 0
    assert summary.node_types_breakdown["repository"] == 1

    # 2. Test Navigator
    navigator = RepositoryKnowledgeGraphNavigator(builder=builder)

    # find_node
    node = navigator.find_node(repo_name, repo_name)
    assert node is not None
    assert node.type == "repository"

    # find_neighbors
    neighbors = navigator.find_neighbors(repo_name, repo_name)
    neighbor_ids = {n.id for n in neighbors}
    assert f"{repo_name}::health" in neighbor_ids

    # find_shortest_path
    path = navigator.find_shortest_path(repo_name, repo_name, f"{repo_name}::main.py")
    assert len(path) == 2
    assert path[0] == repo_name
    assert path[1] == f"{repo_name}::main.py"

    # find_impact
    impact = navigator.find_impact(repo_name, f"{repo_name}::main.py")
    assert f"{repo_name}::utils.py" in impact


def test_knowledge_graph_endpoints() -> None:
    """Verifies that HTTP endpoints for knowledge-graph work correctly with mocks."""
    repo_name = "test-owner/test-repo"

    with (
        patch(
            "backend.routers.knowledge_graph.repository_knowledge_graph_builder"
        ) as mock_builder,
        patch("backend.routers.knowledge_graph.repository_knowledge_graph_navigator"),
    ):
        mock_builder.build_graph.return_value = KnowledgeGraph(
            repository_name=repo_name,
            nodes=[KnowledgeGraphNode(id=repo_name, type="repository")],
            edges=[],
        )
        mock_builder.build_graph_summary.return_value = KnowledgeGraphSummary(
            repository_name=repo_name,
            nodes_count=1,
            edges_count=0,
            node_types_breakdown={"repository": 1},
            edge_types_breakdown={},
        )
        mock_builder.twin_builder.store = {repo_name: {}}

        # Test full KG route
        response = client.get("/api/repositories/test-owner/test-repo/knowledge-graph")
        assert response.status_code == 200
        assert response.json()["repository_name"] == repo_name

        # Test KG summary route
        response_summary = client.get(
            "/api/repositories/test-owner/test-repo/knowledge-graph/summary"
        )
        assert response_summary.status_code == 200
        assert response_summary.json()["nodes_count"] == 1

        # Test versioned v1 route
        response_v1 = client.get(
            "/api/v1/repositories/test-owner/test-repo/knowledge-graph/summary"
        )
        assert response_v1.status_code == 200
        assert response_v1.json()["nodes_count"] == 1