"""CLI smoke tests. Most CLI behavior is now indirectly covered by test_api.py (the CLI is a thin httpx wrapper over the JSON API). These tests verify the local-only verbs (`init`) and the "server unreachable" error path. """ from __future__ import annotations from pathlib import Path import pytest from typer.testing import CliRunner from claire.cli import app def test_init_creates_config_and_db(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data")) monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config")) runner = CliRunner() result = runner.invoke(app, ["init"]) assert result.exit_code == 0, result.output assert "machine_id" in result.output assert (tmp_path / "config" / "claire" / "claire.toml").exists() assert (tmp_path / "data" / "claire" / "claire.db").exists() def test_cli_emits_helpful_error_when_server_down( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """`claire project list` should exit 3 with a clear message when the server isn't running. We point at a definitely-dead port to force ConnectError.""" monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data")) monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config")) # Init creates the config so we can mutate it. runner = CliRunner() runner.invoke(app, ["init"]) # Point the CLI at a dead port. cfg_path = tmp_path / "config" / "claire" / "claire.toml" content = cfg_path.read_text() content = content.replace("port = 8765", "port = 1") cfg_path.write_text(content) result = runner.invoke(app, ["project", "list"]) assert result.exit_code == 3 assert "could not reach claire server" in result.output assert "claire serve" in result.output