Navegador

navegador / tests / test_enrichment_rails.py
Blame History Raw 257 lines
1
"""Tests for navegador.enrichment.rails — RailsEnricher."""
2
3
from unittest.mock import MagicMock, call
4
5
import pytest
6
7
from navegador.enrichment.base import EnrichmentResult, FrameworkEnricher
8
from navegador.enrichment.rails import RailsEnricher
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 TestRailsEnricherIdentity:
37
def test_framework_name(self):
38
store = _mock_store()
39
assert RailsEnricher(store).framework_name == "rails"
40
41
def test_is_framework_enricher_subclass(self):
42
assert issubclass(RailsEnricher, FrameworkEnricher)
43
44
def test_detection_patterns_contains_rails(self):
45
store = _mock_store()
46
assert "rails" in RailsEnricher(store).detection_patterns
47
48
def test_detection_patterns_contains_active_record(self):
49
store = _mock_store()
50
assert "active_record" in RailsEnricher(store).detection_patterns
51
52
def test_detection_patterns_contains_action_controller(self):
53
store = _mock_store()
54
assert "action_controller" in RailsEnricher(store).detection_patterns
55
56
def test_detection_files_contains_gemfile(self):
57
store = _mock_store()
58
assert "Gemfile" in RailsEnricher(store).detection_files
59
60
def test_detection_patterns_has_three_entries(self):
61
store = _mock_store()
62
assert len(RailsEnricher(store).detection_patterns) == 3
63
64
65
# ── enrich() return type ──────────────────────────────────────────────────────
66
67
68
class TestRailsEnricherEnrichReturnType:
69
def test_returns_enrichment_result(self):
70
store = _mock_store(result_set=[])
71
result = RailsEnricher(store).enrich()
72
assert isinstance(result, EnrichmentResult)
73
74
def test_result_has_promoted_attribute(self):
75
store = _mock_store(result_set=[])
76
result = RailsEnricher(store).enrich()
77
assert hasattr(result, "promoted")
78
79
def test_result_has_edges_added_attribute(self):
80
store = _mock_store(result_set=[])
81
result = RailsEnricher(store).enrich()
82
assert hasattr(result, "edges_added")
83
84
def test_result_has_patterns_found_attribute(self):
85
store = _mock_store(result_set=[])
86
result = RailsEnricher(store).enrich()
87
assert hasattr(result, "patterns_found")
88
89
90
# ── enrich() with no matching nodes ──────────────────────────────────────────
91
92
93
class TestRailsEnricherNoMatches:
94
def test_promoted_is_zero_when_no_nodes(self):
95
store = _mock_store(result_set=[])
96
result = RailsEnricher(store).enrich()
97
assert result.promoted == 0
98
99
def test_all_pattern_counts_zero_when_no_nodes(self):
100
store = _mock_store(result_set=[])
101
result = RailsEnricher(store).enrich()
102
for key in ("controllers", "models", "routes", "jobs", "concerns"):
103
assert result.patterns_found[key] == 0
104
105
def test_patterns_found_has_five_keys(self):
106
store = _mock_store(result_set=[])
107
result = RailsEnricher(store).enrich()
108
assert set(result.patterns_found.keys()) == {
109
"controllers", "models", "routes", "jobs", "concerns"
110
}
111
112
113
# ── enrich() with matching nodes ─────────────────────────────────────────────
114
115
116
class TestRailsEnricherWithMatches:
117
def _make_store_for_fragment(self, target_fragment, rows):
118
"""Return a store that returns `rows` only when the query fragment matches."""
119
120
def side_effect(cypher, params):
121
fragment = params.get("fragment", "")
122
if fragment == target_fragment:
123
return MagicMock(result_set=rows)
124
return MagicMock(result_set=[])
125
126
return _store_with_side_effect(side_effect)
127
128
def test_controller_promoted(self):
129
store = self._make_store_for_fragment(
130
"controllers/", [["UsersController", "app/controllers/users_controller.rb"]]
131
)
132
result = RailsEnricher(store).enrich()
133
assert result.patterns_found["controllers"] == 1
134
assert result.promoted >= 1
135
136
def test_model_promoted(self):
137
store = self._make_store_for_fragment(
138
"models/", [["User", "app/models/user.rb"]]
139
)
140
result = RailsEnricher(store).enrich()
141
assert result.patterns_found["models"] == 1
142
assert result.promoted >= 1
143
144
def test_route_promoted(self):
145
store = self._make_store_for_fragment(
146
"routes.rb", [["routes", "config/routes.rb"]]
147
)
148
result = RailsEnricher(store).enrich()
149
assert result.patterns_found["routes"] == 1
150
assert result.promoted >= 1
151
152
def test_job_promoted(self):
153
store = self._make_store_for_fragment(
154
"jobs/", [["SendEmailJob", "app/jobs/send_email_job.rb"]]
155
)
156
result = RailsEnricher(store).enrich()
157
assert result.patterns_found["jobs"] == 1
158
assert result.promoted >= 1
159
160
def test_concern_promoted(self):
161
store = self._make_store_for_fragment(
162
"concerns/", [["Auditable", "app/models/concerns/auditable.rb"]]
163
)
164
result = RailsEnricher(store).enrich()
165
assert result.patterns_found["concerns"] == 1
166
assert result.promoted >= 1
167
168
def test_promoted_count_accumulates_across_types(self):
169
rows_map = {
170
"controllers/": [
171
["UsersController", "app/controllers/users_controller.rb"],
172
["PostsController", "app/controllers/posts_controller.rb"],
173
],
174
"models/": [["User", "app/models/user.rb"]],
175
"routes.rb": [],
176
"jobs/": [],
177
"concerns/": [],
178
}
179
180
def side_effect(cypher, params):
181
fragment = params.get("fragment", "")
182
return MagicMock(result_set=rows_map.get(fragment, []))
183
184
store = _store_with_side_effect(side_effect)
185
result = RailsEnricher(store).enrich()
186
assert result.promoted == 3
187
assert result.patterns_found["controllers"] == 2
188
assert result.patterns_found["models"] == 1
189
190
def test_promote_node_called_with_correct_semantic_type_for_controller(self):
191
store = self._make_store_for_fragment(
192
"controllers/", [["UsersController", "app/controllers/users_controller.rb"]]
193
)
194
RailsEnricher(store).enrich()
195
196
# The _promote_node path ultimately calls store.query with SET n.semantic_type
197
calls = [str(c) for c in store._graph.query.call_args_list]
198
promote_calls = [c for c in calls if "semantic_type" in c and "RailsController" in c]
199
assert len(promote_calls) >= 1
200
201
def test_promote_node_called_with_correct_semantic_type_for_model(self):
202
store = self._make_store_for_fragment(
203
"models/", [["User", "app/models/user.rb"]]
204
)
205
RailsEnricher(store).enrich()
206
207
calls = [str(c) for c in store._graph.query.call_args_list]
208
promote_calls = [c for c in calls if "semantic_type" in c and "RailsModel" in c]
209
assert len(promote_calls) >= 1
210
211
def test_promote_node_called_with_correct_semantic_type_for_job(self):
212
store = self._make_store_for_fragment(
213
"jobs/", [["SendEmailJob", "app/jobs/send_email_job.rb"]]
214
)
215
RailsEnricher(store).enrich()
216
217
calls = [str(c) for c in store._graph.query.call_args_list]
218
promote_calls = [c for c in calls if "semantic_type" in c and "RailsJob" in c]
219
assert len(promote_calls) >= 1
220
221
def test_promote_node_called_with_correct_semantic_type_for_concern(self):
222
store = self._make_store_for_fragment(
223
"concerns/", [["Auditable", "app/models/concerns/auditable.rb"]]
224
)
225
RailsEnricher(store).enrich()
226
227
calls = [str(c) for c in store._graph.query.call_args_list]
228
promote_calls = [c for c in calls if "semantic_type" in c and "RailsConcern" in c]
229
assert len(promote_calls) >= 1
230
231
def test_promote_node_called_with_correct_semantic_type_for_route(self):
232
store = self._make_store_for_fragment(
233
"routes.rb", [["routes", "config/routes.rb"]]
234
)
235
RailsEnricher(store).enrich()
236
237
calls = [str(c) for c in store._graph.query.call_args_list]
238
promote_calls = [c for c in calls if "semantic_type" in c and "RailsRoute" in c]
239
assert len(promote_calls) >= 1
240
241
242
# ── detect() integration ──────────────────────────────────────────────────────
243
244
245
class TestRailsEnricherDetect:
246
def test_detect_true_when_gemfile_present(self):
247
store = _mock_store(result_set=[[1]])
248
assert RailsEnricher(store).detect() is True
249
250
def test_detect_false_when_no_patterns_match(self):
251
store = _mock_store(result_set=[[0]])
252
assert RailsEnricher(store).detect() is False
253
254
def test_detect_false_on_empty_result_set(self):
255
store = _mock_store(result_set=[])
256
assert RailsEnricher(store).detect() is False
257

Keyboard Shortcuts

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