haiku.rag/tests/ingester/test_cli.py
Chris McDonough 1bf2505093
Improve test coverage for cli, filter, registry, and migrations
These files were not touched by the recent performance and
correctness PRs but had coverage gaps. Adds tests for:

- CLI: serve, queue init/migrate, config loading, cli() entry point
  including MigrationRequiredError exit path
- filter: _default_supported_extensions, __call__ watchfiles callback,
  FileFilter with supported_extensions=None
- registry: resolve_adhoc_fetcher with bucket-less S3 URI
- migrations: pragma no-cover on unreachable schema upgrade path
  (no diff migrations exist until SCHEMA_VERSION > 1)
2026-06-01 18:00:47 +03:00

165 lines
4.7 KiB
Python

"""haiku-ingester CLI: exercises every subcommand via CliRunner with
IngesterApp / open_queue patched out so no real ingestion runs."""
from unittest.mock import AsyncMock, MagicMock
import pytest
from typer.testing import CliRunner
from haiku.rag.ingester.app import BatchReport
from haiku.rag.ingester.cli import _cli as cli
runner = CliRunner()
# --- helpers ---
def _fake_app(report: BatchReport, monkeypatch) -> AsyncMock:
fake = AsyncMock()
fake.run_batch.return_value = report
monkeypatch.setattr("haiku.rag.ingester.cli.IngesterApp", lambda **_: fake)
return fake
def test_run_batch_reports_and_exits_zero(monkeypatch):
fake = _fake_app(BatchReport(succeeded=3, dead=0), monkeypatch)
result = runner.invoke(cli, ["run-batch", "--db", "x.lancedb"])
assert result.exit_code == 0
assert "3 succeeded, 0 dead" in result.output
fake.run_batch.assert_awaited_once()
def test_run_batch_exits_nonzero_when_dead(monkeypatch):
_fake_app(BatchReport(succeeded=1, dead=2), monkeypatch)
result = runner.invoke(cli, ["run-batch", "--db", "x.lancedb"])
assert result.exit_code == 1
assert "2 dead" in result.output
def test_run_batch_exits_nonzero_when_sweep_fails(monkeypatch):
_fake_app(BatchReport(succeeded=2, dead=0, failed_sweeps=["docs"]), monkeypatch)
result = runner.invoke(cli, ["run-batch", "--db", "x.lancedb"])
assert result.exit_code == 1
assert "failed to sweep: docs" in result.output
# --- serve ---
def test_serve_invokes_app(monkeypatch):
fake = AsyncMock()
monkeypatch.setattr("haiku.rag.ingester.cli.IngesterApp", lambda **_: fake)
result = runner.invoke(cli, ["serve", "--db", "x.lancedb", "--no-api"])
assert result.exit_code == 0
fake.serve.assert_awaited_once_with(api=False)
def test_serve_passes_host_and_port(monkeypatch):
fake = AsyncMock()
captured = {}
def _capture(**kwargs):
captured.update(kwargs)
return fake
monkeypatch.setattr("haiku.rag.ingester.cli.IngesterApp", _capture)
result = runner.invoke(
cli, ["serve", "--db", "x.lancedb", "--host", "0.0.0.0", "--port", "9999"]
)
assert result.exit_code == 0
assert captured["config"].ingester.api.host == "0.0.0.0"
assert captured["config"].ingester.api.port == 9999
# --- queue init / migrate ---
def test_queue_init(tmp_path, monkeypatch):
fake_conn = AsyncMock()
monkeypatch.setattr(
"haiku.rag.ingester.cli.open_queue", AsyncMock(return_value=fake_conn)
)
db_path = tmp_path / "queue.db"
result = runner.invoke(cli, ["queue", "init", "--queue", str(db_path)])
assert result.exit_code == 0
assert "initialized" in result.output
def test_queue_migrate(tmp_path, monkeypatch):
fake_conn = AsyncMock()
monkeypatch.setattr(
"haiku.rag.ingester.cli.open_queue", AsyncMock(return_value=fake_conn)
)
db_path = tmp_path / "queue.db"
result = runner.invoke(cli, ["queue", "migrate", "--queue", str(db_path)])
assert result.exit_code == 0
assert "up to date" in result.output
# --- config loading ---
def test_load_config_with_explicit_path(tmp_path):
from haiku.rag.ingester.cli import _load_config_with_override
config_file = tmp_path / "test.yaml"
config_file.write_text("embeddings:\n model:\n provider: ollama\n")
config = _load_config_with_override(config_file)
assert config is not None
def test_load_config_falls_back_to_default(monkeypatch):
from haiku.rag.ingester.cli import _load_config_with_override
monkeypatch.setattr("haiku.rag.ingester.cli.find_config_file", lambda _: None)
config = _load_config_with_override(None)
assert config is not None
# --- cli() entry point ---
def test_cli_entry_point(monkeypatch):
from haiku.rag.ingester.cli import cli as cli_entry
mock_cli = MagicMock()
monkeypatch.setattr("haiku.rag.ingester.cli._cli", mock_cli)
monkeypatch.setattr("haiku.rag.ingester.cli.configure_cli_logging", lambda: None)
monkeypatch.setattr("haiku.rag.telemetry.configure", lambda **_: None)
cli_entry()
mock_cli.assert_called_once()
def test_cli_entry_point_exits_on_migration_error(monkeypatch):
from haiku.rag.ingester.cli import cli as cli_entry
from haiku.rag.store.exceptions import MigrationRequiredError
monkeypatch.setattr(
"haiku.rag.ingester.cli._cli",
MagicMock(side_effect=MigrationRequiredError("need migration")),
)
monkeypatch.setattr("haiku.rag.ingester.cli.configure_cli_logging", lambda: None)
monkeypatch.setattr("haiku.rag.telemetry.configure", lambda **_: None)
with pytest.raises(SystemExit) as exc_info:
cli_entry()
assert exc_info.value.code == 1