|
b663b12…
|
lmata
|
1 |
"""Tests for navegador CLI commands via click CliRunner.""" |
|
b663b12…
|
lmata
|
2 |
|
|
b663b12…
|
lmata
|
3 |
import json |
|
b663b12…
|
lmata
|
4 |
from pathlib import Path |
|
b663b12…
|
lmata
|
5 |
from unittest.mock import MagicMock, patch |
|
b663b12…
|
lmata
|
6 |
|
|
b663b12…
|
lmata
|
7 |
from click.testing import CliRunner |
|
b663b12…
|
lmata
|
8 |
|
|
b663b12…
|
lmata
|
9 |
from navegador.cli.commands import main |
|
b663b12…
|
lmata
|
10 |
from navegador.context.loader import ContextBundle, ContextNode |
|
b663b12…
|
lmata
|
11 |
|
|
b663b12…
|
lmata
|
12 |
# ── Helpers ─────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
13 |
|
|
b663b12…
|
lmata
|
14 |
def _mock_store(): |
|
b663b12…
|
lmata
|
15 |
store = MagicMock() |
|
b663b12…
|
lmata
|
16 |
store.query.return_value = MagicMock(result_set=[]) |
|
b663b12…
|
lmata
|
17 |
return store |
|
b663b12…
|
lmata
|
18 |
|
|
b663b12…
|
lmata
|
19 |
|
|
b663b12…
|
lmata
|
20 |
def _node(name="foo", type_="Function", file_path="app.py"): |
|
b663b12…
|
lmata
|
21 |
return ContextNode(name=name, type=type_, file_path=file_path) |
|
b663b12…
|
lmata
|
22 |
|
|
b663b12…
|
lmata
|
23 |
|
|
b663b12…
|
lmata
|
24 |
def _empty_bundle(name="target", type_="Function"): |
|
b663b12…
|
lmata
|
25 |
"""Return a ContextBundle with a minimal target for testing.""" |
|
b663b12…
|
lmata
|
26 |
return ContextBundle(target=_node(name, type_), nodes=[]) |
|
b663b12…
|
lmata
|
27 |
|
|
b663b12…
|
lmata
|
28 |
|
|
b663b12…
|
lmata
|
29 |
# ── init ────────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
30 |
|
|
b663b12…
|
lmata
|
31 |
class TestInitCommand: |
|
b663b12…
|
lmata
|
32 |
def test_creates_navegador_dir(self): |
|
b663b12…
|
lmata
|
33 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
34 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
35 |
result = runner.invoke(main, ["init", "."]) |
|
b663b12…
|
lmata
|
36 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
37 |
assert Path(".navegador").exists() |
|
b663b12…
|
lmata
|
38 |
|
|
b663b12…
|
lmata
|
39 |
def test_shows_redis_hint_when_url_provided(self): |
|
b663b12…
|
lmata
|
40 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
41 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
42 |
result = runner.invoke(main, ["init", ".", "--redis", "redis://localhost:6379"]) |
|
b663b12…
|
lmata
|
43 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
44 |
assert "redis://localhost:6379" in result.output |
|
b663b12…
|
lmata
|
45 |
|
|
b663b12…
|
lmata
|
46 |
def test_shows_sqlite_hint_by_default(self): |
|
b663b12…
|
lmata
|
47 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
48 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
49 |
result = runner.invoke(main, ["init", "."]) |
|
b663b12…
|
lmata
|
50 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
51 |
assert "Local SQLite" in result.output |
|
b663b12…
|
lmata
|
52 |
|
|
c4536b2…
|
lmata
|
53 |
def test_llm_provider_shown(self): |
|
c4536b2…
|
lmata
|
54 |
runner = CliRunner() |
|
c4536b2…
|
lmata
|
55 |
with runner.isolated_filesystem(): |
|
c4536b2…
|
lmata
|
56 |
result = runner.invoke(main, ["init", ".", "--llm-provider", "anthropic"]) |
|
c4536b2…
|
lmata
|
57 |
assert result.exit_code == 0 |
|
c4536b2…
|
lmata
|
58 |
assert "anthropic" in result.output |
|
c4536b2…
|
lmata
|
59 |
|
|
c4536b2…
|
lmata
|
60 |
def test_cluster_flag_shown(self): |
|
c4536b2…
|
lmata
|
61 |
runner = CliRunner() |
|
c4536b2…
|
lmata
|
62 |
with runner.isolated_filesystem(): |
|
c4536b2…
|
lmata
|
63 |
result = runner.invoke(main, ["init", ".", "--cluster"]) |
|
c4536b2…
|
lmata
|
64 |
assert result.exit_code == 0 |
|
c4536b2…
|
lmata
|
65 |
assert "Cluster mode" in result.output |
|
c4536b2…
|
lmata
|
66 |
|
|
b663b12…
|
lmata
|
67 |
|
|
b663b12…
|
lmata
|
68 |
# ── ingest ──────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
69 |
|
|
b663b12…
|
lmata
|
70 |
class TestIngestCommand: |
|
b663b12…
|
lmata
|
71 |
def test_outputs_table_on_success(self): |
|
b663b12…
|
lmata
|
72 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
73 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
74 |
Path("src").mkdir() |
|
b663b12…
|
lmata
|
75 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
76 |
patch("navegador.ingestion.RepoIngester") as MockRI: |
|
b663b12…
|
lmata
|
77 |
MockRI.return_value.ingest.return_value = {"files": 5, "functions": 20} |
|
b663b12…
|
lmata
|
78 |
result = runner.invoke(main, ["ingest", "src"]) |
|
b663b12…
|
lmata
|
79 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
80 |
|
|
b663b12…
|
lmata
|
81 |
def test_json_flag_outputs_json(self): |
|
b663b12…
|
lmata
|
82 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
83 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
84 |
Path("src").mkdir() |
|
b663b12…
|
lmata
|
85 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
86 |
patch("navegador.ingestion.RepoIngester") as MockRI: |
|
b663b12…
|
lmata
|
87 |
MockRI.return_value.ingest.return_value = {"files": 5} |
|
b663b12…
|
lmata
|
88 |
result = runner.invoke(main, ["ingest", "src", "--json"]) |
|
b663b12…
|
lmata
|
89 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
90 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
91 |
assert data["files"] == 5 |
|
df3d6fa…
|
lmata
|
92 |
|
|
df3d6fa…
|
lmata
|
93 |
def test_incremental_flag_passes_through(self): |
|
df3d6fa…
|
lmata
|
94 |
runner = CliRunner() |
|
df3d6fa…
|
lmata
|
95 |
with runner.isolated_filesystem(): |
|
df3d6fa…
|
lmata
|
96 |
Path("src").mkdir() |
|
df3d6fa…
|
lmata
|
97 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
df3d6fa…
|
lmata
|
98 |
patch("navegador.ingestion.RepoIngester") as MockRI: |
|
df3d6fa…
|
lmata
|
99 |
MockRI.return_value.ingest.return_value = { |
|
df3d6fa…
|
lmata
|
100 |
"files": 2, "functions": 5, "classes": 1, "edges": 3, "skipped": 8 |
|
df3d6fa…
|
lmata
|
101 |
} |
|
df3d6fa…
|
lmata
|
102 |
result = runner.invoke(main, ["ingest", "src", "--incremental"]) |
|
df3d6fa…
|
lmata
|
103 |
assert result.exit_code == 0 |
|
df3d6fa…
|
lmata
|
104 |
MockRI.return_value.ingest.assert_called_once() |
|
df3d6fa…
|
lmata
|
105 |
_, kwargs = MockRI.return_value.ingest.call_args |
|
df3d6fa…
|
lmata
|
106 |
assert kwargs["incremental"] is True |
|
df3d6fa…
|
lmata
|
107 |
|
|
df3d6fa…
|
lmata
|
108 |
def test_watch_flag_calls_watch(self): |
|
df3d6fa…
|
lmata
|
109 |
runner = CliRunner() |
|
df3d6fa…
|
lmata
|
110 |
with runner.isolated_filesystem(): |
|
df3d6fa…
|
lmata
|
111 |
Path("src").mkdir() |
|
df3d6fa…
|
lmata
|
112 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
df3d6fa…
|
lmata
|
113 |
patch("navegador.ingestion.RepoIngester") as MockRI: |
|
df3d6fa…
|
lmata
|
114 |
# watch should be called, simulate immediate stop |
|
df3d6fa…
|
lmata
|
115 |
MockRI.return_value.watch.side_effect = KeyboardInterrupt() |
|
df3d6fa…
|
lmata
|
116 |
result = runner.invoke(main, ["ingest", "src", "--watch", "--interval", "0.1"]) |
|
df3d6fa…
|
lmata
|
117 |
assert result.exit_code == 0 |
|
df3d6fa…
|
lmata
|
118 |
MockRI.return_value.watch.assert_called_once() |
|
df3d6fa…
|
lmata
|
119 |
|
|
df3d6fa…
|
lmata
|
120 |
def test_watch_with_interval(self): |
|
df3d6fa…
|
lmata
|
121 |
runner = CliRunner() |
|
df3d6fa…
|
lmata
|
122 |
with runner.isolated_filesystem(): |
|
df3d6fa…
|
lmata
|
123 |
Path("src").mkdir() |
|
df3d6fa…
|
lmata
|
124 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
df3d6fa…
|
lmata
|
125 |
patch("navegador.ingestion.RepoIngester") as MockRI: |
|
df3d6fa…
|
lmata
|
126 |
MockRI.return_value.watch.side_effect = KeyboardInterrupt() |
|
df3d6fa…
|
lmata
|
127 |
runner.invoke(main, ["ingest", "src", "--watch", "--interval", "5.0"]) |
|
df3d6fa…
|
lmata
|
128 |
_, kwargs = MockRI.return_value.watch.call_args |
|
df3d6fa…
|
lmata
|
129 |
assert kwargs["interval"] == 5.0 |
|
b663b12…
|
lmata
|
130 |
|
|
b663b12…
|
lmata
|
131 |
|
|
b663b12…
|
lmata
|
132 |
# ── context ─────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
133 |
|
|
b663b12…
|
lmata
|
134 |
class TestContextCommand: |
|
b663b12…
|
lmata
|
135 |
def test_json_format(self): |
|
b663b12…
|
lmata
|
136 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
137 |
bundle = ContextBundle(target=_node("MyClass", "Class"), nodes=[]) |
|
b663b12…
|
lmata
|
138 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
139 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
140 |
MockCL.return_value.load_file.return_value = bundle |
|
b663b12…
|
lmata
|
141 |
result = runner.invoke(main, ["context", "app.py", "--format", "json"]) |
|
b663b12…
|
lmata
|
142 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
143 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
144 |
assert isinstance(data, dict) |
|
b663b12…
|
lmata
|
145 |
|
|
b663b12…
|
lmata
|
146 |
def test_markdown_format(self): |
|
b663b12…
|
lmata
|
147 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
148 |
bundle = ContextBundle(target=_node(), nodes=[]) |
|
b663b12…
|
lmata
|
149 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
150 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
151 |
MockCL.return_value.load_file.return_value = bundle |
|
b663b12…
|
lmata
|
152 |
result = runner.invoke(main, ["context", "app.py"]) |
|
b663b12…
|
lmata
|
153 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
154 |
|
|
b663b12…
|
lmata
|
155 |
|
|
b663b12…
|
lmata
|
156 |
# ── function ────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
157 |
|
|
b663b12…
|
lmata
|
158 |
class TestFunctionCommand: |
|
b663b12…
|
lmata
|
159 |
def test_function_json(self): |
|
b663b12…
|
lmata
|
160 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
161 |
bundle = ContextBundle(target=_node("my_func"), nodes=[]) |
|
b663b12…
|
lmata
|
162 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
163 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
164 |
MockCL.return_value.load_function.return_value = bundle |
|
b663b12…
|
lmata
|
165 |
result = runner.invoke(main, ["function", "my_func", "--format", "json"]) |
|
b663b12…
|
lmata
|
166 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
167 |
json.loads(result.output) # must be valid JSON |
|
b663b12…
|
lmata
|
168 |
|
|
b663b12…
|
lmata
|
169 |
def test_function_with_file_option(self): |
|
b663b12…
|
lmata
|
170 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
171 |
bundle = _empty_bundle() |
|
b663b12…
|
lmata
|
172 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
173 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
174 |
MockCL.return_value.load_function.return_value = bundle |
|
b663b12…
|
lmata
|
175 |
result = runner.invoke(main, ["function", "foo", "--file", "bar.py"]) |
|
b663b12…
|
lmata
|
176 |
MockCL.return_value.load_function.assert_called_with( |
|
b663b12…
|
lmata
|
177 |
"foo", file_path="bar.py", depth=2) |
|
b663b12…
|
lmata
|
178 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
179 |
|
|
b663b12…
|
lmata
|
180 |
|
|
b663b12…
|
lmata
|
181 |
# ── class ───────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
182 |
|
|
b663b12…
|
lmata
|
183 |
class TestClassCommand: |
|
b663b12…
|
lmata
|
184 |
def test_class_json(self): |
|
b663b12…
|
lmata
|
185 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
186 |
bundle = ContextBundle(target=_node("MyClass", "Class"), nodes=[]) |
|
b663b12…
|
lmata
|
187 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
188 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
189 |
MockCL.return_value.load_class.return_value = bundle |
|
b663b12…
|
lmata
|
190 |
result = runner.invoke(main, ["class", "MyClass", "--format", "json"]) |
|
b663b12…
|
lmata
|
191 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
192 |
json.loads(result.output) |
|
b663b12…
|
lmata
|
193 |
|
|
b663b12…
|
lmata
|
194 |
|
|
b663b12…
|
lmata
|
195 |
# ── explain ─────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
196 |
|
|
b663b12…
|
lmata
|
197 |
class TestExplainCommand: |
|
b663b12…
|
lmata
|
198 |
def test_explain_json(self): |
|
b663b12…
|
lmata
|
199 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
200 |
bundle = _empty_bundle() |
|
b663b12…
|
lmata
|
201 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
202 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
203 |
MockCL.return_value.explain.return_value = bundle |
|
b663b12…
|
lmata
|
204 |
result = runner.invoke(main, ["explain", "SomeName", "--format", "json"]) |
|
b663b12…
|
lmata
|
205 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
206 |
json.loads(result.output) |
|
b663b12…
|
lmata
|
207 |
|
|
b663b12…
|
lmata
|
208 |
|
|
b663b12…
|
lmata
|
209 |
# ── search ──────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
210 |
|
|
b663b12…
|
lmata
|
211 |
class TestSearchCommand: |
|
b663b12…
|
lmata
|
212 |
def test_search_json_no_results(self): |
|
b663b12…
|
lmata
|
213 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
214 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
215 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
216 |
MockCL.return_value.search.return_value = [] |
|
b663b12…
|
lmata
|
217 |
result = runner.invoke(main, ["search", "foo", "--format", "json"]) |
|
b663b12…
|
lmata
|
218 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
219 |
assert json.loads(result.output) == [] |
|
b663b12…
|
lmata
|
220 |
|
|
b663b12…
|
lmata
|
221 |
def test_search_all_flag(self): |
|
b663b12…
|
lmata
|
222 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
223 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
224 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
225 |
MockCL.return_value.search_all.return_value = [] |
|
b663b12…
|
lmata
|
226 |
result = runner.invoke(main, ["search", "foo", "--all", "--format", "json"]) |
|
b663b12…
|
lmata
|
227 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
228 |
MockCL.return_value.search_all.assert_called_once() |
|
b663b12…
|
lmata
|
229 |
|
|
b663b12…
|
lmata
|
230 |
def test_search_docs_flag(self): |
|
b663b12…
|
lmata
|
231 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
232 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
233 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
234 |
MockCL.return_value.search_by_docstring.return_value = [] |
|
b663b12…
|
lmata
|
235 |
result = runner.invoke(main, ["search", "foo", "--docs", "--format", "json"]) |
|
b663b12…
|
lmata
|
236 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
237 |
MockCL.return_value.search_by_docstring.assert_called_once() |
|
b663b12…
|
lmata
|
238 |
|
|
b663b12…
|
lmata
|
239 |
def test_search_markdown_no_results(self): |
|
b663b12…
|
lmata
|
240 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
241 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
242 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
243 |
MockCL.return_value.search.return_value = [] |
|
b663b12…
|
lmata
|
244 |
result = runner.invoke(main, ["search", "nothing"]) |
|
b663b12…
|
lmata
|
245 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
246 |
|
|
b663b12…
|
lmata
|
247 |
def test_search_with_results(self): |
|
b663b12…
|
lmata
|
248 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
249 |
node = _node("result_fn") |
|
b663b12…
|
lmata
|
250 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
251 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
252 |
MockCL.return_value.search.return_value = [node] |
|
b663b12…
|
lmata
|
253 |
result = runner.invoke(main, ["search", "result", "--format", "json"]) |
|
b663b12…
|
lmata
|
254 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
255 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
256 |
assert len(data) == 1 |
|
b663b12…
|
lmata
|
257 |
assert data[0]["name"] == "result_fn" |
|
b663b12…
|
lmata
|
258 |
|
|
b663b12…
|
lmata
|
259 |
|
|
b663b12…
|
lmata
|
260 |
# ── decorated ───────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
261 |
|
|
b663b12…
|
lmata
|
262 |
class TestDecoratedCommand: |
|
b663b12…
|
lmata
|
263 |
def test_decorated_json_no_results(self): |
|
b663b12…
|
lmata
|
264 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
265 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
266 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
267 |
MockCL.return_value.decorated_by.return_value = [] |
|
b663b12…
|
lmata
|
268 |
result = runner.invoke(main, ["decorated", "login_required", "--format", "json"]) |
|
b663b12…
|
lmata
|
269 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
270 |
assert json.loads(result.output) == [] |
|
b663b12…
|
lmata
|
271 |
|
|
b663b12…
|
lmata
|
272 |
def test_decorated_with_results(self): |
|
b663b12…
|
lmata
|
273 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
274 |
node = _node("my_view", "Function", "views.py") |
|
b663b12…
|
lmata
|
275 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
276 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
277 |
MockCL.return_value.decorated_by.return_value = [node] |
|
b663b12…
|
lmata
|
278 |
result = runner.invoke(main, ["decorated", "login_required", "--format", "json"]) |
|
b663b12…
|
lmata
|
279 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
280 |
assert data[0]["name"] == "my_view" |
|
b663b12…
|
lmata
|
281 |
|
|
b663b12…
|
lmata
|
282 |
|
|
b663b12…
|
lmata
|
283 |
# ── query ───────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
284 |
|
|
b663b12…
|
lmata
|
285 |
class TestQueryCommand: |
|
b663b12…
|
lmata
|
286 |
def test_returns_json(self): |
|
b663b12…
|
lmata
|
287 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
288 |
store = _mock_store() |
|
b663b12…
|
lmata
|
289 |
store.query.return_value = MagicMock(result_set=[["Node1", "Node2"]]) |
|
b663b12…
|
lmata
|
290 |
with patch("navegador.cli.commands._get_store", return_value=store): |
|
b663b12…
|
lmata
|
291 |
result = runner.invoke(main, ["query", "MATCH (n) RETURN n.name"]) |
|
b663b12…
|
lmata
|
292 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
293 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
294 |
assert data == [["Node1", "Node2"]] |
|
b663b12…
|
lmata
|
295 |
|
|
b663b12…
|
lmata
|
296 |
def test_empty_result(self): |
|
b663b12…
|
lmata
|
297 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
298 |
store = _mock_store() |
|
b663b12…
|
lmata
|
299 |
with patch("navegador.cli.commands._get_store", return_value=store): |
|
b663b12…
|
lmata
|
300 |
result = runner.invoke(main, ["query", "MATCH (n) RETURN n"]) |
|
b663b12…
|
lmata
|
301 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
302 |
assert json.loads(result.output) == [] |
|
b663b12…
|
lmata
|
303 |
|
|
b663b12…
|
lmata
|
304 |
|
|
b663b12…
|
lmata
|
305 |
# ── add concept / rule / decision / person / domain ─────────────────────────── |
|
b663b12…
|
lmata
|
306 |
|
|
b663b12…
|
lmata
|
307 |
class TestAddCommands: |
|
b663b12…
|
lmata
|
308 |
def _run_add(self, *args): |
|
b663b12…
|
lmata
|
309 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
310 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
311 |
patch("navegador.ingestion.KnowledgeIngester") as MockKI: |
|
b663b12…
|
lmata
|
312 |
MockKI.return_value = MagicMock() |
|
b663b12…
|
lmata
|
313 |
result = runner.invoke(main, list(args)) |
|
b663b12…
|
lmata
|
314 |
return result, MockKI |
|
b663b12…
|
lmata
|
315 |
|
|
b663b12…
|
lmata
|
316 |
def test_add_concept(self): |
|
b663b12…
|
lmata
|
317 |
result, MockKI = self._run_add("add", "concept", "Payment", "--desc", "Handles money") |
|
b663b12…
|
lmata
|
318 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
319 |
MockKI.return_value.add_concept.assert_called_once() |
|
b663b12…
|
lmata
|
320 |
|
|
b663b12…
|
lmata
|
321 |
def test_add_rule(self): |
|
b663b12…
|
lmata
|
322 |
result, MockKI = self._run_add("add", "rule", "NoNullIds", "--severity", "critical") |
|
b663b12…
|
lmata
|
323 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
324 |
MockKI.return_value.add_rule.assert_called_once() |
|
b663b12…
|
lmata
|
325 |
|
|
b663b12…
|
lmata
|
326 |
def test_add_decision(self): |
|
b663b12…
|
lmata
|
327 |
result, MockKI = self._run_add("add", "decision", "Use PostgreSQL") |
|
b663b12…
|
lmata
|
328 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
329 |
MockKI.return_value.add_decision.assert_called_once() |
|
b663b12…
|
lmata
|
330 |
|
|
b663b12…
|
lmata
|
331 |
def test_add_person(self): |
|
b663b12…
|
lmata
|
332 |
result, MockKI = self._run_add("add", "person", "Alice", "--email", "[email protected]") |
|
b663b12…
|
lmata
|
333 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
334 |
MockKI.return_value.add_person.assert_called_once() |
|
b663b12…
|
lmata
|
335 |
|
|
b663b12…
|
lmata
|
336 |
def test_add_domain(self): |
|
b663b12…
|
lmata
|
337 |
result, MockKI = self._run_add("add", "domain", "Billing", "--desc", "All billing logic") |
|
b663b12…
|
lmata
|
338 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
339 |
MockKI.return_value.add_domain.assert_called_once() |
|
b663b12…
|
lmata
|
340 |
|
|
b663b12…
|
lmata
|
341 |
|
|
b663b12…
|
lmata
|
342 |
# ── annotate ────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
343 |
|
|
b663b12…
|
lmata
|
344 |
class TestAnnotateCommand: |
|
b663b12…
|
lmata
|
345 |
def test_annotate_with_concept(self): |
|
b663b12…
|
lmata
|
346 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
347 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
348 |
patch("navegador.ingestion.KnowledgeIngester") as MockKI: |
|
b663b12…
|
lmata
|
349 |
MockKI.return_value = MagicMock() |
|
b663b12…
|
lmata
|
350 |
result = runner.invoke(main, ["annotate", "process_payment", "--concept", "Payment"]) |
|
b663b12…
|
lmata
|
351 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
352 |
MockKI.return_value.annotate_code.assert_called_once() |
|
b663b12…
|
lmata
|
353 |
|
|
b663b12…
|
lmata
|
354 |
def test_annotate_with_rule(self): |
|
b663b12…
|
lmata
|
355 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
356 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
357 |
patch("navegador.ingestion.KnowledgeIngester") as MockKI: |
|
b663b12…
|
lmata
|
358 |
MockKI.return_value = MagicMock() |
|
b663b12…
|
lmata
|
359 |
result = runner.invoke(main, ["annotate", "validate_card", "--rule", "PCI"]) |
|
b663b12…
|
lmata
|
360 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
361 |
|
|
b663b12…
|
lmata
|
362 |
|
|
b663b12…
|
lmata
|
363 |
# ── domain / concept ────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
364 |
|
|
b663b12…
|
lmata
|
365 |
class TestDomainConceptCommands: |
|
b663b12…
|
lmata
|
366 |
def test_domain_json(self): |
|
b663b12…
|
lmata
|
367 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
368 |
bundle = _empty_bundle() |
|
b663b12…
|
lmata
|
369 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
370 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
371 |
MockCL.return_value.load_domain.return_value = bundle |
|
b663b12…
|
lmata
|
372 |
result = runner.invoke(main, ["domain", "Billing", "--format", "json"]) |
|
b663b12…
|
lmata
|
373 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
374 |
json.loads(result.output) |
|
b663b12…
|
lmata
|
375 |
|
|
b663b12…
|
lmata
|
376 |
def test_concept_json(self): |
|
b663b12…
|
lmata
|
377 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
378 |
bundle = _empty_bundle() |
|
b663b12…
|
lmata
|
379 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
380 |
patch("navegador.context.ContextLoader") as MockCL: |
|
b663b12…
|
lmata
|
381 |
MockCL.return_value.load_concept.return_value = bundle |
|
b663b12…
|
lmata
|
382 |
result = runner.invoke(main, ["concept", "Payment", "--format", "json"]) |
|
b663b12…
|
lmata
|
383 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
384 |
json.loads(result.output) |
|
b663b12…
|
lmata
|
385 |
|
|
b663b12…
|
lmata
|
386 |
|
|
b663b12…
|
lmata
|
387 |
# ── wiki ingest ─────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
388 |
|
|
b663b12…
|
lmata
|
389 |
class TestWikiIngestCommand: |
|
b663b12…
|
lmata
|
390 |
def test_ingest_local_dir(self): |
|
b663b12…
|
lmata
|
391 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
392 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
393 |
Path("wiki").mkdir() |
|
b663b12…
|
lmata
|
394 |
(Path("wiki") / "home.md").write_text("# Home") |
|
b663b12…
|
lmata
|
395 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
396 |
patch("navegador.ingestion.WikiIngester") as MockWI: |
|
b663b12…
|
lmata
|
397 |
MockWI.return_value.ingest_local.return_value = {"pages": 1, "links": 0} |
|
b663b12…
|
lmata
|
398 |
result = runner.invoke(main, ["wiki", "ingest", "--dir", "wiki"]) |
|
b663b12…
|
lmata
|
399 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
400 |
assert "1" in result.output |
|
b663b12…
|
lmata
|
401 |
|
|
b663b12…
|
lmata
|
402 |
def test_error_without_repo_or_dir(self): |
|
b663b12…
|
lmata
|
403 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
404 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()): |
|
b663b12…
|
lmata
|
405 |
result = runner.invoke(main, ["wiki", "ingest"]) |
|
b663b12…
|
lmata
|
406 |
assert result.exit_code != 0 |
|
b663b12…
|
lmata
|
407 |
|
|
b663b12…
|
lmata
|
408 |
def test_ingest_github_api(self): |
|
b663b12…
|
lmata
|
409 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
410 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
411 |
patch("navegador.ingestion.WikiIngester") as MockWI: |
|
b663b12…
|
lmata
|
412 |
MockWI.return_value.ingest_github_api.return_value = {"pages": 3, "links": 2} |
|
b663b12…
|
lmata
|
413 |
result = runner.invoke(main, ["wiki", "ingest", "--repo", "owner/repo", "--api"]) |
|
b663b12…
|
lmata
|
414 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
415 |
|
|
b663b12…
|
lmata
|
416 |
|
|
b663b12…
|
lmata
|
417 |
# ── stats ───────────────────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
418 |
|
|
b663b12…
|
lmata
|
419 |
class TestStatsCommand: |
|
b663b12…
|
lmata
|
420 |
def test_json_output(self): |
|
b663b12…
|
lmata
|
421 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
422 |
store = _mock_store() |
|
b663b12…
|
lmata
|
423 |
store.query.side_effect = [ |
|
b663b12…
|
lmata
|
424 |
MagicMock(result_set=[["Function", 10], ["Class", 5]]), |
|
b663b12…
|
lmata
|
425 |
MagicMock(result_set=[["CALLS", 20]]), |
|
b663b12…
|
lmata
|
426 |
] |
|
b663b12…
|
lmata
|
427 |
with patch("navegador.cli.commands._get_store", return_value=store): |
|
b663b12…
|
lmata
|
428 |
result = runner.invoke(main, ["stats", "--json"]) |
|
b663b12…
|
lmata
|
429 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
430 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
431 |
assert data["total_nodes"] == 15 |
|
b663b12…
|
lmata
|
432 |
assert data["total_edges"] == 20 |
|
b663b12…
|
lmata
|
433 |
|
|
b663b12…
|
lmata
|
434 |
def test_table_output(self): |
|
b663b12…
|
lmata
|
435 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
436 |
store = _mock_store() |
|
b663b12…
|
lmata
|
437 |
store.query.side_effect = [ |
|
b663b12…
|
lmata
|
438 |
MagicMock(result_set=[["Function", 10]]), |
|
b663b12…
|
lmata
|
439 |
MagicMock(result_set=[["CALLS", 5]]), |
|
b663b12…
|
lmata
|
440 |
] |
|
b663b12…
|
lmata
|
441 |
with patch("navegador.cli.commands._get_store", return_value=store): |
|
b663b12…
|
lmata
|
442 |
result = runner.invoke(main, ["stats"]) |
|
b663b12…
|
lmata
|
443 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
444 |
|
|
b663b12…
|
lmata
|
445 |
def test_empty_graph(self): |
|
b663b12…
|
lmata
|
446 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
447 |
store = _mock_store() |
|
b663b12…
|
lmata
|
448 |
store.query.side_effect = [ |
|
b663b12…
|
lmata
|
449 |
MagicMock(result_set=[]), |
|
b663b12…
|
lmata
|
450 |
MagicMock(result_set=[]), |
|
b663b12…
|
lmata
|
451 |
] |
|
b663b12…
|
lmata
|
452 |
with patch("navegador.cli.commands._get_store", return_value=store): |
|
b663b12…
|
lmata
|
453 |
result = runner.invoke(main, ["stats", "--json"]) |
|
b663b12…
|
lmata
|
454 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
455 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
456 |
assert data["total_nodes"] == 0 |
|
b663b12…
|
lmata
|
457 |
|
|
b663b12…
|
lmata
|
458 |
|
|
b663b12…
|
lmata
|
459 |
# ── planopticon ingest ──────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
460 |
|
|
b663b12…
|
lmata
|
461 |
class TestPlanopticonIngestCommand: |
|
b663b12…
|
lmata
|
462 |
def test_auto_detect_kg(self): |
|
b663b12…
|
lmata
|
463 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
464 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
465 |
Path("knowledge_graph.json").write_text('{"nodes":[],"relationships":[],"sources":[]}') |
|
b663b12…
|
lmata
|
466 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
467 |
patch("navegador.ingestion.PlanopticonIngester") as MockPI: |
|
b663b12…
|
lmata
|
468 |
MockPI.return_value.ingest_kg.return_value = {"nodes": 0, "edges": 0} |
|
b663b12…
|
lmata
|
469 |
result = runner.invoke(main, ["planopticon", "ingest", "knowledge_graph.json"]) |
|
b663b12…
|
lmata
|
470 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
471 |
|
|
b663b12…
|
lmata
|
472 |
def test_auto_detect_manifest(self): |
|
b663b12…
|
lmata
|
473 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
474 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
475 |
Path("manifest.json").write_text("{}") |
|
b663b12…
|
lmata
|
476 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
477 |
patch("navegador.ingestion.PlanopticonIngester") as MockPI: |
|
b663b12…
|
lmata
|
478 |
MockPI.return_value.ingest_manifest.return_value = {"nodes": 0, "edges": 0} |
|
b663b12…
|
lmata
|
479 |
result = runner.invoke(main, ["planopticon", "ingest", "manifest.json"]) |
|
b663b12…
|
lmata
|
480 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
481 |
MockPI.return_value.ingest_manifest.assert_called_once() |
|
b663b12…
|
lmata
|
482 |
|
|
b663b12…
|
lmata
|
483 |
def test_json_output(self): |
|
b663b12…
|
lmata
|
484 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
485 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
486 |
Path("kg.json").write_text('{"nodes":[],"relationships":[],"sources":[]}') |
|
b663b12…
|
lmata
|
487 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
488 |
patch("navegador.ingestion.PlanopticonIngester") as MockPI: |
|
b663b12…
|
lmata
|
489 |
MockPI.return_value.ingest_kg.return_value = {"nodes": 3, "edges": 1} |
|
b663b12…
|
lmata
|
490 |
result = runner.invoke(main, ["planopticon", "ingest", "kg.json", "--json"]) |
|
b663b12…
|
lmata
|
491 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
492 |
data = json.loads(result.output) |
|
b663b12…
|
lmata
|
493 |
assert data["nodes"] == 3 |
|
b663b12…
|
lmata
|
494 |
|
|
b663b12…
|
lmata
|
495 |
def test_directory_resolves_manifest(self): |
|
b663b12…
|
lmata
|
496 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
497 |
with runner.isolated_filesystem(): |
|
b663b12…
|
lmata
|
498 |
Path("output").mkdir() |
|
b663b12…
|
lmata
|
499 |
Path("output/manifest.json").write_text("{}") |
|
b663b12…
|
lmata
|
500 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
b663b12…
|
lmata
|
501 |
patch("navegador.ingestion.PlanopticonIngester") as MockPI: |
|
b663b12…
|
lmata
|
502 |
MockPI.return_value.ingest_manifest.return_value = {"nodes": 0, "edges": 0} |
|
b663b12…
|
lmata
|
503 |
result = runner.invoke(main, ["planopticon", "ingest", "output"]) |
|
b663b12…
|
lmata
|
504 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
505 |
MockPI.return_value.ingest_manifest.assert_called_once() |
|
b663b12…
|
lmata
|
506 |
|
|
b663b12…
|
lmata
|
507 |
|
|
b663b12…
|
lmata
|
508 |
# ── --help smoke tests ───────────────────────────────────────────────────────── |
|
b663b12…
|
lmata
|
509 |
|
|
b663b12…
|
lmata
|
510 |
class TestHelp: |
|
b663b12…
|
lmata
|
511 |
def test_main_help(self): |
|
b663b12…
|
lmata
|
512 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
513 |
result = runner.invoke(main, ["--help"]) |
|
b663b12…
|
lmata
|
514 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
515 |
assert "navegador" in result.output.lower() or "knowledge" in result.output.lower() |
|
b663b12…
|
lmata
|
516 |
|
|
b663b12…
|
lmata
|
517 |
def test_add_help(self): |
|
b663b12…
|
lmata
|
518 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
519 |
result = runner.invoke(main, ["add", "--help"]) |
|
b663b12…
|
lmata
|
520 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
521 |
|
|
b663b12…
|
lmata
|
522 |
def test_wiki_help(self): |
|
b663b12…
|
lmata
|
523 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
524 |
result = runner.invoke(main, ["wiki", "--help"]) |
|
b663b12…
|
lmata
|
525 |
assert result.exit_code == 0 |
|
b663b12…
|
lmata
|
526 |
|
|
b663b12…
|
lmata
|
527 |
def test_planopticon_help(self): |
|
b663b12…
|
lmata
|
528 |
runner = CliRunner() |
|
b663b12…
|
lmata
|
529 |
result = runner.invoke(main, ["planopticon", "--help"]) |
|
2270d4f…
|
lmata
|
530 |
assert result.exit_code == 0 |
|
2270d4f…
|
lmata
|
531 |
|
|
2270d4f…
|
lmata
|
532 |
|
|
2270d4f…
|
lmata
|
533 |
# ── _get_store with custom db path (lines 31-32) ────────────────────────────── |
|
2270d4f…
|
lmata
|
534 |
|
|
2270d4f…
|
lmata
|
535 |
class TestGetStoreCustomPath: |
|
2270d4f…
|
lmata
|
536 |
def test_get_store_calls_get_store_with_custom_path(self): |
|
2270d4f…
|
lmata
|
537 |
"""_get_store body: custom path is forwarded to config.get_store.""" |
|
2270d4f…
|
lmata
|
538 |
from navegador.cli.commands import _get_store |
|
2270d4f…
|
lmata
|
539 |
with patch("navegador.config.get_store", return_value=_mock_store()) as mock_gs: |
|
2270d4f…
|
lmata
|
540 |
_get_store("/custom/path.db") |
|
2270d4f…
|
lmata
|
541 |
mock_gs.assert_called_once_with("/custom/path.db") |
|
2270d4f…
|
lmata
|
542 |
|
|
2270d4f…
|
lmata
|
543 |
def test_get_store_passes_none_for_default_path(self): |
|
2270d4f…
|
lmata
|
544 |
from navegador.cli.commands import _get_store |
|
2270d4f…
|
lmata
|
545 |
from navegador.config import DEFAULT_DB_PATH |
|
2270d4f…
|
lmata
|
546 |
with patch("navegador.config.get_store", return_value=_mock_store()) as mock_gs: |
|
2270d4f…
|
lmata
|
547 |
_get_store(DEFAULT_DB_PATH) |
|
2270d4f…
|
lmata
|
548 |
mock_gs.assert_called_once_with(None) |
|
2270d4f…
|
lmata
|
549 |
|
|
2270d4f…
|
lmata
|
550 |
|
|
2270d4f…
|
lmata
|
551 |
# ── search table output with results (lines 208-216) ───────────────────────── |
|
2270d4f…
|
lmata
|
552 |
|
|
2270d4f…
|
lmata
|
553 |
class TestSearchTableOutput: |
|
2270d4f…
|
lmata
|
554 |
def test_search_renders_table_with_results(self): |
|
2270d4f…
|
lmata
|
555 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
556 |
node = _node("process_payment", "Function", "payments.py") |
|
2270d4f…
|
lmata
|
557 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
2270d4f…
|
lmata
|
558 |
patch("navegador.context.ContextLoader") as MockCL: |
|
2270d4f…
|
lmata
|
559 |
MockCL.return_value.search.return_value = [node] |
|
2270d4f…
|
lmata
|
560 |
result = runner.invoke(main, ["search", "payment"]) |
|
2270d4f…
|
lmata
|
561 |
assert result.exit_code == 0 |
|
2270d4f…
|
lmata
|
562 |
assert "process_payment" in result.output |
|
2270d4f…
|
lmata
|
563 |
|
|
2270d4f…
|
lmata
|
564 |
|
|
2270d4f…
|
lmata
|
565 |
# ── decorated table output with results (lines 237-248) ────────────────────── |
|
2270d4f…
|
lmata
|
566 |
|
|
2270d4f…
|
lmata
|
567 |
class TestDecoratedTableOutput: |
|
2270d4f…
|
lmata
|
568 |
def test_decorated_no_results_table(self): |
|
2270d4f…
|
lmata
|
569 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
570 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
2270d4f…
|
lmata
|
571 |
patch("navegador.context.ContextLoader") as MockCL: |
|
2270d4f…
|
lmata
|
572 |
MockCL.return_value.decorated_by.return_value = [] |
|
2270d4f…
|
lmata
|
573 |
result = runner.invoke(main, ["decorated", "login_required"]) |
|
2270d4f…
|
lmata
|
574 |
assert result.exit_code == 0 |
|
2270d4f…
|
lmata
|
575 |
assert "login_required" in result.output |
|
2270d4f…
|
lmata
|
576 |
|
|
2270d4f…
|
lmata
|
577 |
def test_decorated_renders_table_with_results(self): |
|
2270d4f…
|
lmata
|
578 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
579 |
node = _node("my_view", "Function", "views.py") |
|
2270d4f…
|
lmata
|
580 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
2270d4f…
|
lmata
|
581 |
patch("navegador.context.ContextLoader") as MockCL: |
|
2270d4f…
|
lmata
|
582 |
MockCL.return_value.decorated_by.return_value = [node] |
|
2270d4f…
|
lmata
|
583 |
result = runner.invoke(main, ["decorated", "login_required"]) |
|
2270d4f…
|
lmata
|
584 |
assert result.exit_code == 0 |
|
2270d4f…
|
lmata
|
585 |
assert "my_view" in result.output |
|
2270d4f…
|
lmata
|
586 |
|
|
2270d4f…
|
lmata
|
587 |
|
|
2270d4f…
|
lmata
|
588 |
# ── wiki ingest without --api flag (line 410) ───────────────────────────────── |
|
2270d4f…
|
lmata
|
589 |
|
|
2270d4f…
|
lmata
|
590 |
class TestWikiIngestGithubNoApi: |
|
2270d4f…
|
lmata
|
591 |
def test_ingest_github_without_api_flag(self): |
|
2270d4f…
|
lmata
|
592 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
593 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
2270d4f…
|
lmata
|
594 |
patch("navegador.ingestion.WikiIngester") as MockWI: |
|
2270d4f…
|
lmata
|
595 |
MockWI.return_value.ingest_github.return_value = {"pages": 5, "links": 3} |
|
2270d4f…
|
lmata
|
596 |
result = runner.invoke(main, ["wiki", "ingest", "--repo", "owner/repo"]) |
|
2270d4f…
|
lmata
|
597 |
assert result.exit_code == 0 |
|
2270d4f…
|
lmata
|
598 |
MockWI.return_value.ingest_github.assert_called_once() |
|
2270d4f…
|
lmata
|
599 |
assert "5" in result.output |
|
2270d4f…
|
lmata
|
600 |
|
|
2270d4f…
|
lmata
|
601 |
|
|
2270d4f…
|
lmata
|
602 |
# ── planopticon dir with no recognised files (line 497) ────────────────────── |
|
2270d4f…
|
lmata
|
603 |
|
|
2270d4f…
|
lmata
|
604 |
class TestPlanopticonIngestNoKnownFiles: |
|
2270d4f…
|
lmata
|
605 |
def test_empty_directory_raises_usage_error(self): |
|
2270d4f…
|
lmata
|
606 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
607 |
with runner.isolated_filesystem(): |
|
2270d4f…
|
lmata
|
608 |
Path("output").mkdir() |
|
2270d4f…
|
lmata
|
609 |
# No manifest.json, knowledge_graph.json, or interchange.json |
|
2270d4f…
|
lmata
|
610 |
Path("output/readme.txt").write_text("nothing") |
|
2270d4f…
|
lmata
|
611 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()): |
|
2270d4f…
|
lmata
|
612 |
result = runner.invoke(main, ["planopticon", "ingest", "output"]) |
|
2270d4f…
|
lmata
|
613 |
assert result.exit_code != 0 |
|
2270d4f…
|
lmata
|
614 |
|
|
2270d4f…
|
lmata
|
615 |
|
|
2270d4f…
|
lmata
|
616 |
# ── planopticon auto-detect interchange/batch (lines 505, 507) ─────────────── |
|
2270d4f…
|
lmata
|
617 |
|
|
2270d4f…
|
lmata
|
618 |
class TestPlanopticonAutoDetect: |
|
2270d4f…
|
lmata
|
619 |
def test_auto_detect_interchange(self): |
|
2270d4f…
|
lmata
|
620 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
621 |
with runner.isolated_filesystem(): |
|
2270d4f…
|
lmata
|
622 |
Path("interchange.json").write_text("{}") |
|
2270d4f…
|
lmata
|
623 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
2270d4f…
|
lmata
|
624 |
patch("navegador.ingestion.PlanopticonIngester") as MockPI: |
|
2270d4f…
|
lmata
|
625 |
MockPI.return_value.ingest_interchange.return_value = {"nodes": 0, "edges": 0} |
|
2270d4f…
|
lmata
|
626 |
result = runner.invoke(main, ["planopticon", "ingest", "interchange.json"]) |
|
2270d4f…
|
lmata
|
627 |
assert result.exit_code == 0 |
|
2270d4f…
|
lmata
|
628 |
MockPI.return_value.ingest_interchange.assert_called_once() |
|
2270d4f…
|
lmata
|
629 |
|
|
2270d4f…
|
lmata
|
630 |
def test_auto_detect_batch(self): |
|
2270d4f…
|
lmata
|
631 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
632 |
with runner.isolated_filesystem(): |
|
2270d4f…
|
lmata
|
633 |
Path("batch.json").write_text("{}") |
|
2270d4f…
|
lmata
|
634 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
2270d4f…
|
lmata
|
635 |
patch("navegador.ingestion.PlanopticonIngester") as MockPI: |
|
2270d4f…
|
lmata
|
636 |
MockPI.return_value.ingest_batch.return_value = {"nodes": 0, "edges": 0} |
|
2270d4f…
|
lmata
|
637 |
result = runner.invoke(main, ["planopticon", "ingest", "batch.json"]) |
|
2270d4f…
|
lmata
|
638 |
assert result.exit_code == 0 |
|
2270d4f…
|
lmata
|
639 |
MockPI.return_value.ingest_batch.assert_called_once() |
|
2270d4f…
|
lmata
|
640 |
|
|
2270d4f…
|
lmata
|
641 |
|
|
2270d4f…
|
lmata
|
642 |
# ── mcp command (lines 538-549) ─────────────────────────────────────────────── |
|
57ba118…
|
lmata
|
643 |
|
|
57ba118…
|
lmata
|
644 |
# ── export / import ────────────────────────────────────────────────────────── |
|
57ba118…
|
lmata
|
645 |
|
|
57ba118…
|
lmata
|
646 |
class TestExportCommand: |
|
57ba118…
|
lmata
|
647 |
def test_export_success(self): |
|
57ba118…
|
lmata
|
648 |
runner = CliRunner() |
|
57ba118…
|
lmata
|
649 |
with runner.isolated_filesystem(): |
|
57ba118…
|
lmata
|
650 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
57ba118…
|
lmata
|
651 |
patch("navegador.graph.export.export_graph", return_value={"nodes": 10, "edges": 5}): |
|
57ba118…
|
lmata
|
652 |
result = runner.invoke(main, ["export", "graph.jsonl"]) |
|
57ba118…
|
lmata
|
653 |
assert result.exit_code == 0 |
|
57ba118…
|
lmata
|
654 |
assert "10 nodes" in result.output |
|
57ba118…
|
lmata
|
655 |
|
|
57ba118…
|
lmata
|
656 |
def test_export_json(self): |
|
57ba118…
|
lmata
|
657 |
runner = CliRunner() |
|
57ba118…
|
lmata
|
658 |
with runner.isolated_filesystem(): |
|
57ba118…
|
lmata
|
659 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
57ba118…
|
lmata
|
660 |
patch("navegador.graph.export.export_graph", return_value={"nodes": 10, "edges": 5}): |
|
57ba118…
|
lmata
|
661 |
result = runner.invoke(main, ["export", "graph.jsonl", "--json"]) |
|
57ba118…
|
lmata
|
662 |
assert result.exit_code == 0 |
|
57ba118…
|
lmata
|
663 |
data = json.loads(result.output) |
|
57ba118…
|
lmata
|
664 |
assert data["nodes"] == 10 |
|
57ba118…
|
lmata
|
665 |
|
|
57ba118…
|
lmata
|
666 |
|
|
57ba118…
|
lmata
|
667 |
class TestImportCommand: |
|
57ba118…
|
lmata
|
668 |
def test_import_success(self): |
|
57ba118…
|
lmata
|
669 |
runner = CliRunner() |
|
57ba118…
|
lmata
|
670 |
with runner.isolated_filesystem(): |
|
57ba118…
|
lmata
|
671 |
Path("graph.jsonl").write_text("") |
|
57ba118…
|
lmata
|
672 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
57ba118…
|
lmata
|
673 |
patch("navegador.graph.export.import_graph", return_value={"nodes": 10, "edges": 5}): |
|
57ba118…
|
lmata
|
674 |
result = runner.invoke(main, ["import", "graph.jsonl"]) |
|
57ba118…
|
lmata
|
675 |
assert result.exit_code == 0 |
|
57ba118…
|
lmata
|
676 |
assert "10 nodes" in result.output |
|
57ba118…
|
lmata
|
677 |
|
|
57ba118…
|
lmata
|
678 |
def test_import_json(self): |
|
57ba118…
|
lmata
|
679 |
runner = CliRunner() |
|
57ba118…
|
lmata
|
680 |
with runner.isolated_filesystem(): |
|
57ba118…
|
lmata
|
681 |
Path("graph.jsonl").write_text("") |
|
57ba118…
|
lmata
|
682 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
57ba118…
|
lmata
|
683 |
patch("navegador.graph.export.import_graph", return_value={"nodes": 8, "edges": 3}): |
|
57ba118…
|
lmata
|
684 |
result = runner.invoke(main, ["import", "graph.jsonl", "--json"]) |
|
57ba118…
|
lmata
|
685 |
assert result.exit_code == 0 |
|
57ba118…
|
lmata
|
686 |
data = json.loads(result.output) |
|
57ba118…
|
lmata
|
687 |
assert data["nodes"] == 8 |
|
57ba118…
|
lmata
|
688 |
|
|
57ba118…
|
lmata
|
689 |
def test_import_no_clear(self): |
|
57ba118…
|
lmata
|
690 |
runner = CliRunner() |
|
57ba118…
|
lmata
|
691 |
with runner.isolated_filesystem(): |
|
57ba118…
|
lmata
|
692 |
Path("graph.jsonl").write_text("") |
|
57ba118…
|
lmata
|
693 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
57ba118…
|
lmata
|
694 |
patch("navegador.graph.export.import_graph", return_value={"nodes": 0, "edges": 0}) as mock_imp: |
|
57ba118…
|
lmata
|
695 |
runner.invoke(main, ["import", "graph.jsonl", "--no-clear"]) |
|
57ba118…
|
lmata
|
696 |
mock_imp.assert_called_once() |
|
57ba118…
|
lmata
|
697 |
assert mock_imp.call_args[1]["clear"] is False |
|
57ba118…
|
lmata
|
698 |
|
|
0838495…
|
lmata
|
699 |
|
|
0838495…
|
lmata
|
700 |
# ── migrate ────────────────────────────────────────────────────────────────── |
|
0838495…
|
lmata
|
701 |
|
|
0838495…
|
lmata
|
702 |
class TestMigrateCommand: |
|
0838495…
|
lmata
|
703 |
def test_migrate_applies_migrations(self): |
|
0838495…
|
lmata
|
704 |
runner = CliRunner() |
|
0838495…
|
lmata
|
705 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
0838495…
|
lmata
|
706 |
patch("navegador.graph.migrations.get_schema_version", return_value=0), \ |
|
0838495…
|
lmata
|
707 |
patch("navegador.graph.migrations.migrate", return_value=[1, 2]) as mock_migrate, \ |
|
0838495…
|
lmata
|
708 |
patch("navegador.graph.migrations.CURRENT_SCHEMA_VERSION", 2): |
|
0838495…
|
lmata
|
709 |
result = runner.invoke(main, ["migrate"]) |
|
0838495…
|
lmata
|
710 |
assert result.exit_code == 0 |
|
0838495…
|
lmata
|
711 |
assert "Migrated" in result.output |
|
0838495…
|
lmata
|
712 |
|
|
0838495…
|
lmata
|
713 |
def test_migrate_already_current(self): |
|
0838495…
|
lmata
|
714 |
runner = CliRunner() |
|
0838495…
|
lmata
|
715 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
0838495…
|
lmata
|
716 |
patch("navegador.graph.migrations.get_schema_version", return_value=2), \ |
|
0838495…
|
lmata
|
717 |
patch("navegador.graph.migrations.migrate", return_value=[]): |
|
0838495…
|
lmata
|
718 |
result = runner.invoke(main, ["migrate"]) |
|
0838495…
|
lmata
|
719 |
assert result.exit_code == 0 |
|
0838495…
|
lmata
|
720 |
assert "up to date" in result.output |
|
0838495…
|
lmata
|
721 |
|
|
0838495…
|
lmata
|
722 |
def test_migrate_check_needed(self): |
|
0838495…
|
lmata
|
723 |
runner = CliRunner() |
|
0838495…
|
lmata
|
724 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
0838495…
|
lmata
|
725 |
patch("navegador.graph.migrations.get_schema_version", return_value=0), \ |
|
0838495…
|
lmata
|
726 |
patch("navegador.graph.migrations.needs_migration", return_value=True), \ |
|
0838495…
|
lmata
|
727 |
patch("navegador.graph.migrations.CURRENT_SCHEMA_VERSION", 2): |
|
0838495…
|
lmata
|
728 |
result = runner.invoke(main, ["migrate", "--check"]) |
|
0838495…
|
lmata
|
729 |
assert result.exit_code == 0 |
|
0838495…
|
lmata
|
730 |
assert "Migration needed" in result.output |
|
0838495…
|
lmata
|
731 |
|
|
0838495…
|
lmata
|
732 |
def test_migrate_check_not_needed(self): |
|
0838495…
|
lmata
|
733 |
runner = CliRunner() |
|
0838495…
|
lmata
|
734 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
0838495…
|
lmata
|
735 |
patch("navegador.graph.migrations.get_schema_version", return_value=2), \ |
|
0838495…
|
lmata
|
736 |
patch("navegador.graph.migrations.needs_migration", return_value=False): |
|
0838495…
|
lmata
|
737 |
result = runner.invoke(main, ["migrate", "--check"]) |
|
0838495…
|
lmata
|
738 |
assert result.exit_code == 0 |
|
0838495…
|
lmata
|
739 |
assert "up to date" in result.output |
|
0838495…
|
lmata
|
740 |
|
|
2270d4f…
|
lmata
|
741 |
|
|
2270d4f…
|
lmata
|
742 |
class TestMcpCommand: |
|
2270d4f…
|
lmata
|
743 |
def test_mcp_command_runs_server(self): |
|
2270d4f…
|
lmata
|
744 |
from contextlib import asynccontextmanager |
|
2270d4f…
|
lmata
|
745 |
|
|
2270d4f…
|
lmata
|
746 |
runner = CliRunner() |
|
2270d4f…
|
lmata
|
747 |
|
|
2270d4f…
|
lmata
|
748 |
@asynccontextmanager |
|
2270d4f…
|
lmata
|
749 |
async def _fake_stdio(): |
|
2270d4f…
|
lmata
|
750 |
yield (MagicMock(), MagicMock()) |
|
2270d4f…
|
lmata
|
751 |
|
|
2270d4f…
|
lmata
|
752 |
async def _fake_run(*args, **kwargs): |
|
2270d4f…
|
lmata
|
753 |
pass |
|
2270d4f…
|
lmata
|
754 |
|
|
2270d4f…
|
lmata
|
755 |
mock_server = MagicMock() |
|
2270d4f…
|
lmata
|
756 |
mock_server.create_initialization_options.return_value = {} |
|
2270d4f…
|
lmata
|
757 |
mock_server.run = _fake_run |
|
2270d4f…
|
lmata
|
758 |
|
|
2270d4f…
|
lmata
|
759 |
with patch("navegador.cli.commands._get_store", return_value=_mock_store()), \ |
|
2270d4f…
|
lmata
|
760 |
patch.dict("sys.modules", { |
|
2270d4f…
|
lmata
|
761 |
"mcp": MagicMock(), |
|
2270d4f…
|
lmata
|
762 |
"mcp.server": MagicMock(), |
|
2270d4f…
|
lmata
|
763 |
"mcp.server.stdio": MagicMock(stdio_server=_fake_stdio), |
|
2270d4f…
|
lmata
|
764 |
}), \ |
|
2270d4f…
|
lmata
|
765 |
patch("navegador.mcp.create_mcp_server", return_value=mock_server): |
|
2270d4f…
|
lmata
|
766 |
result = runner.invoke(main, ["mcp"]) |
|
b663b12…
|
lmata
|
767 |
assert result.exit_code == 0 |