Navegador

navegador / tests / test_enrichment_laravel.py
Blame History Raw 273 lines
1
"""Tests for navegador.enrichment.laravel — LaravelEnricher."""
2
3
from unittest.mock import MagicMock
4
5
import pytest
6
7
from navegador.enrichment.base import EnrichmentResult, FrameworkEnricher
8
from navegador.enrichment.laravel import LaravelEnricher
9
from navegador.graph.store import GraphStore
10
11
12
# ── Helpers ───────────────────────────────────────────────────────────────────
13
14
15
def _mock_store(result_set=None):
16
"""Return a GraphStore backed by a mock FalkorDB graph."""
17
client = MagicMock()
18
graph = MagicMock()
19
graph.query.return_value = MagicMock(result_set=result_set)
20
client.select_graph.return_value = graph
21
return GraphStore(client)
22
23
24
def _store_with_side_effect(side_effect):
25
"""Return a GraphStore whose graph.query uses a side_effect callable."""
26
client = MagicMock()
27
graph = MagicMock()
28
graph.query.side_effect = side_effect
29
client.select_graph.return_value = graph
30
return GraphStore(client)
31
32
33
# ── Identity / contract ───────────────────────────────────────────────────────
34
35
36
class TestLaravelEnricherIdentity:
37
def test_framework_name(self):
38
store = _mock_store()
39
assert LaravelEnricher(store).framework_name == "laravel"
40
41
def test_is_framework_enricher_subclass(self):
42
assert issubclass(LaravelEnricher, FrameworkEnricher)
43
44
def test_detection_patterns_contains_illuminate(self):
45
store = _mock_store()
46
assert "Illuminate" in LaravelEnricher(store).detection_patterns
47
48
def test_detection_files_contains_artisan(self):
49
store = _mock_store()
50
assert "artisan" in LaravelEnricher(store).detection_files
51
52
def test_detection_patterns_has_one_entry(self):
53
store = _mock_store()
54
assert len(LaravelEnricher(store).detection_patterns) == 1
55
56
def test_detection_files_is_nonempty(self):
57
store = _mock_store()
58
assert len(LaravelEnricher(store).detection_files) >= 1
59
60
61
# ── enrich() return type ──────────────────────────────────────────────────────
62
63
64
class TestLaravelEnricherEnrichReturnType:
65
def test_returns_enrichment_result(self):
66
store = _mock_store(result_set=[])
67
result = LaravelEnricher(store).enrich()
68
assert isinstance(result, EnrichmentResult)
69
70
def test_result_has_promoted_attribute(self):
71
store = _mock_store(result_set=[])
72
result = LaravelEnricher(store).enrich()
73
assert hasattr(result, "promoted")
74
75
def test_result_has_edges_added_attribute(self):
76
store = _mock_store(result_set=[])
77
result = LaravelEnricher(store).enrich()
78
assert hasattr(result, "edges_added")
79
80
def test_result_has_patterns_found_attribute(self):
81
store = _mock_store(result_set=[])
82
result = LaravelEnricher(store).enrich()
83
assert hasattr(result, "patterns_found")
84
85
86
# ── enrich() with no matching nodes ──────────────────────────────────────────
87
88
89
class TestLaravelEnricherNoMatches:
90
def test_promoted_is_zero_when_no_nodes(self):
91
store = _mock_store(result_set=[])
92
result = LaravelEnricher(store).enrich()
93
assert result.promoted == 0
94
95
def test_all_pattern_counts_zero_when_no_nodes(self):
96
store = _mock_store(result_set=[])
97
result = LaravelEnricher(store).enrich()
98
for key in ("controllers", "routes", "jobs", "policies", "models"):
99
assert result.patterns_found[key] == 0
100
101
def test_patterns_found_has_five_keys(self):
102
store = _mock_store(result_set=[])
103
result = LaravelEnricher(store).enrich()
104
assert set(result.patterns_found.keys()) == {
105
"controllers", "routes", "jobs", "policies", "models"
106
}
107
108
109
# ── enrich() with matching nodes ─────────────────────────────────────────────
110
111
112
class TestLaravelEnricherWithMatches:
113
def _make_store_for_fragment(self, target_fragment, rows):
114
"""Return a store that returns `rows` only when the fragment matches."""
115
116
def side_effect(cypher, params):
117
fragment = params.get("fragment", "")
118
if fragment == target_fragment:
119
return MagicMock(result_set=rows)
120
return MagicMock(result_set=[])
121
122
return _store_with_side_effect(side_effect)
123
124
def test_controller_promoted(self):
125
store = self._make_store_for_fragment(
126
"Controllers/",
127
[["UserController", "app/Http/Controllers/UserController.php"]],
128
)
129
result = LaravelEnricher(store).enrich()
130
assert result.patterns_found["controllers"] == 1
131
assert result.promoted >= 1
132
133
def test_route_promoted(self):
134
store = self._make_store_for_fragment(
135
"routes/",
136
[["web", "routes/web.php"]],
137
)
138
result = LaravelEnricher(store).enrich()
139
assert result.patterns_found["routes"] == 1
140
assert result.promoted >= 1
141
142
def test_job_promoted(self):
143
store = self._make_store_for_fragment(
144
"Jobs/",
145
[["SendEmailJob", "app/Jobs/SendEmailJob.php"]],
146
)
147
result = LaravelEnricher(store).enrich()
148
assert result.patterns_found["jobs"] == 1
149
assert result.promoted >= 1
150
151
def test_policy_promoted(self):
152
store = self._make_store_for_fragment(
153
"Policies/",
154
[["UserPolicy", "app/Policies/UserPolicy.php"]],
155
)
156
result = LaravelEnricher(store).enrich()
157
assert result.patterns_found["policies"] == 1
158
assert result.promoted >= 1
159
160
def test_model_promoted_via_model_keyword(self):
161
store = self._make_store_for_fragment(
162
"Model",
163
[["User", "app/Models/User.php"]],
164
)
165
result = LaravelEnricher(store).enrich()
166
assert result.patterns_found["models"] == 1
167
assert result.promoted >= 1
168
169
def test_promoted_count_accumulates_across_types(self):
170
rows_map = {
171
"Controllers/": [
172
["UserController", "app/Http/Controllers/UserController.php"],
173
["PostController", "app/Http/Controllers/PostController.php"],
174
],
175
"routes/": [["web", "routes/web.php"]],
176
"Jobs/": [],
177
"Policies/": [["UserPolicy", "app/Policies/UserPolicy.php"]],
178
"Model": [],
179
}
180
181
def side_effect(cypher, params):
182
fragment = params.get("fragment", "")
183
return MagicMock(result_set=rows_map.get(fragment, []))
184
185
store = _store_with_side_effect(side_effect)
186
result = LaravelEnricher(store).enrich()
187
assert result.promoted == 4
188
assert result.patterns_found["controllers"] == 2
189
assert result.patterns_found["routes"] == 1
190
assert result.patterns_found["policies"] == 1
191
assert result.patterns_found["jobs"] == 0
192
assert result.patterns_found["models"] == 0
193
194
def test_promote_node_called_with_laravel_controller_type(self):
195
store = self._make_store_for_fragment(
196
"Controllers/",
197
[["UserController", "app/Http/Controllers/UserController.php"]],
198
)
199
LaravelEnricher(store).enrich()
200
calls = [str(c) for c in store._graph.query.call_args_list]
201
promote_calls = [c for c in calls if "semantic_type" in c and "LaravelController" in c]
202
assert len(promote_calls) >= 1
203
204
def test_promote_node_called_with_laravel_model_type(self):
205
store = self._make_store_for_fragment(
206
"Model",
207
[["User", "app/Models/User.php"]],
208
)
209
LaravelEnricher(store).enrich()
210
calls = [str(c) for c in store._graph.query.call_args_list]
211
promote_calls = [c for c in calls if "semantic_type" in c and "LaravelModel" in c]
212
assert len(promote_calls) >= 1
213
214
def test_promote_node_called_with_laravel_route_type(self):
215
store = self._make_store_for_fragment(
216
"routes/",
217
[["web", "routes/web.php"]],
218
)
219
LaravelEnricher(store).enrich()
220
calls = [str(c) for c in store._graph.query.call_args_list]
221
promote_calls = [c for c in calls if "semantic_type" in c and "LaravelRoute" in c]
222
assert len(promote_calls) >= 1
223
224
def test_promote_node_called_with_laravel_job_type(self):
225
store = self._make_store_for_fragment(
226
"Jobs/",
227
[["SendEmailJob", "app/Jobs/SendEmailJob.php"]],
228
)
229
LaravelEnricher(store).enrich()
230
calls = [str(c) for c in store._graph.query.call_args_list]
231
promote_calls = [c for c in calls if "semantic_type" in c and "LaravelJob" in c]
232
assert len(promote_calls) >= 1
233
234
def test_promote_node_called_with_laravel_policy_type(self):
235
store = self._make_store_for_fragment(
236
"Policies/",
237
[["UserPolicy", "app/Policies/UserPolicy.php"]],
238
)
239
LaravelEnricher(store).enrich()
240
calls = [str(c) for c in store._graph.query.call_args_list]
241
promote_calls = [c for c in calls if "semantic_type" in c and "LaravelPolicy" in c]
242
assert len(promote_calls) >= 1
243
244
def test_model_query_uses_fragment_param_named_fragment(self):
245
"""Model detection must pass fragment='Model', not an 'annotation' key."""
246
captured = []
247
248
def side_effect(cypher, params):
249
captured.append(dict(params))
250
return MagicMock(result_set=[])
251
252
store = _store_with_side_effect(side_effect)
253
LaravelEnricher(store).enrich()
254
model_queries = [p for p in captured if p.get("fragment") == "Model"]
255
assert len(model_queries) >= 1
256
257
258
# ── detect() integration ──────────────────────────────────────────────────────
259
260
261
class TestLaravelEnricherDetect:
262
def test_detect_true_when_artisan_present(self):
263
store = _mock_store(result_set=[[1]])
264
assert LaravelEnricher(store).detect() is True
265
266
def test_detect_false_when_no_patterns_match(self):
267
store = _mock_store(result_set=[[0]])
268
assert LaravelEnricher(store).detect() is False
269
270
def test_detect_false_on_empty_result_set(self):
271
store = _mock_store(result_set=[])
272
assert LaravelEnricher(store).detect() is False
273

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button