Files
kg-scr/tests/test_doctor.py
Aman Nalakath 7de0e716eb
Some checks failed
tests / core (macos-latest, py3.11) (push) Has been cancelled
tests / core (macos-latest, py3.12) (push) Has been cancelled
tests / core (macos-latest, py3.13) (push) Has been cancelled
tests / core (ubuntu-latest, py3.11) (push) Has been cancelled
tests / core (ubuntu-latest, py3.12) (push) Has been cancelled
tests / core (ubuntu-latest, py3.13) (push) Has been cancelled
tests / doctor CLI smoke test (push) Has been cancelled
repo hygiene, tooling config, v0.2.0
- Untrack src/ocr_pipeline.egg-info from git, delete pyproject.toml~,
  stale run_008/run_009 dirs
- Ruff config moved to [tool.ruff.lint] (silences deprecation warning)
- Pre-commit: bump revs (ruff v0.14.13, mypy v1.18.1, hooks v6.0.0);
  drop redundant isort hook (ruff I already sorts imports)
- mypy: add ignore_missing_imports for optional deps; types-PyYAML in
  dev extras; python_version=3.12 for numpy 2.5 stubs compat; CI step
- Doctor: suppress paddle ccache UserWarning noise during check
- CHANGELOG dated for 0.2.0; README minimal-style rewrite with
  collapsible details sections
- Bump version to 0.2.0
2026-07-21 00:40:19 +02:00

167 lines
4.8 KiB
Python

from __future__ import annotations
from typer.testing import CliRunner
from ocr_pipeline.cli import app
from ocr_pipeline.config import settings
from ocr_pipeline.doctor import (
check_config,
check_db,
check_output_dir,
check_paddle,
check_python,
check_spacy,
check_tables,
check_tesseract,
check_torch,
exit_code_for,
render,
run_checks,
)
runner = CliRunner()
def test_check_python_ok() -> None:
r = check_python()
assert r.status == "ok"
assert "Python" in r.name
def test_check_tesseract_ok_when_on_path(monkeypatch) -> None:
monkeypatch.setattr("shutil.which", lambda _: "/usr/bin/tesseract")
r = check_tesseract()
assert r.status == "ok"
assert "/usr/bin/tesseract" in r.detail
def test_check_tesseract_fails_when_missing(monkeypatch) -> None:
monkeypatch.setattr("shutil.which", lambda _: None)
r = check_tesseract()
assert r.status == "fail"
assert r.hint is not None
def test_check_paddle_warns_when_missing(monkeypatch) -> None:
monkeypatch.setattr(
"ocr_pipeline.doctor._has_module", lambda name: False if name == "paddleocr" else True
)
r = check_paddle()
assert r.status == "warn"
assert "paddle" in r.hint.lower() if r.hint else True
def test_check_torch_reports_backend(monkeypatch) -> None:
monkeypatch.setattr("ocr_pipeline.doctor._has_module", lambda name: True)
class FakeMPS:
@staticmethod
def is_available(): # type: ignore[no-untyped-def]
return False
class FakeBackends:
mps = FakeMPS
class FakeCuda:
@staticmethod
def is_available(): # type: ignore[no-untyped-def]
return False
fake_torch = type("T", (), {"cuda": FakeCuda, "backends": FakeBackends})
monkeypatch.setitem(__import__("sys").modules, "torch", fake_torch)
r = check_torch()
assert r.status == "ok"
assert "backend=" in r.detail
def test_check_tables_warns_when_missing(monkeypatch) -> None:
monkeypatch.setattr(
"ocr_pipeline.doctor._has_module",
lambda name: False if name in ("torch", "transformers") else True,
)
r = check_tables()
assert r.status == "warn"
def test_check_spacy_warns_when_model_missing(monkeypatch, tmp_path) -> None:
monkeypatch.setattr("ocr_pipeline.doctor._has_module", lambda name: True)
class FakeSpacy:
@staticmethod
def load(_model):
raise OSError("model not found")
monkeypatch.setitem(__import__("sys").modules, "spacy", FakeSpacy)
r = check_spacy()
assert r.status == "warn"
assert "en_core_sci_lg" in (r.hint or "")
def test_check_config_warns_when_no_file(monkeypatch, tmp_path) -> None:
monkeypatch.chdir(tmp_path)
monkeypatch.setattr("ocr_pipeline.config.user_config_dir", lambda: tmp_path / "missing")
r = check_config()
assert r.status == "warn"
assert "init" in (r.hint or "")
def test_check_db_writes_temp_file(tmp_path, monkeypatch) -> None:
db = tmp_path / "sub" / "test.db"
monkeypatch.setattr(settings.watch, "db_path", str(db))
r = check_db()
assert r.status == "ok"
assert db.exists()
def test_check_output_dir_creates_path(tmp_path, monkeypatch) -> None:
out = tmp_path / "deep" / "output"
monkeypatch.setattr(settings.output, "base_directory", str(out))
r = check_output_dir()
assert r.status == "ok"
assert out.is_dir()
def test_exit_code_for() -> None:
from ocr_pipeline.doctor import CheckResult
assert exit_code_for([]) == 0
assert exit_code_for([CheckResult("a", "ok", "x")]) == 0
assert exit_code_for([CheckResult("a", "warn", "x")]) == 0
assert exit_code_for([CheckResult("a", "fail", "x")]) == 1
def test_run_checks_returns_all_categories() -> None:
results = run_checks()
names = {r.name for r in results}
assert {"Tesseract", "PaddleOCR", "Python", "Config"}.issubset(names)
def test_render_writes_table(capsys) -> None:
from rich.console import Console
from ocr_pipeline.doctor import CheckResult
console = Console(file=__import__("sys").stdout)
render(
[CheckResult("A", "ok", "fine"), CheckResult("B", "fail", "bad", hint="fix me")], console
)
out = capsys.readouterr().out
assert "A" in out and "B" in out
def test_doctor_cli_runs(monkeypatch) -> None:
from ocr_pipeline.doctor import CheckResult
monkeypatch.setattr("ocr_pipeline.cli.run_checks", lambda: [CheckResult("X", "ok", "ok")])
result = runner.invoke(app, ["doctor"])
assert result.exit_code == 0
assert "X" in result.output
def test_doctor_cli_exit_code_on_fail(monkeypatch) -> None:
from ocr_pipeline.doctor import CheckResult
monkeypatch.setattr("ocr_pipeline.cli.run_checks", lambda: [CheckResult("X", "fail", "bad")])
result = runner.invoke(app, ["doctor"])
assert result.exit_code == 1