Navegador

navegador / tests / test_cli.py
Source Blame History 767 lines
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

Keyboard Shortcuts

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