Skip to content

Commit d3fb77b

Browse files
divideby0claude
andcommitted
test: add CLI tool hang regression tests
Add tests to verify CLI commands (tool, status) exit cleanly without hanging. The tests reproduce a bug where CLI commands hang indefinitely after displaying output. The root cause is that ensure_initialization() creates database connections via asyncio.run() that are not cleaned up before the event loop closes. These tests will fail until the fix is applied. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Cedric Hurst <cedric@spantree.net>
1 parent 897b1ed commit d3fb77b

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

tests/cli/test_cli_tool_exit.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""Test that CLI tool commands exit cleanly without hanging.
2+
3+
This test ensures that CLI commands properly clean up database connections
4+
on exit, preventing process hangs. See GitHub issue for details.
5+
6+
The issue occurs when:
7+
1. ensure_initialization() calls asyncio.run(initialize_app())
8+
2. initialize_app() creates global database connections via db.get_or_create_db()
9+
3. When asyncio.run() completes, the event loop closes
10+
4. But the global database engine holds async connections that prevent clean exit
11+
5. Process hangs indefinitely
12+
13+
The fix ensures db.shutdown_db() is called before asyncio.run() returns.
14+
"""
15+
16+
import subprocess
17+
import sys
18+
19+
import pytest
20+
21+
22+
class TestCLIToolExit:
23+
"""Test that CLI tool commands exit cleanly."""
24+
25+
@pytest.mark.parametrize(
26+
"command",
27+
[
28+
["tool", "--help"],
29+
["tool", "write-note", "--help"],
30+
["tool", "read-note", "--help"],
31+
["tool", "search-notes", "--help"],
32+
["tool", "build-context", "--help"],
33+
["status"], # Also affected by same issue
34+
],
35+
)
36+
def test_cli_command_exits_cleanly(self, command: list[str]):
37+
"""Test that CLI commands exit without hanging.
38+
39+
Each command should complete within the timeout without requiring
40+
manual termination (Ctrl+C).
41+
"""
42+
full_command = [sys.executable, "-m", "basic_memory.cli.main"] + command
43+
44+
try:
45+
result = subprocess.run(
46+
full_command,
47+
capture_output=True,
48+
text=True,
49+
timeout=10.0, # 10 second timeout - commands should complete in ~2s
50+
)
51+
# Command should exit with code 0 for --help
52+
assert result.returncode == 0, f"Command failed: {result.stderr}"
53+
except subprocess.TimeoutExpired:
54+
pytest.fail(
55+
f"Command '{' '.join(command)}' hung and did not exit within timeout. "
56+
"This indicates database connections are not being cleaned up properly."
57+
)
58+
59+
def test_ensure_initialization_exits_cleanly(self):
60+
"""Test that ensure_initialization doesn't cause process hang.
61+
62+
This test directly tests the initialization function that's called
63+
by CLI commands, ensuring it cleans up database connections properly.
64+
"""
65+
code = """
66+
import asyncio
67+
from basic_memory.config import ConfigManager
68+
from basic_memory.services.initialization import ensure_initialization
69+
70+
app_config = ConfigManager().config
71+
ensure_initialization(app_config)
72+
print("OK")
73+
"""
74+
try:
75+
result = subprocess.run(
76+
[sys.executable, "-c", code],
77+
capture_output=True,
78+
text=True,
79+
timeout=10.0,
80+
)
81+
assert "OK" in result.stdout, f"Unexpected output: {result.stdout}"
82+
except subprocess.TimeoutExpired:
83+
pytest.fail(
84+
"ensure_initialization() caused process hang. "
85+
"Database connections are not being cleaned up before event loop closes."
86+
)

0 commit comments

Comments
 (0)