Navegador

navegador / tests / test_chef_enricher.py
Blame History Raw 211 lines
1
"""Tests for navegador.enrichment.chef — ChefEnricher."""
2
3
from unittest.mock import MagicMock
4
5
from navegador.enrichment.chef import ChefEnricher
6
7
8
def _make_store(query_results=None):
9
"""Create a mock GraphStore.
10
11
*query_results* maps Cypher query substrings to result_set lists.
12
Unmatched queries return an empty result_set.
13
"""
14
store = MagicMock()
15
mapping = query_results or {}
16
17
def _side_effect(query, params=None):
18
result = MagicMock()
19
for substr, rows in mapping.items():
20
if substr in query:
21
result.result_set = rows
22
return result
23
result.result_set = []
24
return result
25
26
store.query.side_effect = _side_effect
27
return store
28
29
30
class TestIdentity:
31
"""Framework identity properties."""
32
33
def test_framework_name(self):
34
store = _make_store()
35
enricher = ChefEnricher(store)
36
assert enricher.framework_name == "chef"
37
38
def test_detection_files(self):
39
store = _make_store()
40
enricher = ChefEnricher(store)
41
assert "metadata.rb" in enricher.detection_files
42
assert "Berksfile" in enricher.detection_files
43
44
def test_detection_patterns(self):
45
store = _make_store()
46
enricher = ChefEnricher(store)
47
assert "chef" in enricher.detection_patterns
48
49
50
class TestDetect:
51
"""Tests for detect() — framework presence detection."""
52
53
def test_detect_true_when_metadata_rb_exists(self):
54
store = _make_store(
55
{
56
"f.name = $name": [[1]],
57
}
58
)
59
enricher = ChefEnricher(store)
60
assert enricher.detect() is True
61
62
def test_detect_false_when_no_markers(self):
63
store = _make_store()
64
enricher = ChefEnricher(store)
65
assert enricher.detect() is False
66
67
def test_detect_true_via_import_pattern(self):
68
store = _make_store(
69
{
70
"n.name = $name OR n.module = $name": [[1]],
71
}
72
)
73
enricher = ChefEnricher(store)
74
assert enricher.detect() is True
75
76
77
class TestEnrichRecipes:
78
"""Tests for enrich() promoting recipe files."""
79
80
def test_promotes_recipe_files(self):
81
store = _make_store(
82
{
83
"n.file_path CONTAINS $pattern": [
84
["default.rb", "cookbooks/web/recipes/default.rb"],
85
["install.rb", "cookbooks/web/recipes/install.rb"],
86
],
87
}
88
)
89
enricher = ChefEnricher(store)
90
result = enricher.enrich()
91
92
assert result.patterns_found["recipes"] == 2
93
assert result.promoted >= 2
94
95
# Verify _promote_node was called via store.query SET
96
set_calls = [c for c in store.query.call_args_list if "SET n.semantic_type" in str(c)]
97
assert len(set_calls) >= 2
98
99
100
class TestEnrichResources:
101
"""Tests for enrich() promoting Chef resource calls."""
102
103
def test_promotes_resource_functions(self):
104
# _enrich_resources queries twice (recipes/ and libraries/),
105
# so we use a custom side_effect to return data only once.
106
call_count = {"resource": 0}
107
original_results = [
108
["package", "cookbooks/web/recipes/default.rb"],
109
["template", "cookbooks/web/recipes/default.rb"],
110
["not_a_resource", "cookbooks/web/recipes/default.rb"],
111
]
112
113
def _side_effect(query, params=None):
114
result = MagicMock()
115
if "(n:Function OR n:Method)" in query:
116
call_count["resource"] += 1
117
if call_count["resource"] == 1:
118
result.result_set = original_results
119
else:
120
result.result_set = []
121
else:
122
result.result_set = []
123
return result
124
125
store = MagicMock()
126
store.query.side_effect = _side_effect
127
enricher = ChefEnricher(store)
128
result = enricher.enrich()
129
130
# "package" and "template" match, "not_a_resource" does not
131
assert result.patterns_found["resources"] == 2
132
133
def test_skips_non_resource_functions(self):
134
store = _make_store(
135
{
136
"(n:Function OR n:Method)": [
137
["my_helper", "cookbooks/web/libraries/helpers.rb"],
138
],
139
}
140
)
141
enricher = ChefEnricher(store)
142
result = enricher.enrich()
143
144
assert result.patterns_found["resources"] == 0
145
146
147
class TestEnrichIncludeRecipe:
148
"""Tests for enrich() handling include_recipe edges."""
149
150
def test_creates_depends_on_edge(self):
151
# Strategy 1: follow CALLS edges from include_recipe nodes
152
def _query_side_effect(query, params=None):
153
result = MagicMock()
154
if "[:CALLS]" in query and "n.name = $name" in query:
155
result.result_set = [
156
[
157
"cookbooks/web/recipes/default.rb",
158
"database::install",
159
],
160
]
161
elif "f.file_path CONTAINS $recipes" in query:
162
result.result_set = [["install.rb"]]
163
elif "f.file_path = $path" in query:
164
result.result_set = [["default.rb"]]
165
elif "MERGE" in query:
166
result.result_set = []
167
else:
168
result.result_set = []
169
return result
170
171
store = MagicMock()
172
store.query.side_effect = _query_side_effect
173
enricher = ChefEnricher(store)
174
result = enricher.enrich()
175
176
assert result.edges_added >= 1
177
assert result.patterns_found["include_recipe"] >= 1
178
179
# Verify MERGE query was issued for the DEPENDS_ON edge
180
merge_calls = [
181
c for c in store.query.call_args_list if "MERGE" in str(c) and "DEPENDS_ON" in str(c)
182
]
183
assert len(merge_calls) >= 1
184
185
def test_no_edges_when_no_include_recipe(self):
186
store = _make_store()
187
enricher = ChefEnricher(store)
188
result = enricher.enrich()
189
190
assert result.edges_added == 0
191
assert result.patterns_found["include_recipe"] == 0
192
193
194
class TestEnrichCookbooks:
195
"""Tests for enrich() promoting cookbook metadata files."""
196
197
def test_promotes_metadata_rb(self):
198
store = _make_store(
199
{
200
"n.name = $name": [
201
["metadata.rb", "cookbooks/web/metadata.rb"],
202
],
203
}
204
)
205
enricher = ChefEnricher(store)
206
result = enricher.enrich()
207
208
assert result.patterns_found["cookbooks"] == 1
209
set_calls = [c for c in store.query.call_args_list if "chef_cookbook" in str(c)]
210
assert len(set_calls) >= 1
211

Keyboard Shortcuts

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