"""PreviewService 单元测试,不依赖数据库或真实 LibreOffice。""" from __future__ import annotations from pathlib import Path import pytest from dms.services.document_converter import FakeDocumentConverter from dms.services.preview_service import PreviewService from dms.storage.paths import UnsafeStoragePathError, preview_cache_path @pytest.fixture def storage_root(tmp_path: Path) -> Path: root = tmp_path / "dms-storage" (root / "preview").mkdir(parents=True) (root / "original").mkdir(parents=True) return root @pytest.fixture def preview_service(storage_root: Path) -> PreviewService: return PreviewService(storage_root, FakeDocumentConverter()) def test_preview_cache_path_with_hash_prefix(storage_root: Path) -> None: path = preview_cache_path("abcdef123456", storage_root) assert path.name == "abcdef123456.pdf" assert path.parent.name == "ab" assert path.parent.parent.name == "preview" def test_preview_cache_path_rejects_invalid_hash(storage_root: Path) -> None: with pytest.raises(UnsafeStoragePathError): preview_cache_path("", storage_root) def test_validate_cached_pdf_accepts_valid_pdf( preview_service: PreviewService, storage_root: Path ) -> None: cache = preview_cache_path("validhash", storage_root) cache.parent.mkdir(parents=True, exist_ok=True) # 生成 fitz 可真正打开的合法最小 PDF,避免仅写入魔数导致验证失败 import fitz doc = fitz.open() doc.new_page() doc.save(str(cache)) doc.close() assert preview_service.validate_cached_pdf(cache) is True def test_validate_cached_pdf_rejects_non_pdf( preview_service: PreviewService, storage_root: Path ) -> None: cache = preview_cache_path("nonpdfhash", storage_root) cache.parent.mkdir(parents=True, exist_ok=True) cache.write_bytes(b"not a pdf") assert preview_service.validate_cached_pdf(cache) is False def test_validate_cached_pdf_rejects_path_outside_preview( preview_service: PreviewService, tmp_path: Path ) -> None: outside = tmp_path / "outside.pdf" outside.write_bytes(b"%PDF-1.4") assert preview_service.validate_cached_pdf(outside) is False class FakeDocument: def __init__( self, file_extension: str, file_hash: str, file_relative_path: str, ) -> None: self.file_extension = file_extension self.file_hash = file_hash self.file_relative_path = file_relative_path def test_preview_path_for_pdf_returns_original( preview_service: PreviewService, storage_root: Path ) -> None: original = storage_root / "original" / "sample.pdf" original.write_bytes(b"%PDF-1.4") document = FakeDocument("pdf", "pdfhash", "original/sample.pdf") path = preview_service.preview_path_for(document) assert path == original def test_preview_path_for_docx_generates_cache( preview_service: PreviewService, storage_root: Path ) -> None: original = storage_root / "original" / "sample.docx" original.write_bytes(b"fake docx content") document = FakeDocument("docx", "docxhash", "original/sample.docx") path = preview_service.preview_path_for(document) assert path.name == "docxhash.pdf" assert path.exists() assert path.read_bytes().startswith(b"%PDF-") def test_preview_path_for_doc_uses_cache_on_second_call( preview_service: PreviewService, storage_root: Path ) -> None: original = storage_root / "original" / "sample.doc" original.write_bytes(b"fake doc content") document = FakeDocument("doc", "dochash", "original/sample.doc") first = preview_service.preview_path_for(document) mtime = first.stat().st_mtime second = preview_service.preview_path_for(document) assert second == first assert second.stat().st_mtime == mtime