Files
wehub-resource-sync 461bf6fd40
CI / lint (3.11) (push) Blocked by required conditions
CI / lint (3.12) (push) Blocked by required conditions
CI / lint (3.13) (push) Blocked by required conditions
CI / shellcheck (push) Waiting to run
CI / shfmt (push) Waiting to run
CI / setup (3.11) (push) Waiting to run
CI / setup (3.12) (push) Waiting to run
CI / setup (3.13) (push) Waiting to run
CI / check-licenses (3.12) (push) Blocked by required conditions
CI / test_unit (3.11) (push) Blocked by required conditions
CI / test_unit (3.12) (push) Blocked by required conditions
CI / test_unit (3.13) (push) Blocked by required conditions
CI / test_unit_no_extras (3.11) (push) Blocked by required conditions
CI / test_unit_no_extras (3.12) (push) Blocked by required conditions
CI / test_json_to_html (3.12) (push) Blocked by required conditions
CI / test_unit_no_extras (3.13) (push) Blocked by required conditions
CI / test_unit_dependency_extras (csv, 3.11, --extra csv) (push) Blocked by required conditions
CI / test_unit_dependency_extras (csv, 3.12, --extra csv) (push) Blocked by required conditions
CI / test_unit_dependency_extras (csv, 3.13, --extra csv) (push) Blocked by required conditions
CI / test_unit_dependency_extras (docx, 3.11, --extra docx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (docx, 3.12, --extra docx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (docx, 3.13, --extra docx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (markdown, 3.11, --extra md) (push) Blocked by required conditions
CI / test_unit_dependency_extras (markdown, 3.12, --extra md) (push) Blocked by required conditions
CI / test_unit_dependency_extras (markdown, 3.13, --extra md) (push) Blocked by required conditions
CI / test_unit_dependency_extras (odt, 3.11, --extra odt) (push) Blocked by required conditions
CI / test_unit_dependency_extras (odt, 3.12, --extra odt) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pdf-image, 3.12, --extra pdf --extra image --extra paddleocr) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pdf-image, 3.13, --extra pdf --extra image --extra paddleocr) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pptx, 3.13, --extra pptx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (odt, 3.13, --extra odt) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pdf-image, 3.11, --extra pdf --extra image --extra paddleocr) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pptx, 3.11, --extra pptx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pptx, 3.12, --extra pptx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pypandoc, 3.11, --extra epub --extra org --extra rtf --extra rst) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pypandoc, 3.12, --extra epub --extra org --extra rtf --extra rst) (push) Blocked by required conditions
CI / test_unit_dependency_extras (pypandoc, 3.13, --extra epub --extra org --extra rtf --extra rst) (push) Blocked by required conditions
CI / test_unit_dependency_extras (xlsx, 3.11, --extra xlsx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (xlsx, 3.12, --extra xlsx) (push) Blocked by required conditions
CI / test_unit_dependency_extras (xlsx, 3.13, --extra xlsx) (push) Blocked by required conditions
CI / test_ingest_src (3.12) (push) Blocked by required conditions
CI / test_json_to_markdown (3.12) (push) Blocked by required conditions
CI / changelog (push) Waiting to run
CI / test_dockerfile (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Build And Push Docker Image / set-short-sha (push) Waiting to run
Build And Push Docker Image / build-images (linux/amd64, opensource-linux-8core) (push) Blocked by required conditions
Build And Push Docker Image / build-images (linux/arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
Build And Push Docker Image / publish-images (push) Blocked by required conditions
Partition Benchmark / setup (push) Waiting to run
Partition Benchmark / Measure and compare partition() runtime (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:33:56 +08:00

359 lines
11 KiB
Python

"""Tests for :mod:`unstructured.cli` and :mod:`unstructured.doctor`."""
from __future__ import annotations
import sys
import zipfile
from pathlib import Path
from unittest import mock
import pytest
from unstructured.cli import _cmd_doctor, main
from unstructured.doctor import (
_libmagic_status,
_needs_pandoc_runtime,
_pip_hint_for_file_type,
_tool_status,
build_report,
environment_rows,
evaluate_file_type_capability,
evaluate_specifier,
file_path_to_capability,
format_table,
python_import_deps_ok,
resolve_specifier,
system_tool_rows,
)
from unstructured.file_utils.model import FileType
def test_resolve_specifier_pdf() -> None:
fts = resolve_specifier("pdf")
assert fts == [FileType.PDF]
def test_resolve_specifier_jpg_exact() -> None:
assert resolve_specifier("jpg") == [FileType.JPG]
def test_resolve_specifier_image_family() -> None:
fts = resolve_specifier("image")
assert len(fts) >= 1
assert all(ft.extra_name == "image" for ft in fts)
def test_resolve_specifier_audio_family() -> None:
fts = resolve_specifier("audio")
assert len(fts) >= 1
assert all(ft.extra_name == "audio" for ft in fts)
def test_resolve_specifier_email_partitioner_shortname() -> None:
assert FileType.EML in resolve_specifier("email")
def test_resolve_specifier_unknown() -> None:
with pytest.raises(ValueError, match="Unknown"):
resolve_specifier("not-a-real-type-xyz")
def test_resolve_specifier_empty() -> None:
with pytest.raises(ValueError, match="Empty"):
resolve_specifier(" ")
def test_evaluate_specifier_dedupes_image_family() -> None:
with mock.patch("unstructured.doctor.evaluate_file_type_capability") as ev:
ev.return_value = mock.Mock(ready=True, messages=())
r = evaluate_specifier("image")
assert r.ready is True
assert ev.call_count == 1
def test_evaluate_specifier_multiple_targets() -> None:
def _cap(ready: bool, *msgs: str) -> mock.Mock:
m = mock.Mock()
m.ready = ready
m.messages = tuple(msgs)
return m
with (
mock.patch(
"unstructured.doctor.resolve_specifier",
return_value=[FileType.PDF, FileType.CSV],
),
mock.patch(
"unstructured.doctor.evaluate_file_type_capability",
side_effect=[_cap(False, "a"), _cap(True)],
),
):
r = evaluate_specifier("x")
assert r.ready is False
assert "[PDF] a" in r.messages
def test_evaluate_specifier_dedupes_repeated_message_lines() -> None:
with (
mock.patch("unstructured.doctor.resolve_specifier", return_value=[FileType.PDF]),
mock.patch(
"unstructured.doctor.evaluate_file_type_capability",
return_value=mock.Mock(ready=False, messages=("dup", "dup")),
),
):
r = evaluate_specifier("pdf")
assert r.messages.count("[PDF] dup") == 1
def test_main_doctor_invocation() -> None:
with mock.patch("unstructured.cli._cmd_doctor", return_value=0) as m:
assert main(["doctor"]) == 0
m.assert_called_once_with([])
def test_main_no_argv_reads_sys_argv(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr("sys.argv", ["unstructured", "doctor", "--for", "txt"])
with mock.patch("unstructured.cli._cmd_doctor", return_value=0) as m:
assert main(None) == 0
m.assert_called_once_with(["--for", "txt"])
def test_main_unknown() -> None:
assert main(["nope"]) == 2
def test_main_help_returns_zero() -> None:
assert main(["--help"]) == 0
def test_cmd_doctor_for_missing_dep_exit_code() -> None:
with mock.patch(
"unstructured.doctor.evaluate_specifier",
return_value=mock.Mock(ready=False, messages=("missing",)),
):
assert _cmd_doctor(["--for", "pdf"]) == 1
def test_cmd_doctor_for_ok() -> None:
with mock.patch(
"unstructured.doctor.evaluate_specifier",
return_value=mock.Mock(ready=True, messages=()),
):
assert _cmd_doctor(["--for", "pdf"]) == 0
def test_cmd_doctor_for_prints_messages(capsys: pytest.CaptureFixture[str]) -> None:
with mock.patch(
"unstructured.doctor.evaluate_specifier",
return_value=mock.Mock(ready=True, messages=("note",)),
):
assert _cmd_doctor(["--for", "pdf"]) == 0
assert "note" in capsys.readouterr().out
def test_cmd_doctor_file_not_found(tmp_path: Path) -> None:
missing = tmp_path / "nope.pdf"
assert _cmd_doctor(["--file", str(missing)]) == 1
def test_cmd_doctor_file_txt_ok(tmp_path: Path) -> None:
pytest.importorskip("olefile")
f = tmp_path / "a.txt"
f.write_text("hello", encoding="utf-8")
assert _cmd_doctor(["--file", str(f)]) == 0
def test_cmd_doctor_file_prints_messages(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
f = tmp_path / "b.txt"
f.write_text("x", encoding="utf-8")
with mock.patch(
"unstructured.doctor.file_path_to_capability",
return_value=mock.Mock(ready=True, messages=("hi",)),
):
assert _cmd_doctor(["--file", str(f)]) == 0
assert "hi" in capsys.readouterr().out
def test_cmd_doctor_for_and_file_mutually_exclusive_returns_2() -> None:
assert _cmd_doctor(["--for", "pdf", "--file", "any.pdf"]) == 2
def test_cmd_doctor_invalid_for_returns_2() -> None:
assert _cmd_doctor(["--for", "not-a-real-type-xyz"]) == 2
def test_cmd_doctor_default_prints_report(capsys: pytest.CaptureFixture[str]) -> None:
assert _cmd_doctor([]) == 0
out = capsys.readouterr().out
assert "Environment" in out
assert "Partitionable file types" in out
def test_evaluate_doc_requires_soffice_when_deps_ok() -> None:
with mock.patch("unstructured.doctor.python_import_deps_ok", return_value=(True, [])):
with mock.patch("unstructured.doctor._libreoffice_on_path", return_value=None):
r = evaluate_file_type_capability(FileType.DOC)
assert r.ready is False
assert "soffice" in "".join(r.messages).lower()
def test_evaluate_epub_requires_pandoc_binary_when_python_ok(
monkeypatch: pytest.MonkeyPatch,
) -> None:
with mock.patch("unstructured.doctor.python_import_deps_ok", return_value=(True, [])):
def _which(cmd: str) -> str | None:
if cmd == "pandoc":
return None
return "/fake/bin/" + cmd
monkeypatch.setattr("unstructured.doctor.shutil.which", _which)
r = evaluate_file_type_capability(FileType.EPUB)
assert r.ready is False
assert "Pandoc" in "".join(r.messages)
def test_evaluate_zip_not_partitionable() -> None:
r = evaluate_file_type_capability(FileType.ZIP)
assert r.ready is False
assert "not partitionable" in r.messages[0]
def test_evaluate_html_no_extra() -> None:
r = evaluate_file_type_capability(FileType.HTML)
assert r.ready is True
assert not r.messages
def test_evaluate_audio_not_ready_without_ffmpeg(monkeypatch: pytest.MonkeyPatch) -> None:
import shutil
real_which = shutil.which
def _which(cmd: str) -> str | None:
if cmd == "ffmpeg":
return None
return real_which(cmd)
monkeypatch.setattr("unstructured.doctor.shutil.which", _which)
with mock.patch("unstructured.doctor._audio_extra_import_ok", return_value=True):
r = evaluate_file_type_capability(FileType.MP3)
assert r.ready is False
assert "ffmpeg" in " ".join(r.messages).lower()
def test_python_import_deps_ok_returns_missing() -> None:
ok, missing = python_import_deps_ok(FileType.PDF)
assert isinstance(ok, bool)
assert isinstance(missing, list)
def test_format_table_empty_rows() -> None:
out = format_table(("A", "B"), [])
assert "(no rows)" in out
def test_format_table_with_rows() -> None:
out = format_table(("Col",), [("val",)])
assert "Col" in out
assert "val" in out
def test_environment_rows() -> None:
rows = environment_rows()
labels = [r[0] for r in rows]
assert "Python" in labels
assert "unstructured" in labels
def test_system_tool_rows_shape() -> None:
rows = system_tool_rows()
assert len(rows) >= 5
assert all(len(r) == 3 for r in rows)
def test_tool_status_branches() -> None:
assert _tool_status("x", True)[0] == "ok"
assert _tool_status("x", False)[0] == "missing"
def test_pip_hint_for_file_type() -> None:
assert "pdf" in _pip_hint_for_file_type(FileType.PDF)
assert _pip_hint_for_file_type(FileType.HTML) == "pip install unstructured"
def test_needs_pandoc_runtime() -> None:
assert _needs_pandoc_runtime(FileType.EPUB) is True
assert _needs_pandoc_runtime(FileType.HTML) is False
def test_build_report_smoke() -> None:
report = build_report()
assert "Environment" in report
assert "System tools" in report
assert "Partitionable file types" in report
def test_file_path_to_capability_txt(tmp_path: Path) -> None:
pytest.importorskip("olefile")
f = tmp_path / "n.txt"
f.write_text("x", encoding="utf-8")
r = file_path_to_capability(f)
assert r.ready is True
def test_file_path_to_capability_zip(tmp_path: Path) -> None:
pytest.importorskip("olefile")
zp = tmp_path / "empty.zip"
with zipfile.ZipFile(zp, "w"):
pass
r = file_path_to_capability(zp)
assert r.ready is False
assert "not partitionable" in r.messages[0]
def test_file_path_to_capability_missing_path(tmp_path: Path) -> None:
missing = tmp_path / "does_not_exist.txt"
r = file_path_to_capability(missing)
assert r.ready is False
assert "Not a file" in r.messages[0]
def test_libmagic_status_ok_with_mocked_magic(monkeypatch: pytest.MonkeyPatch) -> None:
fake = mock.MagicMock()
fake.from_buffer = mock.MagicMock(return_value="application/pdf")
monkeypatch.setitem(sys.modules, "magic", fake)
st, msg = _libmagic_status()
assert st == "ok"
assert "OK" in msg or "ok" in msg
def test_libmagic_status_warn_when_from_buffer_fails(
monkeypatch: pytest.MonkeyPatch,
) -> None:
fake = mock.MagicMock()
fake.from_buffer = mock.MagicMock(side_effect=RuntimeError("boom"))
monkeypatch.setitem(sys.modules, "magic", fake)
st, msg = _libmagic_status()
assert st == "warn"
assert "libmagic" in msg.lower()
def test_libmagic_status_import_error(monkeypatch: pytest.MonkeyPatch) -> None:
import builtins
real_import = builtins.__import__
def _imp(name: str, *args, **kwargs):
if name == "magic":
raise ImportError("blocked for test")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", _imp)
st, msg = _libmagic_status()
assert st == "warn"
assert "magic" in msg.lower() or "fallback" in msg.lower()