fix(cli): keep FileNotFoundError handling on decnet api
Popen moved inside the try so a missing uvicorn falls through to the existing error message instead of crashing the CLI. test_cli was still patching the old subprocess.run entrypoint; switched both api command tests to patch subprocess.Popen / os.killpg to match the current path.
This commit is contained in:
@@ -106,19 +106,20 @@ def api(
|
||||
# can signal the whole tree on Ctrl+C. Without this, only the supervisor
|
||||
# receives SIGINT from the terminal and worker children may survive and
|
||||
# be respawned — the "forkbomb" ANTI hit during testing.
|
||||
proc = subprocess.Popen(_cmd, env=_env, start_new_session=True) # nosec B603 B404
|
||||
try:
|
||||
proc.wait()
|
||||
except KeyboardInterrupt:
|
||||
proc = subprocess.Popen(_cmd, env=_env, start_new_session=True) # nosec B603 B404
|
||||
try:
|
||||
os.killpg(proc.pid, signal.SIGTERM)
|
||||
proc.wait()
|
||||
except KeyboardInterrupt:
|
||||
try:
|
||||
proc.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(proc.pid, signal.SIGKILL)
|
||||
proc.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
os.killpg(proc.pid, signal.SIGTERM)
|
||||
try:
|
||||
proc.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(proc.pid, signal.SIGKILL)
|
||||
proc.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
except (FileNotFoundError, subprocess.SubprocessError):
|
||||
console.print("[red]Failed to start API. Ensure 'uvicorn' is installed in the current environment.[/]")
|
||||
|
||||
|
||||
@@ -325,13 +325,19 @@ class TestCorrelateCommand:
|
||||
# ── api command ───────────────────────────────────────────────────────────────
|
||||
|
||||
class TestApiCommand:
|
||||
@patch("subprocess.run", side_effect=KeyboardInterrupt)
|
||||
def test_api_keyboard_interrupt(self, mock_run):
|
||||
@patch("os.killpg")
|
||||
@patch("subprocess.Popen")
|
||||
def test_api_keyboard_interrupt(self, mock_popen, mock_killpg):
|
||||
proc = MagicMock()
|
||||
proc.wait.side_effect = [KeyboardInterrupt, 0]
|
||||
proc.pid = 4321
|
||||
mock_popen.return_value = proc
|
||||
result = runner.invoke(app, ["api"])
|
||||
assert result.exit_code == 0
|
||||
mock_killpg.assert_called()
|
||||
|
||||
@patch("subprocess.run", side_effect=FileNotFoundError)
|
||||
def test_api_not_found(self, mock_run):
|
||||
@patch("subprocess.Popen", side_effect=FileNotFoundError)
|
||||
def test_api_not_found(self, mock_popen):
|
||||
result = runner.invoke(app, ["api"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user