@@ -0,0 +1,376 @@
1 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """Tests for navegador.enrichment — EnrichmentResult, FrameworkEnricher, and CLI."""
2 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
3 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import json
4 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from unittest.mock import MagicMock, call, patch
5 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
6 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import pytest
7 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from click.testing import CliRunner
8 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
9 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.enrichment import EnrichmentResult, FrameworkEnricher
10 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.enrichment.base import FrameworkEnricher as FrameworkEnricherBase
11 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.graph.store import GraphStore
12 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
13 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
14 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── Helpers ───────────────────────────────────────────────────────────────────
15 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
16 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
17 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def _mock_store(result_set=None):
18 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """Return a GraphStore backed by a mock FalkorDB graph."""
19 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ client = MagicMock()
20 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ graph = MagicMock()
21 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ graph.query.return_value = MagicMock(result_set=result_set)
22 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ client.select_graph.return_value = graph
23 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = GraphStore(client)
24 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return store
25 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
26 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
27 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class MockEnricher(FrameworkEnricher):
28 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """Concrete enricher used in tests."""
29 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
30 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @property
31 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def framework_name(self) -> str:
32 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return "mock"
33 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
34 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @property
35 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def detection_patterns(self) -> list[str]:
36 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return ["mock_module", "mock_settings.py"]
37 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
38 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def enrich(self) -> EnrichmentResult:
39 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result = EnrichmentResult()
40 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result.promoted = 3
41 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result.edges_added = 2
42 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result.patterns_found = {"mock_view": 3, "mock_model": 0}
43 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return result
44 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
45 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
46 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── EnrichmentResult defaults ─────────────────────────────────────────────────
47 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
48 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
49 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestEnrichmentResult:
50 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_promoted_defaults_to_zero(self):
51 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r = EnrichmentResult()
52 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert r.promoted == 0
53 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
54 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_edges_added_defaults_to_zero(self):
55 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r = EnrichmentResult()
56 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert r.edges_added == 0
57 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
58 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_patterns_found_defaults_to_empty_dict(self):
59 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r = EnrichmentResult()
60 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert r.patterns_found == {}
61 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
62 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_attributes_are_mutable(self):
63 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r = EnrichmentResult()
64 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r.promoted = 5
65 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r.edges_added = 10
66 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r.patterns_found["view"] = 7
67 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert r.promoted == 5
68 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert r.edges_added == 10
69 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert r.patterns_found["view"] == 7
70 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
71 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_instances_are_independent(self):
72 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r1 = EnrichmentResult()
73 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r2 = EnrichmentResult()
74 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ r1.patterns_found["x"] = 1
75 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "x" not in r2.patterns_found
76 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
77 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
78 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── FrameworkEnricher.detect() ────────────────────────────────────────────────
79 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
80 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
81 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestDetect:
82 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_true_when_pattern_matches(self):
83 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(result_set=[[1]])
84 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
85 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert enricher.detect() is True
86 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
87 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_false_when_no_match(self):
88 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(result_set=[[0]])
89 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
90 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert enricher.detect() is False
91 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
92 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_false_when_result_set_is_empty(self):
93 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(result_set=[])
94 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
95 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert enricher.detect() is False
96 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
97 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_false_when_result_set_is_none(self):
98 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(result_set=None)
99 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
100 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert enricher.detect() is False
101 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
102 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_true_on_second_pattern_if_first_misses(self):
103 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """detect() short-circuits on the first positive match, but we verify
104 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ it tries subsequent patterns when earlier ones return zero."""
105 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ call_count = 0
106 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
107 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def _side_effect(cypher, params):
108 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ nonlocal call_count
109 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ call_count += 1
110 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # First pattern returns 0, second returns 1
111 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ count = 1 if call_count >= 2 else 0
112 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return MagicMock(result_set=[[count]])
113 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
114 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ client = MagicMock()
115 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ graph = MagicMock()
116 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ graph.query.side_effect = _side_effect
117 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ client.select_graph.return_value = graph
118 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = GraphStore(client)
119 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
120 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
121 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert enricher.detect() is True
122 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert call_count == 2
123 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
124 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_detect_queries_each_pattern_with_correct_param(self):
125 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(result_set=[[0]])
126 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
127 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher.detect()
128 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
129 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ calls = store._graph.query.call_args_list
130 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # Two patterns → two queries
131 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert len(calls) == 2
132 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ _, kwargs0 = calls[0]
133 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t — E"""Tests for navegador.enrichment — EnrichmentResult, FrameworkEnricher, and CLI."""
134 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
135 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import json
136 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from unittest.mock import MagicMock, call, patch
137 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
138 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import pytest
139 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from click.testing import CliRunner
140 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
141 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ frompattern": "mock_module"}
142 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ pattern": "mock_settings.py"}
143 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
144 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_stops_early_when_first_pattern_matches(self):
145 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(result_set=[[5]])
146 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
147 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert enricher.detect() is True
148 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # Should only query once (short-circuit on first match)
149 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert store._graph.query.call_count == 1
150 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
151 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
152 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── FrameworkEnricher._promote_node() ────────────────────────────────────────
153 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
154 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
155 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestPromoteNode:
156 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_calls_store_query_with_correct_cypher_and_params(self):
157 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
158 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
159 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._promote_node("MyView", "app/views.py", "DjangoView")
160 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
161 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store._graph.query.assert_called_once()
162 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, params = store._graph.query.call_args[0]
163 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "SET n.semantic_type = $semantic_type" in cypher
164 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "n.name = $name" in cypher
165 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "n.file_path = $file_path" in cypher
166 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["name"] == "MyView"
167 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["file_path"] == "app/views.py"
168 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["semantic_type"] == "DjangoView"
169 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
170 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_extra_props_appended_to_set_clause(self):
171 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
172 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
173 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._promote_node(
174 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "MyModel", "app/models.py", "DjangoModel", props={"table": "my_table"}
175 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ )
176 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
177 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, params = store._graph.query.call_args[0]
178 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "n.table = $table" in cypher
179 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["table"] == "my_table"
180 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
181 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_no_extra_props_produces_clean_cypher(self):
182 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
183 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
184 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._promote_node("Fn", "a.py", "endpoint")
185 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
186 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, _ = store._graph.query.call_args[0]
187 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # Should not have a trailing comma or extra SET clause pieces
188 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert cypher.count("SET") == 1
189 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "None" not in cypher
190 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
191 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_multiple_extra_props(self):
192 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
193 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
194 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._promote_node(
195 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "Router", "routes.py", "FastAPIRouter", props={"prefix": "/api", "version": "v1"}
196 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ )
197 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
198 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, params = store._graph.query.call_args[0]
199 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "n.prefix = $prefix" in cypher
200 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "n.version = $version" in cypher
201 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["prefix"] == "/api"
202 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["version"] == "v1"
203 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
204 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
205 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── FrameworkEnricher._add_semantic_edge() ────────────────────────────────────
206 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
207 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
208 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestAddSemanticEdge:
209 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_calls_store_query_with_correct_cypher_and_params(self):
210 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
211 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
212 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._add_semantic_edge("UserView", "HANDLES", "UserModel")
213 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
214 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store._graph.query.assert_called_once()
215 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, params = store._graph.query.call_args[0]
216 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "MERGE (a)-[r:HANDLES]->(b)" in cypher
217 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "a.name = $from_name" in cypher
218 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "b.name = $to_name" in cypher
219 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["from_name"] == "UserView"
220 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["to_name"] == "UserModel"
221 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
222 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_extra_props_produce_set_clause(self):
223 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
224 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
225 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._add_semantic_edge(
226 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ "ViewA", "CALLS", "ViewB", props={"weight": 1, "layer": "http"}
227 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ )
228 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
229 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, params = store._graph.query.call_args[0]
230 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "SET" in cypher
231 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "r.weight = $weight" in cypher
232 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "r.layer = $layer" in cypher
233 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["weight"] == 1
234 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert params["layer"] == "http"
235 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
236 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_no_extra_props_no_set_clause(self):
237 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
238 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
239 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._add_semantic_edge("A", "LINKS", "B")
240 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
241 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, _ = store._graph.query.call_args[0]
242 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "SET" not in cypher
243 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
244 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_edge_type_is_interpolated(self):
245 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
246 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
247 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher._add_semantic_edge("X", "CUSTOM_EDGE_TYPE", "Y")
248 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
249 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher, _ = store._graph.query.call_args[0]
250 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "CUSTOM_EDGE_TYPE" in cypher
251 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
252 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
253 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── MockEnricher.enrich() contract ────────────────────────────────────────────
254 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
255 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
256 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestMockEnricherEnrich:
257 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_enrichment_result(self):
258 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
259 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
260 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result = enricher.enrich()
261 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert isinstance(result, EnrichmentResult)
262 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
263 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_result_values(self):
264 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
265 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ enricher = MockEnricher(store)
266 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result = enricher.enrich()
267 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert result.promoted == 3
268 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert result.edges_added == 2
269 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert result.patterns_found == {"mock_view": 3, "mock_model": 0}
270 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
271 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_framework_name(self):
272 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
273 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert MockEnricher(store).framework_name == "mock"
274 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
275 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_detection_patterns(self):
276 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
277 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ patterns = MockEnricher(store).detection_patterns
278 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "mock_module" in patterns
279 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "mock_settings.py" in patterns
280 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
281 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
282 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── Abstract enforcement ───────────────────────────────────────────────────────
283 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
284 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
285 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestAbstractEnforcement:
286 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_cannot_instantiate_base_class_directly(self):
287 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store()
288 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with pytest.raises(TypeError):
289 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ FrameworkEnricher(store) # type: ignore[abstract]
290 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
291 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_subclass_missing_framework_name_raises(self):
292 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with pytest.raises(TypeError):
293 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
294 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class Incomplete(FrameworkEnricher):
295 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @property
296 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def detection_patterns(self):
297 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return []
298 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
299 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def enrich(self):
300 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return EnrichmentResult()
301 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
302 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Incomplete(_mock_store())
303 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
304 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_subclass_missing_detection_patterns_raises(self):
305 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with pytest.raises(TypeError):
306 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
307 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class Incomplete(FrameworkEnricher):
308 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @property
309 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def framework_name(self):
310 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return "x"
311 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
312 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def enrich(self):
313 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return EnrichmentResult()
314 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
315 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Incomplete(_mock_store())
316 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
317 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_subclass_missing_enrich_raises(self):
318 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with pytest.raises(TypeError):
319 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
320 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class Incomplete(FrameworkEnricher):
321 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @property
322 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def framework_name(self):
323 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return "x"
324 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
325 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ @property
326 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def detection_patterns(self):
327 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return []
328 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
329 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Incomplete(_mock_store())
330 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
331 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
332 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── CLI: navegador enrich ──────────────────────────────────────────────────────
333 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
334 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
335 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestEnrichCLI:
336 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def _runner(self):
337 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return CliRunner()
338 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
339 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_no_frameworks_detected_message(self):
340 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.cli.commands import main
341 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
342 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ runner = self._runner()
343 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with patch("navegador.cli.commands._get_store", return_value=_mock_store()):
344 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # No enricher modules in package yet — message should appear
345 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result = runner.invoke(main, ["enrich"])
346 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert result.exit_code == 0
347 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "No frameworks detected" in result.output
348 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
349 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_unknown_framework_exits_nonzero(self):
350 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.cli.commands import main
351 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
352 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ runner = self._runner()
353 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with patch("navegador.cli.commands._get_store", return_value=_mock_store()):
354 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result = runner.invoke(main, ["enrich", "--framework", "nonexistent_xyz"])
355 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert result.exit_code != 0
356 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
357 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_json_flag_produces_empty_object_when_no_frameworks(self):
358 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.cli.commands import main
359 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
360 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ runner = self._runner()
361 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with patch("navegador.cli.commands._get_store", return_value=_mock_store()):
362 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result = runner.invoke(main, ["enrich", "--json"])
363 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert result.exit_code == 0
364 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ data = json.loads(result.output)
365 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert isinstance(data, dict)
366 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
367 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_enrich_command_exists_in_main_group(self):
368 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.cli.commands import main
369 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
370 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "enrich" in main.commands
371 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
372 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_enrich_runs_enricher_when_framework_registered(self):
373 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """Patch the enrichment package discovery to inject MockEnricher."""
374 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import pkgutil
375 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
376 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from n