diff --git a/src/tui/cli.py b/src/tui/cli.py index a2e4ea773..0f58a5af8 100644 --- a/src/tui/cli.py +++ b/src/tui/cli.py @@ -345,8 +345,13 @@ def _start_services_cli( console.print("Starting OpenRAG services...", style="bold") async def _inner(): + containers_ok = not container_manager.is_available() + docling_ok = docling_manager.is_running() + docling_message = "Docling serve is already running" if docling_ok else "" + # Start container services if container_manager.is_available(): + containers_ok = True async for item in container_manager.start_services(): # start_services yields (success, message) or (success, message, replace_last) success = item[0] @@ -358,6 +363,9 @@ async def _inner(): else: console.print(f" {message}") + if not success: + containers_ok = False + if not success and "error" in message.lower(): console.print(f" [red]✗ {message}[/red]") else: @@ -365,14 +373,26 @@ async def _inner(): # Start docling if not docling_manager.is_running(): - success, message = await docling_manager.start() - if success: - console.print(f" {message}") + docling_ok, docling_message = await docling_manager.start() + if docling_ok: + console.print(f" {docling_message}") else: - console.print(f" [yellow]{message}[/yellow]") + console.print(f" [yellow]{docling_message}[/yellow]") + + return containers_ok, docling_ok, docling_message try: - asyncio.run(_inner()) + containers_ok, docling_ok, docling_message = asyncio.run(_inner()) + if not containers_ok: + console.print( + "[red]Startup incomplete. Run 'Show status' for details.[/red]" + ) + return + if not docling_ok: + console.print( + f"[yellow]Containers started, but docling-serve did not start: {docling_message}[/yellow]" + ) + return console.print("[green]✓ All services started[/green]") except Exception as e: console.print(f"[red]✗ Error starting services: {e}[/red]") @@ -507,3 +527,4 @@ def _validate_password_strength(password: str) -> str | None: if hint: msg += f" — {hint}" return msg + diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py new file mode 100644 index 000000000..03f4ad236 --- /dev/null +++ b/tests/unit/test_cli.py @@ -0,0 +1,60 @@ +from rich.console import Console + +from tui import cli + + +class StubContainerManager: + def __init__(self, available=True, events=None): + self._available = available + self._events = events or [(True, "Services started successfully", False)] + + def is_available(self): + return self._available + + async def start_services(self): + for event in self._events: + yield event + + +class StubDoclingManager: + def __init__(self, running=False, start_result=(True, "Docling serve starting")): + self._running = running + self._start_result = start_result + + def is_running(self): + return self._running + + async def start(self): + return self._start_result + + +def render_start_output(container_manager, docling_manager): + original_console = cli.console + test_console = Console(record=True, width=120) + cli.console = test_console + try: + cli._start_services_cli(container_manager, docling_manager) + return test_console.export_text() + finally: + cli.console = original_console + + +def test_start_services_reports_partial_start_when_docling_fails(): + output = render_start_output( + StubContainerManager(), + StubDoclingManager(start_result=(False, "Docling serve process exited immediately (code: 1)")), + ) + + assert "All services started" not in output + assert "Containers started, but docling-serve did not start" in output + assert "code: 1" in output + + +def test_start_services_reports_success_when_everything_starts(): + output = render_start_output( + StubContainerManager(), + StubDoclingManager(start_result=(True, "Docling serve starting on http://localhost:5001")), + ) + + assert "All services started" in output + assert "did not start" not in output