Navegador

navegador / tests / test_enrichment_react.py
Source Blame History 263 lines
96b97a1… lmata 1 """Tests for navegador.enrichment.react — ReactEnricher."""
96b97a1… lmata 2
96b97a1… lmata 3 from unittest.mock import MagicMock, call
96b97a1… lmata 4
96b97a1… lmata 5 import pytest
96b97a1… lmata 6
96b97a1… lmata 7 from navegador.enrichment import EnrichmentResult
96b97a1… lmata 8 from navegador.enrichment.react import ReactEnricher
96b97a1… lmata 9 from navegador.graph.store import GraphStore
96b97a1… lmata 10
96b97a1… lmata 11
96b97a1… lmata 12 # ── Helpers ───────────────────────────────────────────────────────────────────
96b97a1… lmata 13
96b97a1… lmata 14
96b97a1… lmata 15 def _mock_store(result_set=None):
96b97a1… lmata 16 """Return a GraphStore backed by a mock FalkorDB graph."""
96b97a1… lmata 17 client = MagicMock()
96b97a1… lmata 18 graph = MagicMock()
96b97a1… lmata 19 graph.query.return_value = MagicMock(result_set=result_set)
96b97a1… lmata 20 client.select_graph.return_value = graph
96b97a1… lmata 21 return GraphStore(client)
96b97a1… lmata 22
96b97a1… lmata 23
96b97a1… lmata 24 def _mock_store_with_responses(responses):
96b97a1… lmata 25 """Return a GraphStore whose graph.query returns the given result sets in order.
96b97a1… lmata 26
96b97a1… lmata 27 Any calls beyond the provided list (e.g. _promote_node SET queries) receive
96b97a1… lmata 28 a MagicMock with result_set=None, which is harmless since the enrichers do
96b97a1… lmata 29 not inspect the return value of promote calls.
96b97a1… lmata 30 """
96b97a1… lmata 31 client = MagicMock()
96b97a1… lmata 32 graph = MagicMock()
96b97a1… lmata 33 response_iter = iter(responses)
96b97a1… lmata 34
96b97a1… lmata 35 def _side_effect(cypher, params):
96b97a1… lmata 36 try:
96b97a1… lmata 37 rs = next(response_iter)
96b97a1… lmata 38 except StopIteration:
96b97a1… lmata 39 rs = None
96b97a1… lmata 40 return MagicMock(result_set=rs)
96b97a1… lmata 41
96b97a1… lmata 42 graph.query.side_effect = _side_effect
96b97a1… lmata 43 client.select_graph.return_value = graph
96b97a1… lmata 44 return GraphStore(client)
96b97a1… lmata 45
96b97a1… lmata 46
96b97a1… lmata 47 # ── framework_name ────────────────────────────────────────────────────────────
96b97a1… lmata 48
96b97a1… lmata 49
96b97a1… lmata 50 class TestFrameworkName:
96b97a1… lmata 51 def test_framework_name_is_react(self):
96b97a1… lmata 52 enricher = ReactEnricher(_mock_store())
96b97a1… lmata 53 assert enricher.framework_name == "react"
96b97a1… lmata 54
96b97a1… lmata 55
96b97a1… lmata 56 # ── detection_patterns ────────────────────────────────────────────────────────
96b97a1… lmata 57
96b97a1… lmata 58
96b97a1… lmata 59 class TestDetectionPatterns:
96b97a1… lmata 60 def test_contains_react(self):
96b97a1… lmata 61 enricher = ReactEnricher(_mock_store())
96b97a1… lmata 62 assert "react" in enricher.detection_patterns
96b97a1… lmata 63
2c266d2… lmata 64 def test_contains_react_dom(self):
2c266d2… lmata 65 enricher = ReactEnricher(_mock_store())
2c266d2… lmata 66 assert "react-dom" in enricher.detection_patterns
2c266d2… lmata 67
2c266d2… lmata 68 def test_contains_next(self):
2c266d2… lmata 69 enricher = ReactEnricher(_mock_store())
2c266d2… lmata 70 assert "next" in enricher.detection_patterns
96b97a1… lmata 71
96b97a1… lmata 72 def test_returns_list(self):
96b97a1… lmata 73 enricher = ReactEnricher(_mock_store())
96b97a1… lmata 74 assert isinstance(enricher.detection_patterns, list)
2c266d2… lmata 75
2c266d2… lmata 76 def test_detection_files_contains_next_config_variants(self):
2c266d2… lmata 77 enricher = ReactEnricher(_mock_store())
2c266d2… lmata 78 files = enricher.detection_files
2c266d2… lmata 79 assert any("next.config" in f for f in files)
96b97a1… lmata 80
96b97a1… lmata 81
96b97a1… lmata 82 # ── detect() ─────────────────────────────────────────────────────────────────
96b97a1… lmata 83
96b97a1… lmata 84
96b97a1… lmata 85 class TestDetect:
96b97a1… lmata 86 def test_returns_true_when_react_pattern_found(self):
96b97a1… lmata 87 store = _mock_store(result_set=[[1]])
96b97a1… lmata 88 enricher = ReactEnricher(store)
96b97a1… lmata 89 assert enricher.detect() is True
96b97a1… lmata 90
96b97a1… lmata 91 def test_returns_false_when_no_pattern_found(self):
96b97a1… lmata 92 store = _mock_store(result_set=[[0]])
96b97a1… lmata 93 enricher = ReactEnricher(store)
96b97a1… lmata 94 assert enricher.detect() is False
96b97a1… lmata 95
96b97a1… lmata 96 def test_returns_false_on_empty_result_set(self):
96b97a1… lmata 97 store = _mock_store(result_set=[])
96b97a1… lmata 98 enricher = ReactEnricher(store)
96b97a1… lmata 99 assert enricher.detect() is False
96b97a1… lmata 100
96b97a1… lmata 101 def test_short_circuits_on_first_match(self):
96b97a1… lmata 102 store = _mock_store(result_set=[[3]])
96b97a1… lmata 103 enricher = ReactEnricher(store)
96b97a1… lmata 104 assert enricher.detect() is True
96b97a1… lmata 105 assert store._graph.query.call_count == 1
96b97a1… lmata 106
96b97a1… lmata 107
96b97a1… lmata 108 # ── enrich() ─────────────────────────────────────────────────────────────────
96b97a1… lmata 109
96b97a1… lmata 110
96b97a1… lmata 111 class TestEnrich:
96b97a1… lmata 112 def _make_enricher_empty(self):
96b97a1… lmata 113 """Enricher that returns empty result sets for every pattern query."""
96b97a1… lmata 114 # 5 pattern queries: components, pages, api_routes, hooks, stores
96b97a1… lmata 115 store = _mock_store_with_responses([[], [], [], [], []])
96b97a1… lmata 116 return ReactEnricher(store)
96b97a1… lmata 117
96b97a1… lmata 118 def test_returns_enrichment_result(self):
96b97a1… lmata 119 enricher = self._make_enricher_empty()
96b97a1… lmata 120 assert isinstance(enricher.enrich(), EnrichmentResult)
96b97a1… lmata 121
96b97a1… lmata 122 def test_zero_promoted_when_no_matches(self):
96b97a1… lmata 123 enricher = self._make_enricher_empty()
96b97a1… lmata 124 result = enricher.enrich()
96b97a1… lmata 125 assert result.promoted == 0
96b97a1… lmata 126
96b97a1… lmata 127 def test_patterns_found_keys_present(self):
96b97a1… lmata 128 enricher = self._make_enricher_empty()
96b97a1… lmata 129 result = enricher.enrich()
96b97a1… lmata 130 assert "components" in result.patterns_found
96b97a1… lmata 131 assert "pages" in result.patterns_found
96b97a1… lmata 132 assert "api_routes" in result.patterns_found
96b97a1… lmata 133 assert "hooks" in result.patterns_found
96b97a1… lmata 134 assert "stores" in result.patterns_found
96b97a1… lmata 135
96b97a1… lmata 136 def test_promotes_component_nodes(self):
96b97a1… lmata 137 """A JSX file node should be promoted to ReactComponent."""
96b97a1… lmata 138 store = _mock_store_with_responses(
96b97a1… lmata 139 [
96b97a1… lmata 140 [["Button", "src/components/Button.jsx"]], # components
96b97a1… lmata 141 [], # pages
96b97a1… lmata 142 [], # api_routes
96b97a1… lmata 143 [], # hooks
96b97a1… lmata 144 [], # stores
96b97a1… lmata 145 # _promote_node calls handled by fallback
96b97a1… lmata 146 ]
96b97a1… lmata 147 )
96b97a1… lmata 148 enricher = ReactEnricher(store)
96b97a1… lmata 149 result = enricher.enrich()
96b97a1… lmata 150 assert result.promoted == 1
96b97a1… lmata 151 assert result.patterns_found["components"] == 1
96b97a1… lmata 152
96b97a1… lmata 153 # Verify _promote_node called store.query with correct semantic_type
96b97a1… lmata 154 calls = store._graph.query.call_args_list
96b97a1… lmata 155 promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
96b97a1… lmata 156 assert len(promote_calls) == 1
96b97a1… lmata 157 _, params = promote_calls[0][0]
96b97a1… lmata 158 assert params["semantic_type"] == "ReactComponent"
96b97a1… lmata 159 assert params["name"] == "Button"
96b97a1… lmata 160
96b97a1… lmata 161 def test_promotes_page_nodes(self):
96b97a1… lmata 162 """Nodes inside /pages/ directory should become NextPage."""
96b97a1… lmata 163 store = _mock_store_with_responses(
96b97a1… lmata 164 [
96b97a1… lmata 165 [], # components
96b97a1… lmata 166 [["IndexPage", "src/pages/index.tsx"]], # pages
96b97a1… lmata 167 [], # api_routes
96b97a1… lmata 168 [], # hooks
96b97a1… lmata 169 [], # stores
96b97a1… lmata 170 ]
96b97a1… lmata 171 )
96b97a1… lmata 172 enricher = ReactEnricher(store)
96b97a1… lmata 173 result = enricher.enrich()
96b97a1… lmata 174 assert result.promoted == 1
96b97a1… lmata 175 assert result.patterns_found["pages"] == 1
96b97a1… lmata 176
96b97a1… lmata 177 calls = store._graph.query.call_args_list
96b97a1… lmata 178 promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
96b97a1… lmata 179 assert promote_calls[0][0][1]["semantic_type"] == "NextPage"
96b97a1… lmata 180
96b97a1… lmata 181 def test_promotes_api_route_nodes(self):
96b97a1… lmata 182 """Nodes inside /pages/api/ should become NextApiRoute."""
96b97a1… lmata 183 store = _mock_store_with_responses(
96b97a1… lmata 184 [
96b97a1… lmata 185 [], # components
96b97a1… lmata 186 [], # pages
96b97a1… lmata 187 [["handler", "src/pages/api/user.ts"]], # api_routes
96b97a1… lmata 188 [], # hooks
96b97a1… lmata 189 [], # stores
96b97a1… lmata 190 ]
96b97a1… lmata 191 )
96b97a1… lmata 192 enricher = ReactEnricher(store)
96b97a1… lmata 193 result = enricher.enrich()
96b97a1… lmata 194 assert result.promoted == 1
96b97a1… lmata 195 assert result.patterns_found["api_routes"] == 1
96b97a1… lmata 196
96b97a1… lmata 197 calls = store._graph.query.call_args_list
96b97a1… lmata 198 promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
96b97a1… lmata 199 assert promote_calls[0][0][1]["semantic_type"] == "NextApiRoute"
96b97a1… lmata 200
96b97a1… lmata 201 def test_promotes_hook_nodes(self):
96b97a1… lmata 202 """Functions starting with 'use' should become ReactHook."""
96b97a1… lmata 203 store = _mock_store_with_responses(
96b97a1… lmata 204 [
96b97a1… lmata 205 [], # components
96b97a1… lmata 206 [], # pages
96b97a1… lmata 207 [], # api_routes
96b97a1… lmata 208 [["useAuth", "src/hooks/useAuth.ts"]], # hooks
96b97a1… lmata 209 [], # stores
96b97a1… lmata 210 ]
96b97a1… lmata 211 )
96b97a1… lmata 212 enricher = ReactEnricher(store)
96b97a1… lmata 213 result = enricher.enrich()
96b97a1… lmata 214 assert result.promoted == 1
96b97a1… lmata 215 assert result.patterns_found["hooks"] == 1
96b97a1… lmata 216
96b97a1… lmata 217 calls = store._graph.query.call_args_list
96b97a1… lmata 218 promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
96b97a1… lmata 219 assert promote_calls[0][0][1]["semantic_type"] == "ReactHook"
96b97a1… lmata 220
96b97a1… lmata 221 def test_promotes_store_nodes(self):
96b97a1… lmata 222 """createStore / useStore patterns should become ReactStore."""
96b97a1… lmata 223 store = _mock_store_with_responses(
96b97a1… lmata 224 [
96b97a1… lmata 225 [], # components
96b97a1… lmata 226 [], # pages
96b97a1… lmata 227 [], # api_routes
96b97a1… lmata 228 [], # hooks
96b97a1… lmata 229 [["createStore", "src/store/index.ts"]], # stores
96b97a1… lmata 230 ]
96b97a1… lmata 231 )
96b97a1… lmata 232 enricher = ReactEnricher(store)
96b97a1… lmata 233 result = enricher.enrich()
96b97a1… lmata 234 assert result.promoted == 1
96b97a1… lmata 235 assert result.patterns_found["stores"] == 1
96b97a1… lmata 236
96b97a1… lmata 237 calls = store._graph.query.call_args_list
96b97a1… lmata 238 promote_calls = [c for c in calls if "SET n.semantic_type" in c[0][0]]
96b97a1… lmata 239 assert promote_calls[0][0][1]["semantic_type"] == "ReactStore"
96b97a1… lmata 240
96b97a1… lmata 241 def test_promoted_count_accumulates_across_patterns(self):
96b97a1… lmata 242 """promoted should be the sum of all matched nodes."""
96b97a1… lmata 243 # Each matched node triggers a _promote_node SET query after its pattern query.
96b97a1… lmata 244 # Responses must be interleaved: pattern_query, promote, promote, pattern_query, ...
96b97a1… lmata 245 store = _mock_store_with_responses(
96b97a1… lmata 246 [
96b97a1… lmata 247 [["Btn", "a.jsx"], ["Input", "b.tsx"]], # components query (2 rows)
96b97a1… lmata 248 None, # _promote_node for Btn
96b97a1… lmata 249 None, # _promote_node for Input
96b97a1… lmata 250 [["Home", "pages/index.tsx"]], # pages query (1 row)
96b97a1… lmata 251 None, # _promote_node for Home
96b97a1… lmata 252 [], # api_routes query
96b97a1… lmata 253 [["useUser", "hooks/useUser.ts"]], # hooks query (1 row)
96b97a1… lmata 254 None, # _promote_node for useUser
96b97a1… lmata 255 [], # stores query
96b97a1… lmata 256 ]
96b97a1… lmata 257 )
96b97a1… lmata 258 enricher = ReactEnricher(store)
96b97a1… lmata 259 result = enricher.enrich()
96b97a1… lmata 260 assert result.promoted == 4
96b97a1… lmata 261 assert result.patterns_found["components"] == 2
96b97a1… lmata 262 assert result.patterns_found["pages"] == 1
96b97a1… lmata 263 assert result.patterns_found["hooks"] == 1

Keyboard Shortcuts

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