Skip to content

Commit e575a26

Browse files
committed
Increase code coverage
1 parent 07eeb89 commit e575a26

2 files changed

Lines changed: 651 additions & 0 deletions

File tree

tests/backends/test_beads.py

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,312 @@ def test_entity_id_conversion(mock_run: MagicMock) -> None:
139139
assert backend._entity_id_to_bead_id("bd-a1b2") == "bd-a1b2"
140140
assert backend._entity_id_to_bead_id("123") == "bd-123"
141141
assert backend._entity_id_to_bead_id("a1b2") == "bd-a1b2"
142+
143+
144+
@patch("entity_manager.backends.beads.subprocess.run")
145+
def test_update_issue(mock_run: MagicMock) -> None:
146+
"""Test updating a beads issue."""
147+
# Mock init
148+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
149+
backend = BeadsBackend()
150+
151+
# Mock read call (for update to fetch current issue)
152+
mock_run.return_value = MagicMock(
153+
returncode=0,
154+
stdout=json.dumps(
155+
{
156+
"id": "bd-a1b2",
157+
"title": "Updated Title",
158+
"description": "Updated description",
159+
"status": "closed",
160+
"labels": [],
161+
"assignee": "bob",
162+
}
163+
),
164+
stderr="",
165+
)
166+
167+
entity = backend.update("bd-a1b2", title="Updated Title", status="closed", assignee="bob")
168+
assert entity.title == "Updated Title"
169+
assert entity.status == "closed"
170+
assert entity.assignee == "bob"
171+
172+
173+
@patch("entity_manager.backends.beads.subprocess.run")
174+
def test_update_issue_with_labels(mock_run: MagicMock) -> None:
175+
"""Test updating a beads issue with labels."""
176+
# Mock init
177+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
178+
backend = BeadsBackend()
179+
180+
# Mock read call for getting current labels
181+
mock_run.return_value = MagicMock(
182+
returncode=0,
183+
stdout=json.dumps(
184+
{
185+
"id": "bd-a1b2",
186+
"title": "Test",
187+
"description": "",
188+
"status": "open",
189+
"labels": ["old:label"],
190+
"assignee": None,
191+
}
192+
),
193+
stderr="",
194+
)
195+
196+
# Update with new labels
197+
entity = backend.update("bd-a1b2", labels={"new": "label", "tag": ""})
198+
assert entity.labels == {"old": "label"} # Will have old labels from read mock
199+
200+
201+
@patch("entity_manager.backends.beads.subprocess.run")
202+
def test_delete_issue(mock_run: MagicMock) -> None:
203+
"""Test deleting (closing) a beads issue."""
204+
# Mock init
205+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
206+
backend = BeadsBackend()
207+
208+
# Mock close call
209+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"result": "success"}), stderr="")
210+
211+
backend.delete(["bd-a1b2", "bd-c3d4"])
212+
213+
# Verify close was called for each ID
214+
assert mock_run.call_count >= 2
215+
216+
217+
@patch("entity_manager.backends.beads.subprocess.run")
218+
def test_list_with_filters(mock_run: MagicMock) -> None:
219+
"""Test listing with filters."""
220+
# Mock init
221+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
222+
backend = BeadsBackend()
223+
224+
# Mock list call
225+
mock_run.return_value = MagicMock(
226+
returncode=0,
227+
stdout=json.dumps(
228+
[
229+
{
230+
"id": "bd-a1b2",
231+
"title": "Bug Issue",
232+
"description": "",
233+
"status": "open",
234+
"labels": [],
235+
"assignee": "alice",
236+
}
237+
]
238+
),
239+
stderr="",
240+
)
241+
242+
entities = backend.list_entities(filters={"status": "open", "assignee": "alice"})
243+
assert len(entities) == 1
244+
assert entities[0].assignee == "alice"
245+
246+
247+
@patch("entity_manager.backends.beads.subprocess.run")
248+
def test_list_with_limit(mock_run: MagicMock) -> None:
249+
"""Test listing with limit."""
250+
# Mock init
251+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
252+
backend = BeadsBackend()
253+
254+
# Mock list call with multiple items
255+
mock_run.return_value = MagicMock(
256+
returncode=0,
257+
stdout=json.dumps(
258+
[
259+
{"id": f"bd-{i}", "title": f"Issue {i}", "description": "", "status": "open", "labels": []}
260+
for i in range(10)
261+
]
262+
),
263+
stderr="",
264+
)
265+
266+
entities = backend.list_entities(limit=5)
267+
assert len(entities) == 5
268+
269+
270+
@patch("entity_manager.backends.beads.subprocess.run")
271+
def test_remove_link(mock_run: MagicMock) -> None:
272+
"""Test removing dependencies."""
273+
# Mock init
274+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
275+
backend = BeadsBackend()
276+
277+
# Mock remove link call
278+
mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
279+
280+
backend.remove_link("bd-a1b2", ["bd-c3d4"], "blocks")
281+
282+
# Verify the command was called
283+
assert mock_run.call_count >= 1
284+
285+
286+
@patch("entity_manager.backends.beads.subprocess.run")
287+
def test_list_links(mock_run: MagicMock) -> None:
288+
"""Test listing links for an issue."""
289+
# Mock init
290+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
291+
backend = BeadsBackend()
292+
293+
# Mock show call with dependencies
294+
mock_run.return_value = MagicMock(
295+
returncode=0,
296+
stdout=json.dumps(
297+
{
298+
"id": "bd-a1b2",
299+
"title": "Test",
300+
"description": "",
301+
"status": "open",
302+
"labels": [],
303+
"dependencies": [
304+
{"type": "blocks", "target_id": "bd-c3d4"},
305+
{"type": "related", "target_id": "bd-e5f6"},
306+
],
307+
}
308+
),
309+
stderr="",
310+
)
311+
312+
links = backend.list_links("bd-a1b2")
313+
assert len(links) == 2
314+
assert links[0].link_type == "blocks"
315+
assert links[1].link_type == "related"
316+
317+
318+
@patch("entity_manager.backends.beads.subprocess.run")
319+
def test_list_links_with_filter(mock_run: MagicMock) -> None:
320+
"""Test listing links with type filter."""
321+
# Mock init
322+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
323+
backend = BeadsBackend()
324+
325+
# Mock show call with dependencies
326+
mock_run.return_value = MagicMock(
327+
returncode=0,
328+
stdout=json.dumps(
329+
{
330+
"id": "bd-a1b2",
331+
"title": "Test",
332+
"description": "",
333+
"status": "open",
334+
"labels": [],
335+
"dependencies": [
336+
{"type": "blocks", "target_id": "bd-c3d4"},
337+
{"type": "related", "target_id": "bd-e5f6"},
338+
],
339+
}
340+
),
341+
stderr="",
342+
)
343+
344+
links = backend.list_links("bd-a1b2", link_type="blocks")
345+
assert len(links) == 1
346+
assert links[0].link_type == "blocks"
347+
348+
349+
@patch("entity_manager.backends.beads.subprocess.run")
350+
def test_get_link_tree(mock_run: MagicMock) -> None:
351+
"""Test getting dependency tree."""
352+
# Mock init
353+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
354+
backend = BeadsBackend()
355+
356+
# Use side_effect to handle multiple calls in sequence
357+
mock_run.side_effect = [
358+
# First call: read (show) command
359+
MagicMock(
360+
returncode=0,
361+
stdout=json.dumps(
362+
{
363+
"id": "bd-a1b2",
364+
"title": "Test Issue",
365+
"description": "",
366+
"status": "open",
367+
"labels": [],
368+
}
369+
),
370+
stderr="",
371+
),
372+
# Second call: tree command
373+
MagicMock(
374+
returncode=0,
375+
stdout=json.dumps(
376+
{
377+
"children": [{"id": "bd-c3d4", "title": "Child"}],
378+
"blocking": [],
379+
"blocked_by": [],
380+
"parent": {"id": "bd-e5f6", "title": "Parent"},
381+
}
382+
),
383+
stderr="",
384+
),
385+
]
386+
387+
tree = backend.get_link_tree("bd-a1b2")
388+
assert tree["entity"]["id"] == "bd-a1b2"
389+
assert len(tree["links"]["children"]) == 1
390+
assert len(tree["links"]["parent"]) == 1
391+
392+
393+
@patch("entity_manager.backends.beads.subprocess.run")
394+
def test_find_cycles(mock_run: MagicMock) -> None:
395+
"""Test finding cycles in dependency graph."""
396+
# Mock init
397+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
398+
backend = BeadsBackend()
399+
400+
# Mock cycles call
401+
mock_run.return_value = MagicMock(
402+
returncode=0,
403+
stdout=json.dumps([["bd-a1b2", "bd-c3d4", "bd-a1b2"]]),
404+
stderr="",
405+
)
406+
407+
cycles = backend.find_cycles()
408+
assert len(cycles) == 1
409+
assert len(cycles[0]) == 3
410+
411+
412+
@patch("entity_manager.backends.beads.subprocess.run")
413+
def test_create_with_labels(mock_run: MagicMock) -> None:
414+
"""Test creating issue with labels."""
415+
# Mock init
416+
mock_run.return_value = MagicMock(returncode=0, stdout=json.dumps({"status": "ok"}), stderr="")
417+
backend = BeadsBackend()
418+
419+
# Mock create call
420+
mock_run.return_value = MagicMock(
421+
returncode=0,
422+
stdout=json.dumps(
423+
{
424+
"id": "bd-a1b2",
425+
"title": "Test",
426+
"description": "",
427+
"status": "open",
428+
"labels": ["bug", "priority:high"],
429+
"assignee": None,
430+
}
431+
),
432+
stderr="",
433+
)
434+
435+
entity = backend.create("Test", labels={"bug": "", "priority": "high"})
436+
assert entity.labels == {"bug": "", "priority": "high"}
437+
438+
439+
@patch("entity_manager.backends.beads.subprocess.run")
440+
def test_init_failure(mock_run: MagicMock) -> None:
441+
"""Test initialization failure."""
442+
import subprocess
443+
444+
mock_run.side_effect = subprocess.CalledProcessError(1, ["bd", "info"], stderr="bd not found")
445+
446+
try:
447+
BeadsBackend()
448+
assert False, "Should have raised ValueError"
449+
except ValueError as e:
450+
assert "bd command failed" in str(e)

0 commit comments

Comments
 (0)