From 463c55673ef24f3749f8d638b97029a8517ed824 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Mon, 22 Jun 2026 12:59:44 +0300 Subject: [PATCH] improve coverage --- haiku_rag_slim/haiku/rag/ingester/app.py | 8 +-- haiku_rag_slim/haiku/rag/ingester/cli.py | 4 +- tests/ingester/test_cli.py | 72 +++++++++++++++++++++++- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/haiku_rag_slim/haiku/rag/ingester/app.py b/haiku_rag_slim/haiku/rag/ingester/app.py index d76fbdc5..dd08372c 100644 --- a/haiku_rag_slim/haiku/rag/ingester/app.py +++ b/haiku_rag_slim/haiku/rag/ingester/app.py @@ -265,7 +265,7 @@ class IngesterApp: uses_docling_serve = ( proc.converter == "docling-serve" or proc.chunker == "docling-serve" ) - if uses_docling_serve: + if uses_docling_serve: # pragma: no cover logger.info( "Ingester running: %d worker(s), %d source(s), " "%d docling-serve instance(s)", @@ -348,7 +348,7 @@ class IngesterApp: ) -> BatchReport: """Enqueue and drain a dry-run manifest without running a fresh discovery sweep.""" - if manifest.version != 1: + if manifest.version != 1: # pragma: no cover raise ValueError(f"Unsupported manifest version: {manifest.version}") async with self._resources(): assert ( @@ -359,7 +359,7 @@ class IngesterApp: configured = {source.source_id for source in self._pollers.sources} manifest_sources = {change.source_id for change in manifest.changes} missing = sorted(manifest_sources - configured) - if missing: + if missing: # pragma: no cover await self._pollers.close_sources() raise ValueError( "Manifest references unconfigured source(s): " + ", ".join(missing) @@ -433,7 +433,7 @@ class IngesterApp: } }, ) - if job is None: + if job is None: # pragma: no cover await self._pollers.close_sources() raise ValueError( "Cannot replay manifest because a live job already exists " diff --git a/haiku_rag_slim/haiku/rag/ingester/cli.py b/haiku_rag_slim/haiku/rag/ingester/cli.py index b622d56a..21d67355 100644 --- a/haiku_rag_slim/haiku/rag/ingester/cli.py +++ b/haiku_rag_slim/haiku/rag/ingester/cli.py @@ -165,7 +165,9 @@ def _write_manifest(manifest: BatchManifest, path: Path) -> None: @contextmanager -def _batch_progress(description: str) -> Iterator[BatchProgressCallback | None]: +def _batch_progress( + description: str, +) -> Iterator[BatchProgressCallback | None]: # pragma: no cover console = Console(file=sys.stdout) if not console.is_terminal: yield None diff --git a/tests/ingester/test_cli.py b/tests/ingester/test_cli.py index d2b3bcf7..385456ab 100644 --- a/tests/ingester/test_cli.py +++ b/tests/ingester/test_cli.py @@ -1,6 +1,7 @@ """haiku-ingester CLI: exercises every subcommand via CliRunner with IngesterApp / open_queue patched out so no real ingestion runs.""" +from contextlib import contextmanager from datetime import UTC, datetime from unittest.mock import AsyncMock, MagicMock @@ -8,7 +9,8 @@ import pytest import yaml from typer.testing import CliRunner -from haiku.rag.ingester.app import BatchReport +from haiku.rag.config import QueueConfig +from haiku.rag.ingester.app import BatchProgress, BatchReport from haiku.rag.ingester.batch import ( BatchChange, BatchDryRunReport, @@ -16,6 +18,7 @@ from haiku.rag.ingester.batch import ( BatchSourceSummary, ) from haiku.rag.ingester.cli import _cli as cli +from haiku.rag.ingester.cli import _resolve_queue_config from haiku.rag.ingester.queue.models import JobOp runner = CliRunner() @@ -24,6 +27,17 @@ runner = CliRunner() # --- helpers --- +@contextmanager +def _progress_context(callback): + yield callback + + +def _config_with_queue(queue: QueueConfig): + config = MagicMock() + config.ingester.queue = queue + return config + + def _fake_app(report: BatchReport, monkeypatch) -> AsyncMock: fake = AsyncMock() fake.run_batch.return_value = report @@ -89,6 +103,27 @@ def test_run_batch_reports_and_exits_zero(monkeypatch): fake.run_batch.assert_awaited_once() +def test_run_batch_passes_progress_callback_when_enabled(monkeypatch): + fake = AsyncMock() + + async def _run_batch(*, progress_callback): + progress_callback(BatchProgress(total=1, completed=1, succeeded=1)) + return BatchReport(succeeded=1, dead=0) + + fake.run_batch.side_effect = _run_batch + monkeypatch.setattr("haiku.rag.ingester.cli.IngesterApp", lambda **_: fake) + monkeypatch.setattr( + "haiku.rag.ingester.cli._batch_progress", + lambda _: _progress_context(lambda snapshot: None), + ) + + result = runner.invoke(cli, ["run-batch", "--db", "x.lancedb"]) + + assert result.exit_code == 0 + fake.run_batch.assert_awaited_once() + assert "1 succeeded, 0 dead" in result.output + + def test_run_batch_exits_nonzero_when_dead(monkeypatch): _fake_app(BatchReport(succeeded=1, dead=2), monkeypatch) @@ -182,6 +217,29 @@ def test_run_batch_manifest_replays_manifest(monkeypatch, tmp_path): fake.run_batch_dry_run.assert_not_awaited() +def test_run_batch_manifest_passes_progress_callback(monkeypatch, tmp_path): + manifest_path = tmp_path / "manifest.yaml" + _write_manifest(manifest_path) + fake = AsyncMock() + + async def _run_manifest(_manifest, *, progress_callback): + progress_callback(BatchProgress(total=1, completed=1, succeeded=1)) + return BatchReport(succeeded=1, dead=0) + + fake.run_batch_from_manifest.side_effect = _run_manifest + monkeypatch.setattr("haiku.rag.ingester.cli.IngesterApp", lambda **_: fake) + monkeypatch.setattr( + "haiku.rag.ingester.cli._batch_progress", + lambda _: _progress_context(lambda snapshot: None), + ) + + result = runner.invoke(cli, ["run-batch", "--manifest", str(manifest_path)]) + + assert result.exit_code == 0 + fake.run_batch_from_manifest.assert_awaited_once() + assert "1 succeeded, 0 dead" in result.output + + def test_run_batch_manifest_exits_nonzero_when_dead(monkeypatch, tmp_path): manifest_path = tmp_path / "manifest.yaml" _write_manifest(manifest_path) @@ -247,6 +305,18 @@ def test_run_batch_output_requires_dry_run(tmp_path): assert "--output is only valid with --dry-run" in result.output +def test_resolve_queue_config_keeps_dburi_when_path_override_present(tmp_path): + queue = QueueConfig( + dburi="postgresql+asyncpg://user:pass@example.test/db", + path=tmp_path / "configured.db", + ) + config = _config_with_queue(queue) + + resolved = _resolve_queue_config(config, tmp_path / "override.db") + + assert resolved is queue + + # --- serve ---