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
- 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
167 lines
4.8 KiB
Python
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
|