Skip to content

Commit 84868c5

Browse files
committed
Improve code coverage
1 parent b8cf7fa commit 84868c5

4 files changed

Lines changed: 998 additions & 0 deletions

File tree

tests/test_cli_extended.py

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
"""Extended tests for CLI commands."""
2+
3+
import tempfile
4+
from pathlib import Path
5+
from unittest.mock import MagicMock, patch
6+
7+
import pytest
8+
9+
from entity_manager.cli import configure_logging, create, get_backend, list, read, update
10+
from entity_manager.config import Config
11+
from entity_manager.models import Entity
12+
13+
14+
@pytest.fixture
15+
def temp_config_dir():
16+
"""Create a temporary directory for config."""
17+
with tempfile.TemporaryDirectory() as tmpdir:
18+
yield Path(tmpdir)
19+
20+
21+
@pytest.fixture
22+
def mock_backend():
23+
"""Create a mock backend."""
24+
backend = MagicMock()
25+
backend.create.return_value = Entity(
26+
id="1",
27+
title="Test Task",
28+
description="Test description",
29+
status="open",
30+
labels={},
31+
assignee=None,
32+
)
33+
backend.read.return_value = Entity(
34+
id="1",
35+
title="Test Task",
36+
description="Test description",
37+
status="open",
38+
labels={"type": "bug"},
39+
assignee="user1",
40+
metadata={"url": "http://example.com"},
41+
)
42+
backend.update.return_value = Entity(
43+
id="1",
44+
title="Updated Task",
45+
description="Updated description",
46+
status="closed",
47+
labels={},
48+
assignee=None,
49+
)
50+
backend.list_entities.return_value = [
51+
Entity(
52+
id="1",
53+
title="Task 1",
54+
description="",
55+
status="open",
56+
labels={"priority": "high"},
57+
assignee=None,
58+
),
59+
Entity(
60+
id="2",
61+
title="Task 2",
62+
description="",
63+
status="closed",
64+
labels={},
65+
assignee=None,
66+
),
67+
]
68+
return backend
69+
70+
71+
def test_configure_logging():
72+
"""Test configuring logging."""
73+
# Should not raise an error
74+
configure_logging("debug")
75+
configure_logging("info")
76+
configure_logging("warning")
77+
configure_logging("error")
78+
configure_logging("critical")
79+
80+
81+
def test_get_backend_github(temp_config_dir):
82+
"""Test getting GitHub backend."""
83+
config = Config(config_dir=temp_config_dir)
84+
config.set("backend", "github")
85+
config.set("github.owner", "test_owner")
86+
config.set("github.repository", "test_repo")
87+
config.set("github.token", "test_token")
88+
89+
with patch("entity_manager.cli.get_config", return_value=config):
90+
with patch("entity_manager.cli.GitHubBackend") as mock_github:
91+
backend = get_backend()
92+
assert backend is not None
93+
mock_github.assert_called_once_with(owner="test_owner", repo="test_repo", token="test_token")
94+
95+
96+
def test_get_backend_github_missing_config(temp_config_dir):
97+
"""Test getting GitHub backend with missing config."""
98+
config = Config(config_dir=temp_config_dir)
99+
config.set("backend", "github")
100+
101+
with patch("entity_manager.cli.get_config", return_value=config):
102+
with pytest.raises(ValueError, match="GitHub owner and repo not configured"):
103+
get_backend()
104+
105+
106+
def test_get_backend_backlog(temp_config_dir):
107+
"""Test getting Backlog backend."""
108+
config = Config(config_dir=temp_config_dir)
109+
config.set("backend", "backlog")
110+
config.set("backlog.path", "/path/to/backlog.md")
111+
112+
with patch("entity_manager.cli.get_config", return_value=config):
113+
with patch("entity_manager.cli.BacklogBackend") as mock_backlog:
114+
backend = get_backend()
115+
assert backend is not None
116+
mock_backlog.assert_called_once_with(backlog_path="/path/to/backlog.md")
117+
118+
119+
def test_get_backend_beads(temp_config_dir):
120+
"""Test getting Beads backend."""
121+
config = Config(config_dir=temp_config_dir)
122+
config.set("backend", "beads")
123+
config.set("beads.project_path", "/path/to/project")
124+
125+
with patch("entity_manager.cli.get_config", return_value=config):
126+
with patch("entity_manager.cli.BeadsBackend") as mock_beads:
127+
backend = get_backend()
128+
assert backend is not None
129+
mock_beads.assert_called_once_with(project_path="/path/to/project")
130+
131+
132+
def test_get_backend_markdown(temp_config_dir):
133+
"""Test getting Markdown backend."""
134+
config = Config(config_dir=temp_config_dir)
135+
config.set("backend", "markdown")
136+
config.set("markdown.directory_path", "/path/to/markdown")
137+
138+
with patch("entity_manager.cli.get_config", return_value=config):
139+
with patch("entity_manager.cli.MarkdownBackend") as mock_markdown:
140+
backend = get_backend()
141+
assert backend is not None
142+
mock_markdown.assert_called_once_with(directory_path="/path/to/markdown")
143+
144+
145+
def test_get_backend_markdown_default_path(temp_config_dir):
146+
"""Test getting Markdown backend with default path."""
147+
config = Config(config_dir=temp_config_dir)
148+
config.set("backend", "markdown")
149+
150+
with patch("entity_manager.cli.get_config", return_value=config):
151+
with patch("entity_manager.cli.MarkdownBackend") as mock_markdown:
152+
backend = get_backend()
153+
assert backend is not None
154+
mock_markdown.assert_called_once_with(directory_path=".")
155+
156+
157+
def test_get_backend_sqlite(temp_config_dir):
158+
"""Test getting SQLite backend."""
159+
config = Config(config_dir=temp_config_dir)
160+
config.set("backend", "sqlite")
161+
config.set("sqlite.db_path", "/path/to/db.sqlite")
162+
163+
with patch("entity_manager.cli.get_config", return_value=config):
164+
with patch("entity_manager.cli.SQLiteBackend") as mock_sqlite:
165+
backend = get_backend()
166+
assert backend is not None
167+
mock_sqlite.assert_called_once_with(db_path="/path/to/db.sqlite")
168+
169+
170+
def test_get_backend_unknown(temp_config_dir):
171+
"""Test getting unknown backend raises error."""
172+
config = Config(config_dir=temp_config_dir)
173+
config.set("backend", "unknown_backend")
174+
175+
with patch("entity_manager.cli.get_config", return_value=config):
176+
with pytest.raises(ValueError, match="Unknown backend"):
177+
get_backend()
178+
179+
180+
def test_create_entity_minimal(mock_backend, capsys):
181+
"""Test creating entity with minimal fields."""
182+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
183+
create("Test Task")
184+
185+
mock_backend.create.assert_called_once_with(
186+
title="Test Task",
187+
description="",
188+
labels={},
189+
assignee=None,
190+
)
191+
captured = capsys.readouterr()
192+
assert "Created entity 1: Test Task" in captured.out
193+
194+
195+
def test_create_entity_with_all_fields(mock_backend, capsys):
196+
"""Test creating entity with all fields."""
197+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
198+
create("Test Task", description="Test desc", labels="type:bug,priority:high", assignee="user1")
199+
200+
mock_backend.create.assert_called_once_with(
201+
title="Test Task",
202+
description="Test desc",
203+
labels={"type": "bug", "priority": "high"},
204+
assignee="user1",
205+
)
206+
207+
208+
def test_create_entity_with_labels_no_value(mock_backend):
209+
"""Test creating entity with labels without values."""
210+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
211+
create("Test Task", labels="bug,feature")
212+
213+
mock_backend.create.assert_called_once()
214+
call_args = mock_backend.create.call_args
215+
assert call_args[1]["labels"] == {"bug": "", "feature": ""}
216+
217+
218+
def test_read_entity(mock_backend, capsys):
219+
"""Test reading an entity."""
220+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
221+
read("1")
222+
223+
mock_backend.read.assert_called_once_with("1")
224+
captured = capsys.readouterr()
225+
assert "Entity: 1" in captured.out
226+
assert "Title: Test Task" in captured.out
227+
assert "Description: Test description" in captured.out
228+
assert "Status: open" in captured.out
229+
assert "Labels: type:bug" in captured.out
230+
assert "Assignee: user1" in captured.out
231+
assert "URL: http://example.com" in captured.out
232+
233+
234+
def test_read_entity_no_labels(mock_backend, capsys):
235+
"""Test reading entity without labels."""
236+
mock_backend.read.return_value = Entity(
237+
id="1",
238+
title="Test Task",
239+
description="Test description",
240+
status="open",
241+
labels=None,
242+
assignee=None,
243+
)
244+
245+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
246+
read("1")
247+
248+
captured = capsys.readouterr()
249+
assert "Labels:" not in captured.out
250+
assert "Assignee:" not in captured.out
251+
252+
253+
def test_update_entity_title(mock_backend, capsys):
254+
"""Test updating entity title."""
255+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
256+
update("1", title="New Title")
257+
258+
mock_backend.update.assert_called_once_with(
259+
entity_id="1",
260+
title="New Title",
261+
description=None,
262+
labels=None,
263+
status=None,
264+
assignee=None,
265+
)
266+
captured = capsys.readouterr()
267+
assert "Updated entity 1: Updated Task" in captured.out
268+
269+
270+
def test_update_entity_with_labels(mock_backend):
271+
"""Test updating entity with labels."""
272+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
273+
update("1", labels="type:bug,priority:high")
274+
275+
call_args = mock_backend.update.call_args
276+
assert call_args[1]["labels"] == {"type": "bug", "priority": "high"}
277+
278+
279+
def test_update_entity_all_fields(mock_backend):
280+
"""Test updating entity with all fields."""
281+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
282+
update("1", title="New Title", description="New desc", labels="type:bug", status="closed", assignee="user2")
283+
284+
mock_backend.update.assert_called_once_with(
285+
entity_id="1",
286+
title="New Title",
287+
description="New desc",
288+
labels={"type": "bug"},
289+
status="closed",
290+
assignee="user2",
291+
)
292+
293+
294+
def test_list_entities(mock_backend, capsys):
295+
"""Test listing entities."""
296+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
297+
list()
298+
299+
mock_backend.list_entities.assert_called_once_with(filters=None, sort_by=None, limit=None)
300+
captured = capsys.readouterr()
301+
assert "Found 2 entity(ies):" in captured.out
302+
assert "● 1: Task 1 [priority:high]" in captured.out
303+
assert "○ 2: Task 2" in captured.out
304+
305+
306+
def test_list_entities_with_filter(mock_backend):
307+
"""Test listing entities with filter."""
308+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
309+
list(filter="status=open,assignee=user1")
310+
311+
call_args = mock_backend.list_entities.call_args
312+
assert call_args[1]["filters"] == {"status": "open", "assignee": "user1"}
313+
314+
315+
def test_list_entities_with_sort(mock_backend):
316+
"""Test listing entities with sort."""
317+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
318+
list(sort="title")
319+
320+
call_args = mock_backend.list_entities.call_args
321+
assert call_args[1]["sort_by"] == "title"
322+
323+
324+
def test_list_entities_with_limit(mock_backend):
325+
"""Test listing entities with limit."""
326+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
327+
list(limit=10)
328+
329+
call_args = mock_backend.list_entities.call_args
330+
assert call_args[1]["limit"] == 10
331+
332+
333+
def test_list_entities_no_labels(mock_backend, capsys):
334+
"""Test listing entities without labels."""
335+
mock_backend.list_entities.return_value = [
336+
Entity(id="1", title="Task 1", description="", status="open", labels=None, assignee=None)
337+
]
338+
339+
with patch("entity_manager.cli.get_backend", return_value=mock_backend):
340+
list()
341+
342+
captured = capsys.readouterr()
343+
assert "● 1: Task 1\n" in captured.out

0 commit comments

Comments
 (0)