Navegador

navegador / tests / test_enrichment_fastapi.py
Source Blame History 467 lines
8554cc4… lmata 1 """
8554cc4… lmata 2 Tests for navegador.enrichment.fastapi — FastAPIEnricher.
8554cc4… lmata 3
8554cc4… lmata 4 All tests use a mock GraphStore so no real database is required.
8554cc4… lmata 5 """
8554cc4… lmata 6
8554cc4… lmata 7 from unittest.mock import MagicMock, call, patch
8554cc4… lmata 8
8554cc4… lmata 9 import pytest
8554cc4… lmata 10
8554cc4… lmata 11 from navegador.enrichment.base import EnrichmentResult, FrameworkEnricher
8554cc4… lmata 12 from navegador.enrichment.fastapi import FastAPIEnricher
8554cc4… lmata 13 from navegador.graph.store import GraphStore
8554cc4… lmata 14
8554cc4… lmata 15
8554cc4… lmata 16 # ── Helpers ────────────────────────────────────────────────────────────────────
8554cc4… lmata 17
8554cc4… lmata 18
8554cc4… lmata 19 def _mock_store(result_set=None):
8554cc4… lmata 20 """Return a GraphStore backed by a mock graph that always returns result_set."""
8554cc4… lmata 21 client = MagicMock()
8554cc4… lmata 22 graph = MagicMock()
8554cc4… lmata 23 graph.query.return_value = MagicMock(result_set=result_set or [])
8554cc4… lmata 24 client.select_graph.return_value = graph
8554cc4… lmata 25 return GraphStore(client)
8554cc4… lmata 26
8554cc4… lmata 27
8554cc4… lmata 28 def _mock_store_with_responses(responses):
8554cc4… lmata 29 """
8554cc4… lmata 30 Return a GraphStore whose graph.query() returns successive mock results.
8554cc4… lmata 31
8554cc4… lmata 32 ``responses`` is a list of result_set values in call order. Once
8554cc4… lmata 33 exhausted, subsequent calls return an empty result_set.
8554cc4… lmata 34 """
8554cc4… lmata 35 client = MagicMock()
8554cc4… lmata 36 graph = MagicMock()
8554cc4… lmata 37 iter_resp = iter(responses)
8554cc4… lmata 38
8554cc4… lmata 39 def _side_effect(cypher, params=None):
8554cc4… lmata 40 rs = next(iter_resp, [])
8554cc4… lmata 41 return MagicMock(result_set=rs)
8554cc4… lmata 42
8554cc4… lmata 43 graph.query.side_effect = _side_effect
8554cc4… lmata 44 client.select_graph.return_value = graph
8554cc4… lmata 45 return GraphStore(client)
8554cc4… lmata 46
8554cc4… lmata 47
8554cc4… lmata 48 # ── Identity ───────────────────────────────────────────────────────────────────
8554cc4… lmata 49
8554cc4… lmata 50
8554cc4… lmata 51 class TestFastAPIEnricherIdentity:
8554cc4… lmata 52 def test_framework_name(self):
8554cc4… lmata 53 store = _mock_store()
8554cc4… lmata 54 enricher = FastAPIEnricher(store)
8554cc4… lmata 55 assert enricher.framework_name == "fastapi"
8554cc4… lmata 56
8554cc4… lmata 57 def test_detection_patterns_includes_fastapi_lowercase(self):
8554cc4… lmata 58 store = _mock_store()
8554cc4… lmata 59 assert "fastapi" in FastAPIEnricher(store).detection_patterns
8554cc4… lmata 60
2c266d2… lmata 61 def test_detection_patterns_is_list_of_strings(self):
8554cc4… lmata 62 store = _mock_store()
2c266d2… lmata 63 patterns = FastAPIEnricher(store).detection_patterns
2c266d2… lmata 64 assert isinstance(patterns, list)
2c266d2… lmata 65 assert all(isinstance(p, str) for p in patterns)
8554cc4… lmata 66
2c266d2… lmata 67 def test_detection_patterns_is_nonempty(self):
8554cc4… lmata 68 store = _mock_store()
2c266d2… lmata 69 assert len(FastAPIEnricher(store).detection_patterns) >= 1
8554cc4… lmata 70
8554cc4… lmata 71 def test_is_subclass_of_framework_enricher(self):
8554cc4… lmata 72 store = _mock_store()
8554cc4… lmata 73 assert isinstance(FastAPIEnricher(store), FrameworkEnricher)
8554cc4… lmata 74
8554cc4… lmata 75
8554cc4… lmata 76 # ── detect() ──────────────────────────────────────────────────────────────────
8554cc4… lmata 77
8554cc4… lmata 78
8554cc4… lmata 79 class TestFastAPIEnricherDetect:
8554cc4… lmata 80 def test_detect_returns_true_when_fastapi_import_found(self):
8554cc4… lmata 81 store = _mock_store(result_set=[[1]])
8554cc4… lmata 82 assert FastAPIEnricher(store).detect() is True
8554cc4… lmata 83
8554cc4… lmata 84 def test_detect_returns_false_when_no_match(self):
8554cc4… lmata 85 store = _mock_store(result_set=[[0]])
8554cc4… lmata 86 assert FastAPIEnricher(store).detect() is False
8554cc4… lmata 87
8554cc4… lmata 88 def test_detect_returns_false_on_empty_result_set(self):
8554cc4… lmata 89 store = _mock_store(result_set=[])
8554cc4… lmata 90 assert FastAPIEnricher(store).detect() is False
8554cc4… lmata 91
2c266d2… lmata 92 def test_detect_queries_all_patterns_when_no_match(self):
8554cc4… lmata 93 store = _mock_store(result_set=[[0]])
2c266d2… lmata 94 enricher = FastAPIEnricher(store)
2c266d2… lmata 95 enricher.detect()
2c266d2… lmata 96 # All detection_patterns queried when no match is found
2c266d2… lmata 97 expected = len(enricher.detection_patterns) + len(enricher.detection_files)
2c266d2… lmata 98 assert store._graph.query.call_count == expected
8554cc4… lmata 99
8554cc4… lmata 100 def test_detect_short_circuits_on_first_match(self):
8554cc4… lmata 101 store = _mock_store(result_set=[[7]])
8554cc4… lmata 102 FastAPIEnricher(store).detect()
8554cc4… lmata 103 assert store._graph.query.call_count == 1
8554cc4… lmata 104
8554cc4… lmata 105
8554cc4… lmata 106 # ── enrich() returns EnrichmentResult ─────────────────────────────────────────
8554cc4… lmata 107
8554cc4… lmata 108
8554cc4… lmata 109 class TestFastAPIEnricherEnrichReturnType:
8554cc4… lmata 110 def test_returns_enrichment_result(self):
8554cc4… lmata 111 store = _mock_store()
8554cc4… lmata 112 result = FastAPIEnricher(store).enrich()
8554cc4… lmata 113 assert isinstance(result, EnrichmentResult)
8554cc4… lmata 114
8554cc4… lmata 115 def test_result_has_expected_pattern_keys(self):
8554cc4… lmata 116 store = _mock_store()
8554cc4… lmata 117 result = FastAPIEnricher(store).enrich()
8554cc4… lmata 118 assert "routes" in result.patterns_found
8554cc4… lmata 119 assert "dependencies" in result.patterns_found
8554cc4… lmata 120 assert "pydantic_models" in result.patterns_found
8554cc4… lmata 121 assert "background_tasks" in result.patterns_found
8554cc4… lmata 122
8554cc4… lmata 123 def test_result_promoted_is_sum_of_patterns(self):
8554cc4… lmata 124 store = _mock_store()
8554cc4… lmata 125 result = FastAPIEnricher(store).enrich()
8554cc4… lmata 126 assert result.promoted == sum(result.patterns_found.values())
8554cc4… lmata 127
8554cc4… lmata 128 def test_no_matches_gives_zero_promoted(self):
8554cc4… lmata 129 store = _mock_store(result_set=[])
8554cc4… lmata 130 result = FastAPIEnricher(store).enrich()
8554cc4… lmata 131 assert result.promoted == 0
8554cc4… lmata 132
8554cc4… lmata 133
8554cc4… lmata 134 # ── Routes ────────────────────────────────────────────────────────────────────
8554cc4… lmata 135
8554cc4… lmata 136
8554cc4… lmata 137 class TestFastAPIEnricherRoutes:
8554cc4… lmata 138 def _enricher_with_route_hit(self, name="get_items", file_path="app/main.py"):
8554cc4… lmata 139 """
8554cc4… lmata 140 Build a FastAPIEnricher whose store returns one matching row for the
8554cc4… lmata 141 *first* Decorator-based route query (app.get), then empty for the rest.
8554cc4… lmata 142 """
8554cc4… lmata 143 client = MagicMock()
8554cc4… lmata 144 graph = MagicMock()
8554cc4… lmata 145 call_count = [0]
8554cc4… lmata 146
8554cc4… lmata 147 def _side_effect(cypher, params=None):
8554cc4… lmata 148 call_count[0] += 1
8554cc4… lmata 149 # Return a hit on the very first call only
8554cc4… lmata 150 if call_count[0] == 1:
8554cc4… lmata 151 return MagicMock(result_set=[[name, file_path]])
8554cc4… lmata 152 return MagicMock(result_set=[])
8554cc4… lmata 153
8554cc4… lmata 154 graph.query.side_effect = _side_effect
8554cc4… lmata 155 client.select_graph.return_value = graph
8554cc4… lmata 156 store = GraphStore(client)
8554cc4… lmata 157 return FastAPIEnricher(store)
8554cc4… lmata 158
8554cc4… lmata 159 def test_route_promoted_increments_count(self):
8554cc4… lmata 160 enricher = self._enricher_with_route_hit()
8554cc4… lmata 161 result = enricher.enrich()
8554cc4… lmata 162 assert result.patterns_found["routes"] >= 1
8554cc4… lmata 163
8554cc4… lmata 164 def test_route_calls_promote_node_with_route_semantic_type(self):
8554cc4… lmata 165 enricher = self._enricher_with_route_hit("list_users", "app/users.py")
8554cc4… lmata 166 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 167 # Re-wire store so only the first query returns a hit
8554cc4… lmata 168 call_count = [0]
8554cc4… lmata 169
8554cc4… lmata 170 def _side_effect(cypher, params=None):
8554cc4… lmata 171 call_count[0] += 1
8554cc4… lmata 172 if call_count[0] == 1:
8554cc4… lmata 173 return MagicMock(result_set=[["list_users", "app/users.py"]])
8554cc4… lmata 174 return MagicMock(result_set=[])
8554cc4… lmata 175
8554cc4… lmata 176 enricher.store._graph.query.side_effect = _side_effect
8554cc4… lmata 177 enricher.enrich()
8554cc4… lmata 178
8554cc4… lmata 179 calls = [c for c in mock_promote.call_args_list if c[0][2] == "Route"]
8554cc4… lmata 180 assert len(calls) >= 1
8554cc4… lmata 181 assert calls[0][0][0] == "list_users"
8554cc4… lmata 182 assert calls[0][0][1] == "app/users.py"
8554cc4… lmata 183
8554cc4… lmata 184 def test_route_http_method_stored_as_prop(self):
8554cc4… lmata 185 """The http_method kwarg should be passed to _promote_node for route nodes."""
8554cc4… lmata 186 store = _mock_store()
8554cc4… lmata 187 enricher = FastAPIEnricher(store)
8554cc4… lmata 188
8554cc4… lmata 189 call_count = [0]
8554cc4… lmata 190
8554cc4… lmata 191 def _side_effect(cypher, params=None):
8554cc4… lmata 192 call_count[0] += 1
8554cc4… lmata 193 if call_count[0] == 1:
8554cc4… lmata 194 return MagicMock(result_set=[["create_item", "app/main.py"]])
8554cc4… lmata 195 return MagicMock(result_set=[])
8554cc4… lmata 196
8554cc4… lmata 197 store._graph.query.side_effect = _side_effect
8554cc4… lmata 198
8554cc4… lmata 199 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 200 enricher.enrich()
8554cc4… lmata 201
8554cc4… lmata 202 route_calls = [c for c in mock_promote.call_args_list if c[0][2] == "Route"]
8554cc4… lmata 203 assert route_calls, "Expected at least one _promote_node call with 'Route'"
8554cc4… lmata 204 _, kwargs = route_calls[0]
8554cc4… lmata 205 props = kwargs.get("props") or (route_calls[0][0][3] if len(route_calls[0][0]) > 3 else None)
8554cc4… lmata 206 assert props is not None
8554cc4… lmata 207 assert "http_method" in props
8554cc4… lmata 208
8554cc4… lmata 209
8554cc4… lmata 210 # ── Dependencies ──────────────────────────────────────────────────────────────
8554cc4… lmata 211
8554cc4… lmata 212
8554cc4… lmata 213 class TestFastAPIEnricherDependencies:
8554cc4… lmata 214 def test_dependency_promoted_for_depends_in_signature(self):
8554cc4… lmata 215 store = _mock_store()
8554cc4… lmata 216 enricher = FastAPIEnricher(store)
8554cc4… lmata 217
8554cc4… lmata 218 def _side_effect(cypher, params=None):
8554cc4… lmata 219 params = params or {}
8554cc4… lmata 220 if params.get("pattern") == "Depends(":
8554cc4… lmata 221 return MagicMock(result_set=[["get_db", "app/deps.py"]])
8554cc4… lmata 222 return MagicMock(result_set=[])
8554cc4… lmata 223
8554cc4… lmata 224 store._graph.query.side_effect = _side_effect
8554cc4… lmata 225
8554cc4… lmata 226 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 227 enricher.enrich()
8554cc4… lmata 228
8554cc4… lmata 229 dep_calls = [c for c in mock_promote.call_args_list if c[0][2] == "Dependency"]
8554cc4… lmata 230 assert len(dep_calls) >= 1
8554cc4… lmata 231
8554cc4… lmata 232 def test_dependency_semantic_type_is_dependency(self):
8554cc4… lmata 233 store = _mock_store()
8554cc4… lmata 234 enricher = FastAPIEnricher(store)
8554cc4… lmata 235
8554cc4… lmata 236 def _side_effect(cypher, params=None):
8554cc4… lmata 237 params = params or {}
8554cc4… lmata 238 if "Depends" in params.get("pattern", ""):
8554cc4… lmata 239 return MagicMock(result_set=[["auth_user", "app/auth.py"]])
8554cc4… lmata 240 return MagicMock(result_set=[])
8554cc4… lmata 241
8554cc4… lmata 242 store._graph.query.side_effect = _side_effect
8554cc4… lmata 243
8554cc4… lmata 244 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 245 enricher.enrich()
8554cc4… lmata 246
8554cc4… lmata 247 dep_calls = [c for c in mock_promote.call_args_list if c[0][2] == "Dependency"]
8554cc4… lmata 248 assert dep_calls
8554cc4… lmata 249
8554cc4… lmata 250
8554cc4… lmata 251 # ── Pydantic Models ───────────────────────────────────────────────────────────
8554cc4… lmata 252
8554cc4… lmata 253
8554cc4… lmata 254 class TestFastAPIEnricherPydanticModels:
8554cc4… lmata 255 def test_pydantic_model_promoted_via_inherits_edge(self):
8554cc4… lmata 256 store = _mock_store()
8554cc4… lmata 257 enricher = FastAPIEnricher(store)
8554cc4… lmata 258
8554cc4… lmata 259 def _side_effect(cypher, params=None):
8554cc4… lmata 260 params = params or {}
8554cc4… lmata 261 if params.get("base_name") == "BaseModel":
8554cc4… lmata 262 return MagicMock(result_set=[["UserSchema", "app/schemas.py"]])
8554cc4… lmata 263 return MagicMock(result_set=[])
8554cc4… lmata 264
8554cc4… lmata 265 store._graph.query.side_effect = _side_effect
8554cc4… lmata 266
8554cc4… lmata 267 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 268 enricher.enrich()
8554cc4… lmata 269
8554cc4… lmata 270 pm_calls = [c for c in mock_promote.call_args_list if c[0][2] == "PydanticModel"]
8554cc4… lmata 271 assert len(pm_calls) >= 1
8554cc4… lmata 272 assert pm_calls[0][0][0] == "UserSchema"
8554cc4… lmata 273 assert pm_calls[0][0][1] == "app/schemas.py"
8554cc4… lmata 274
8554cc4… lmata 275 def test_pydantic_model_semantic_type_string(self):
8554cc4… lmata 276 store = _mock_store()
8554cc4… lmata 277 enricher = FastAPIEnricher(store)
8554cc4… lmata 278
8554cc4… lmata 279 def _side_effect(cypher, params=None):
8554cc4… lmata 280 params = params or {}
8554cc4… lmata 281 if params.get("base_name") == "BaseModel":
8554cc4… lmata 282 return MagicMock(result_set=[["ItemModel", "app/models.py"]])
8554cc4… lmata 283 return MagicMock(result_set=[])
8554cc4… lmata 284
8554cc4… lmata 285 store._graph.query.side_effect = _side_effect
8554cc4… lmata 286
8554cc4… lmata 287 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 288 enricher.enrich()
8554cc4… lmata 289
8554cc4… lmata 290 types = {c[0][2] for c in mock_promote.call_args_list}
8554cc4… lmata 291 assert "PydanticModel" in types
8554cc4… lmata 292
8554cc4… lmata 293 def test_pydantic_model_fallback_via_docstring(self):
8554cc4… lmata 294 """If no INHERITS edge, docstring containing 'BaseModel' is the fallback."""
8554cc4… lmata 295 store = _mock_store()
8554cc4… lmata 296 enricher = FastAPIEnricher(store)
8554cc4… lmata 297
8554cc4… lmata 298 call_count = [0]
8554cc4… lmata 299
8554cc4… lmata 300 def _side_effect(cypher, params=None):
8554cc4… lmata 301 call_count[0] += 1
8554cc4… lmata 302 params = params or {}
8554cc4… lmata 303 # Fail INHERITS query, succeed on docstring fallback
8554cc4… lmata 304 if params.get("base_name") == "BaseModel":
8554cc4… lmata 305 return MagicMock(result_set=[])
8554cc4… lmata 306 if "BaseModel" in params.get("pattern", "") and "docstring" in cypher:
8554cc4… lmata 307 return MagicMock(result_set=[["CreateUser", "app/schemas.py"]])
8554cc4… lmata 308 return MagicMock(result_set=[])
8554cc4… lmata 309
8554cc4… lmata 310 store._graph.query.side_effect = _side_effect
8554cc4… lmata 311
8554cc4… lmata 312 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 313 enricher.enrich()
8554cc4… lmata 314
8554cc4… lmata 315 pm_calls = [c for c in mock_promote.call_args_list if c[0][2] == "PydanticModel"]
8554cc4… lmata 316 assert len(pm_calls) >= 1
8554cc4… lmata 317
8554cc4… lmata 318
8554cc4… lmata 319 # ── Background Tasks ──────────────────────────────────────────────────────────
8554cc4… lmata 320
8554cc4… lmata 321
8554cc4… lmata 322 class TestFastAPIEnricherBackgroundTasks:
8554cc4… lmata 323 def test_background_task_promoted_via_on_event_decorator(self):
8554cc4… lmata 324 store = _mock_store()
8554cc4… lmata 325 enricher = FastAPIEnricher(store)
8554cc4… lmata 326
8554cc4… lmata 327 def _side_effect(cypher, params=None):
8554cc4… lmata 328 params = params or {}
8554cc4… lmata 329 if "on_event" in params.get("pattern", ""):
8554cc4… lmata 330 return MagicMock(result_set=[["startup_handler", "app/events.py"]])
8554cc4… lmata 331 return MagicMock(result_set=[])
8554cc4… lmata 332
8554cc4… lmata 333 store._graph.query.side_effect = _side_effect
8554cc4… lmata 334
8554cc4… lmata 335 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 336 enricher.enrich()
8554cc4… lmata 337
8554cc4… lmata 338 bt_calls = [c for c in mock_promote.call_args_list if c[0][2] == "BackgroundTask"]
8554cc4… lmata 339 assert len(bt_calls) >= 1
8554cc4… lmata 340
8554cc4… lmata 341 def test_background_task_promoted_via_background_tasks_in_signature(self):
8554cc4… lmata 342 store = _mock_store()
8554cc4… lmata 343 enricher = FastAPIEnricher(store)
8554cc4… lmata 344
8554cc4… lmata 345 def _side_effect(cypher, params=None):
8554cc4… lmata 346 params = params or {}
8554cc4… lmata 347 if "BackgroundTasks" in params.get("pattern", ""):
8554cc4… lmata 348 return MagicMock(result_set=[["send_email", "app/tasks.py"]])
8554cc4… lmata 349 return MagicMock(result_set=[])
8554cc4… lmata 350
8554cc4… lmata 351 store._graph.query.side_effect = _side_effect
8554cc4… lmata 352
8554cc4… lmata 353 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 354 enricher.enrich()
8554cc4… lmata 355
8554cc4… lmata 356 bt_calls = [c for c in mock_promote.call_args_list if c[0][2] == "BackgroundTask"]
8554cc4… lmata 357 assert len(bt_calls) >= 1
8554cc4… lmata 358
8554cc4… lmata 359 def test_background_task_semantic_type_string(self):
8554cc4… lmata 360 store = _mock_store()
8554cc4… lmata 361 enricher = FastAPIEnricher(store)
8554cc4… lmata 362
8554cc4… lmata 363 def _side_effect(cypher, params=None):
8554cc4… lmata 364 params = params or {}
8554cc4… lmata 365 if "on_event" in params.get("pattern", ""):
8554cc4… lmata 366 return MagicMock(result_set=[["shutdown_handler", "app/main.py"]])
8554cc4… lmata 367 return MagicMock(result_set=[])
8554cc4… lmata 368
8554cc4… lmata 369 store._graph.query.side_effect = _side_effect
8554cc4… lmata 370
8554cc4… lmata 371 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 372 enricher.enrich()
8554cc4… lmata 373
8554cc4… lmata 374 types = {c[0][2] for c in mock_promote.call_args_list}
8554cc4… lmata 375 assert "BackgroundTask" in types
8554cc4… lmata 376
8554cc4… lmata 377
8554cc4… lmata 378 # ── _promote_node integration ─────────────────────────────────────────────────
8554cc4… lmata 379
8554cc4… lmata 380
8554cc4… lmata 381 class TestFastAPIEnricherPromoteNodeIntegration:
8554cc4… lmata 382 def test_promote_node_called_for_each_matched_row(self):
8554cc4… lmata 383 """Two rows returned → two _promote_node calls for that pattern."""
8554cc4… lmata 384 store = _mock_store()
8554cc4… lmata 385 enricher = FastAPIEnricher(store)
8554cc4… lmata 386
8554cc4… lmata 387 call_count = [0]
8554cc4… lmata 388
8554cc4… lmata 389 def _side_effect(cypher, params=None):
8554cc4… lmata 390 call_count[0] += 1
8554cc4… lmata 391 if call_count[0] == 1:
8554cc4… lmata 392 return MagicMock(result_set=[
8554cc4… lmata 393 ["route_a", "app/a.py"],
8554cc4… lmata 394 ["route_b", "app/b.py"],
8554cc4… lmata 395 ])
8554cc4… lmata 396 return MagicMock(result_set=[])
8554cc4… lmata 397
8554cc4… lmata 398 store._graph.query.side_effect = _side_effect
8554cc4… lmata 399
8554cc4… lmata 400 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 401 enricher.enrich()
8554cc4… lmata 402
8554cc4… lmata 403 route_calls = [c for c in mock_promote.call_args_list if c[0][2] == "Route"]
8554cc4… lmata 404 assert len(route_calls) == 2
8554cc4… lmata 405
8554cc4… lmata 406 def test_rows_with_none_name_are_skipped(self):
8554cc4… lmata 407 """Rows where name is None must not call _promote_node."""
8554cc4… lmata 408 store = _mock_store()
8554cc4… lmata 409 enricher = FastAPIEnricher(store)
8554cc4… lmata 410
8554cc4… lmata 411 call_count = [0]
8554cc4… lmata 412
8554cc4… lmata 413 def _side_effect(cypher, params=None):
8554cc4… lmata 414 call_count[0] += 1
8554cc4… lmata 415 if call_count[0] == 1:
8554cc4… lmata 416 return MagicMock(result_set=[[None, "app/main.py"]])
8554cc4… lmata 417 return MagicMock(result_set=[])
8554cc4… lmata 418
8554cc4… lmata 419 store._graph.query.side_effect = _side_effect
8554cc4… lmata 420
8554cc4… lmata 421 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 422 enricher.enrich()
8554cc4… lmata 423
8554cc4… lmata 424 route_calls = [c for c in mock_promote.call_args_list if c[0][2] == "Route"]
8554cc4… lmata 425 assert len(route_calls) == 0
8554cc4… lmata 426
8554cc4… lmata 427 def test_rows_with_none_file_path_are_skipped(self):
8554cc4… lmata 428 """Rows where file_path is None must not call _promote_node."""
8554cc4… lmata 429 store = _mock_store()
8554cc4… lmata 430 enricher = FastAPIEnricher(store)
8554cc4… lmata 431
8554cc4… lmata 432 call_count = [0]
8554cc4… lmata 433
8554cc4… lmata 434 def _side_effect(cypher, params=None):
8554cc4… lmata 435 call_count[0] += 1
8554cc4… lmata 436 if call_count[0] == 1:
8554cc4… lmata 437 return MagicMock(result_set=[["get_items", None]])
8554cc4… lmata 438 return MagicMock(result_set=[])
8554cc4… lmata 439
8554cc4… lmata 440 store._graph.query.side_effect = _side_effect
8554cc4… lmata 441
8554cc4… lmata 442 with patch.object(enricher, "_promote_node") as mock_promote:
8554cc4… lmata 443 enricher.enrich()
8554cc4… lmata 444
8554cc4… lmata 445 route_calls = [c for c in mock_promote.call_args_list if c[0][2] == "Route"]
8554cc4… lmata 446 assert len(route_calls) == 0
8554cc4… lmata 447
8554cc4… lmata 448
8554cc4… lmata 449 # ── HTTP method coverage ──────────────────────────────────────────────────────
8554cc4… lmata 450
8554cc4… lmata 451
8554cc4… lmata 452 class TestFastAPIEnricherHTTPMethods:
8554cc4… lmata 453 def test_all_http_methods_are_queried(self):
8554cc4… lmata 454 """The enricher must issue queries for all standard HTTP verbs."""
8554cc4… lmata 455 store = _mock_store(result_set=[])
8554cc4… lmata 456 FastAPIEnricher(store).enrich()
8554cc4… lmata 457
8554cc4… lmata 458 all_params = [
8554cc4… lmata 459 call_args[0][1] if len(call_args[0]) > 1 else {}
8554cc4… lmata 460 for call_args in store._graph.query.call_args_list
8554cc4… lmata 461 ]
8554cc4… lmata 462 patterns_used = {p.get("pattern", "") for p in all_params}
8554cc4… lmata 463
8554cc4… lmata 464 for method in ("get", "post", "put", "delete", "patch"):
8554cc4… lmata 465 assert any(method in pat for pat in patterns_used), (
8554cc4… lmata 466 f"HTTP method '{method}' not found in any query pattern"
8554cc4… lmata 467 )

Keyboard Shortcuts

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