Navegador

navegador / tests / test_editor.py
Blame History Raw 219 lines
1
"""Tests for editor integration — EditorIntegration class and CLI command."""
2
3
import json
4
from pathlib import Path
5
from unittest.mock import MagicMock, patch
6
7
import pytest
8
from click.testing import CliRunner
9
10
from navegador.cli.commands import main
11
from navegador.editor import SUPPORTED_EDITORS, EditorIntegration
12
13
# ── EditorIntegration unit tests ──────────────────────────────────────────────
14
15
16
class TestEditorIntegration:
17
def setup_method(self):
18
self.integration = EditorIntegration(db=".navegador/graph.db")
19
20
# config_for
21
22
def test_config_for_claude_code(self):
23
cfg = self.integration.config_for("claude-code")
24
assert cfg["mcpServers"]["navegador"]["command"] == "navegador"
25
assert cfg["mcpServers"]["navegador"]["args"] == ["mcp", "--db", ".navegador/graph.db"]
26
27
def test_config_for_cursor(self):
28
cfg = self.integration.config_for("cursor")
29
assert cfg["mcpServers"]["navegador"]["command"] == "navegador"
30
assert cfg["mcpServers"]["navegador"]["args"] == ["mcp", "--db", ".navegador/graph.db"]
31
32
def test_config_for_codex(self):
33
cfg = self.integration.config_for("codex")
34
assert cfg["mcpServers"]["navegador"]["command"] == "navegador"
35
assert cfg["mcpServers"]["navegador"]["args"] == ["mcp", "--db", ".navegador/graph.db"]
36
37
def test_config_for_windsurf(self):
38
cfg = self.integration.config_for("windsurf")
39
assert cfg["mcpServers"]["navegador"]["command"] == "navegador"
40
assert cfg["mcpServers"]["navegador"]["args"] == ["mcp", "--db", ".navegador/graph.db"]
41
42
def test_config_for_invalid_editor_raises(self):
43
with pytest.raises(ValueError, match="Unsupported editor"):
44
self.integration.config_for("vscode")
45
46
# custom db path
47
48
def test_custom_db_path_reflected_in_config(self):
49
integration = EditorIntegration(db="/custom/path/graph.db")
50
cfg = integration.config_for("cursor")
51
assert cfg["mcpServers"]["navegador"]["args"][2] == "/custom/path/graph.db"
52
53
# config_json
54
55
def test_config_json_is_valid_json(self):
56
raw = self.integration.config_json("claude-code")
57
parsed = json.loads(raw)
58
assert "mcpServers" in parsed
59
60
def test_config_json_is_pretty_printed(self):
61
raw = self.integration.config_json("cursor")
62
assert "\n" in raw # indented
63
64
# config_path
65
66
def test_config_path_claude_code(self):
67
assert self.integration.config_path("claude-code") == ".claude/mcp.json"
68
69
def test_config_path_cursor(self):
70
assert self.integration.config_path("cursor") == ".cursor/mcp.json"
71
72
def test_config_path_codex(self):
73
assert self.integration.config_path("codex") == ".codex/config.json"
74
75
def test_config_path_windsurf(self):
76
assert self.integration.config_path("windsurf") == ".windsurf/mcp.json"
77
78
def test_config_path_invalid_editor_raises(self):
79
with pytest.raises(ValueError, match="Unsupported editor"):
80
self.integration.config_path("sublime")
81
82
# write_config
83
84
def test_write_config_creates_file(self, tmp_path):
85
written = self.integration.write_config("claude-code", base_dir=str(tmp_path))
86
assert written.exists()
87
parsed = json.loads(written.read_text())
88
assert "mcpServers" in parsed
89
90
def test_write_config_creates_parent_dirs(self, tmp_path):
91
written = self.integration.write_config("windsurf", base_dir=str(tmp_path))
92
assert (tmp_path / ".windsurf").is_dir()
93
assert written.name == "mcp.json"
94
95
def test_write_config_returns_path_object(self, tmp_path):
96
result = self.integration.write_config("cursor", base_dir=str(tmp_path))
97
assert isinstance(result, Path)
98
99
def test_write_config_content_matches_config_json(self, tmp_path):
100
written = self.integration.write_config("codex", base_dir=str(tmp_path))
101
assert written.read_text() == self.integration.config_json("codex")
102
103
# all editors covered
104
105
def test_all_editors_supported(self):
106
for ed in SUPPORTED_EDITORS:
107
cfg = self.integration.config_for(ed)
108
assert "mcpServers" in cfg
109
110
111
# ── CLI tests ─────────────────────────────────────────────────────────────────
112
113
114
class TestEditorSetupCommand:
115
# Basic output
116
117
def test_claude_code_outputs_json(self):
118
runner = CliRunner()
119
result = runner.invoke(main, ["editor", "setup", "claude-code"])
120
assert result.exit_code == 0
121
parsed = json.loads(result.output)
122
assert "mcpServers" in parsed
123
assert parsed["mcpServers"]["navegador"]["command"] == "navegador"
124
125
def test_cursor_outputs_json(self):
126
runner = CliRunner()
127
result = runner.invoke(main, ["editor", "setup", "cursor"])
128
assert result.exit_code == 0
129
parsed = json.loads(result.output)
130
assert "mcpServers" in parsed
131
132
def test_codex_outputs_json(self):
133
runner = CliRunner()
134
result = runner.invoke(main, ["editor", "setup", "codex"])
135
assert result.exit_code == 0
136
parsed = json.loads(result.output)
137
assert "mcpServers" in parsed
138
139
def test_windsurf_outputs_json(self):
140
runner = CliRunner()
141
result = runner.invoke(main, ["editor", "setup", "windsurf"])
142
assert result.exit_code == 0
143
parsed = json.loads(result.output)
144
assert "mcpServers" in parsed
145
146
# --db option
147
148
def test_custom_db_reflected_in_output(self):
149
runner = CliRunner()
150
result = runner.invoke(main, ["editor", "setup", "cursor", "--db", "/custom/graph.db"])
151
assert result.exit_code == 0
152
parsed = json.loads(result.output)
153
assert parsed["mcpServers"]["navegador"]["args"][2] == "/custom/graph.db"
154
155
# 'all' generates for all editors
156
157
def test_all_generates_for_all_editors(self):
158
runner = CliRunner()
159
result = runner.invoke(main, ["editor", "setup", "all"])
160
assert result.exit_code == 0
161
# Each editor name should appear in the output header
162
for ed in SUPPORTED_EDITORS:
163
assert ed in result.output
164
165
def test_all_output_contains_multiple_json_blocks(self):
166
runner = CliRunner()
167
result = runner.invoke(main, ["editor", "setup", "all"])
168
assert result.exit_code == 0
169
# Count occurrences of "mcpServers" — one per editor
170
assert result.output.count("mcpServers") == len(SUPPORTED_EDITORS)
171
172
# Invalid editor name
173
174
def test_invalid_editor_exits_nonzero(self):
175
runner = CliRunner()
176
result = runner.invoke(main, ["editor", "setup", "vscode"])
177
assert result.exit_code != 0
178
179
def test_invalid_editor_shows_error(self):
180
runner = CliRunner()
181
result = runner.invoke(main, ["editor", "setup", "vim"])
182
assert "vim" in result.output or "vim" in (result.exception or "")
183
184
# --write flag
185
186
def test_write_flag_creates_file(self):
187
runner = CliRunner()
188
with runner.isolated_filesystem():
189
result = runner.invoke(main, ["editor", "setup", "claude-code", "--write"])
190
assert result.exit_code == 0
191
written = Path(".claude/mcp.json")
192
assert written.exists()
193
parsed = json.loads(written.read_text())
194
assert "mcpServers" in parsed
195
196
def test_write_flag_cursor_creates_correct_file(self):
197
runner = CliRunner()
198
with runner.isolated_filesystem():
199
result = runner.invoke(main, ["editor", "setup", "cursor", "--write"])
200
assert result.exit_code == 0
201
assert Path(".cursor/mcp.json").exists()
202
203
def test_write_flag_all_creates_all_files(self):
204
runner = CliRunner()
205
with runner.isolated_filesystem():
206
result = runner.invoke(main, ["editor", "setup", "all", "--write"])
207
assert result.exit_code == 0
208
assert Path(".claude/mcp.json").exists()
209
assert Path(".cursor/mcp.json").exists()
210
assert Path(".codex/config.json").exists()
211
assert Path(".windsurf/mcp.json").exists()
212
213
def test_write_flag_shows_written_path(self):
214
runner = CliRunner()
215
with runner.isolated_filesystem():
216
result = runner.invoke(main, ["editor", "setup", "windsurf", "--write"])
217
assert result.exit_code == 0
218
assert "Written" in result.output or ".windsurf" in result.output
219

Keyboard Shortcuts

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