test_preview_service.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. """PreviewService 单元测试,不依赖数据库或真实 LibreOffice。"""
  2. from __future__ import annotations
  3. from pathlib import Path
  4. import pytest
  5. from dms.services.document_converter import FakeDocumentConverter
  6. from dms.services.preview_service import PreviewService
  7. from dms.storage.paths import UnsafeStoragePathError, preview_cache_path
  8. @pytest.fixture
  9. def storage_root(tmp_path: Path) -> Path:
  10. root = tmp_path / "dms-storage"
  11. (root / "preview").mkdir(parents=True)
  12. (root / "original").mkdir(parents=True)
  13. return root
  14. @pytest.fixture
  15. def preview_service(storage_root: Path) -> PreviewService:
  16. return PreviewService(storage_root, FakeDocumentConverter())
  17. def test_preview_cache_path_with_hash_prefix(storage_root: Path) -> None:
  18. path = preview_cache_path("abcdef123456", storage_root)
  19. assert path.name == "abcdef123456.pdf"
  20. assert path.parent.name == "ab"
  21. assert path.parent.parent.name == "preview"
  22. def test_preview_cache_path_rejects_invalid_hash(storage_root: Path) -> None:
  23. with pytest.raises(UnsafeStoragePathError):
  24. preview_cache_path("", storage_root)
  25. def test_validate_cached_pdf_accepts_valid_pdf(
  26. preview_service: PreviewService, storage_root: Path
  27. ) -> None:
  28. cache = preview_cache_path("validhash", storage_root)
  29. cache.parent.mkdir(parents=True, exist_ok=True)
  30. # 生成 fitz 可真正打开的合法最小 PDF,避免仅写入魔数导致验证失败
  31. import fitz
  32. doc = fitz.open()
  33. doc.new_page()
  34. doc.save(str(cache))
  35. doc.close()
  36. assert preview_service.validate_cached_pdf(cache) is True
  37. def test_validate_cached_pdf_rejects_non_pdf(
  38. preview_service: PreviewService, storage_root: Path
  39. ) -> None:
  40. cache = preview_cache_path("nonpdfhash", storage_root)
  41. cache.parent.mkdir(parents=True, exist_ok=True)
  42. cache.write_bytes(b"not a pdf")
  43. assert preview_service.validate_cached_pdf(cache) is False
  44. def test_validate_cached_pdf_rejects_path_outside_preview(
  45. preview_service: PreviewService, tmp_path: Path
  46. ) -> None:
  47. outside = tmp_path / "outside.pdf"
  48. outside.write_bytes(b"%PDF-1.4")
  49. assert preview_service.validate_cached_pdf(outside) is False
  50. class FakeDocument:
  51. def __init__(
  52. self,
  53. file_extension: str,
  54. file_hash: str,
  55. file_relative_path: str,
  56. ) -> None:
  57. self.file_extension = file_extension
  58. self.file_hash = file_hash
  59. self.file_relative_path = file_relative_path
  60. def test_preview_path_for_pdf_returns_original(
  61. preview_service: PreviewService, storage_root: Path
  62. ) -> None:
  63. original = storage_root / "original" / "sample.pdf"
  64. original.write_bytes(b"%PDF-1.4")
  65. document = FakeDocument("pdf", "pdfhash", "original/sample.pdf")
  66. path = preview_service.preview_path_for(document)
  67. assert path == original
  68. def test_preview_path_for_docx_generates_cache(
  69. preview_service: PreviewService, storage_root: Path
  70. ) -> None:
  71. original = storage_root / "original" / "sample.docx"
  72. original.write_bytes(b"fake docx content")
  73. document = FakeDocument("docx", "docxhash", "original/sample.docx")
  74. path = preview_service.preview_path_for(document)
  75. assert path.name == "docxhash.pdf"
  76. assert path.exists()
  77. assert path.read_bytes().startswith(b"%PDF-")
  78. def test_preview_path_for_doc_uses_cache_on_second_call(
  79. preview_service: PreviewService, storage_root: Path
  80. ) -> None:
  81. original = storage_root / "original" / "sample.doc"
  82. original.write_bytes(b"fake doc content")
  83. document = FakeDocument("doc", "dochash", "original/sample.doc")
  84. first = preview_service.preview_path_for(document)
  85. mtime = first.stat().st_mtime
  86. second = preview_service.preview_path_for(document)
  87. assert second == first
  88. assert second.stat().st_mtime == mtime