208 lines
6.1 KiB
Python
208 lines
6.1 KiB
Python
"""
|
|
Unit tests for ExcelHandler.
|
|
|
|
Tests the ExcelHandler class in isolation, focusing on individual methods
|
|
and edge cases including encrypted workbooks, missing metadata, and corrupted files.
|
|
"""
|
|
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from openpyxl.utils.exceptions import InvalidFileException
|
|
|
|
from src.services.excel_handler import ExcelHandler
|
|
from src.utils.exceptions import (
|
|
MetadataNotFoundError,
|
|
UnsupportedFormatError,
|
|
)
|
|
|
|
# Import path helpers from conftest
|
|
from tests.conftest import get_large_xlsx_test_file, get_xlsx_test_file
|
|
|
|
# Test file paths (cross-platform)
|
|
XLSX_TEST_FILE = get_xlsx_test_file()
|
|
LARGE_XLSX_TEST_FILE = get_large_xlsx_test_file()
|
|
|
|
|
|
# ============== Success Case Tests ==============
|
|
|
|
|
|
@pytest.mark.parametrize("xlsx_file", [XLSX_TEST_FILE, LARGE_XLSX_TEST_FILE])
|
|
def test_read_excel_metadata(xlsx_file):
|
|
"""
|
|
Test reading metadata from Excel files.
|
|
Verifies that read() extracts metadata and populates keys_to_delete.
|
|
"""
|
|
assert Path(xlsx_file).exists(), f"Test file not found: {xlsx_file}"
|
|
handler = ExcelHandler(xlsx_file)
|
|
metadata = handler.read()
|
|
|
|
# Check metadata was extracted
|
|
assert handler.metadata == metadata
|
|
assert isinstance(metadata, dict)
|
|
|
|
# Check keys_to_delete is populated
|
|
assert handler.keys_to_delete is not None
|
|
assert len(handler.keys_to_delete) > 0
|
|
|
|
|
|
@pytest.mark.parametrize("xlsx_file", [XLSX_TEST_FILE, LARGE_XLSX_TEST_FILE])
|
|
def test_wipe_excel_metadata(xlsx_file):
|
|
"""
|
|
Test wiping metadata from Excel files.
|
|
Verifies that wipe() removes metadata entries.
|
|
"""
|
|
assert Path(xlsx_file).exists(), f"Test file not found: {xlsx_file}"
|
|
handler = ExcelHandler(xlsx_file)
|
|
metadata = handler.read()
|
|
handler.wipe()
|
|
|
|
# processed_metadata should differ from original
|
|
assert handler.processed_metadata != metadata
|
|
|
|
|
|
@pytest.mark.parametrize("xlsx_file", [XLSX_TEST_FILE, LARGE_XLSX_TEST_FILE])
|
|
def test_save_processed_excel_metadata(xlsx_file):
|
|
"""
|
|
Test saving processed Excel to output path.
|
|
"""
|
|
output_dir = Path("./tests/assets/output")
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
handler = ExcelHandler(xlsx_file)
|
|
handler.read()
|
|
handler.wipe()
|
|
|
|
output_file = output_dir / Path(xlsx_file).name
|
|
handler.save(str(output_file))
|
|
|
|
# Verify output file exists
|
|
assert output_file.exists()
|
|
|
|
# Cleanup
|
|
shutil.rmtree(output_dir)
|
|
|
|
|
|
def test_format_detection_xlsx():
|
|
"""
|
|
Test that _detect_format() correctly identifies XLSX files.
|
|
"""
|
|
handler = ExcelHandler(XLSX_TEST_FILE)
|
|
detected = handler._detect_format()
|
|
assert detected == "xlsx"
|
|
|
|
|
|
@pytest.mark.parametrize("xlsx_file", [XLSX_TEST_FILE, LARGE_XLSX_TEST_FILE])
|
|
def test_output_file_has_less_metadata(xlsx_file):
|
|
"""
|
|
Test that the output file has metadata stripped.
|
|
"""
|
|
output_dir = Path("./tests/assets/output")
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Process original file
|
|
handler = ExcelHandler(xlsx_file)
|
|
original_metadata = handler.read()
|
|
handler.wipe()
|
|
|
|
# Save processed file
|
|
output_file = output_dir / Path(xlsx_file).name
|
|
handler.save(str(output_file))
|
|
|
|
# Read output file and verify metadata is reduced or gone
|
|
try:
|
|
output_handler = ExcelHandler(str(output_file))
|
|
output_metadata = output_handler.read()
|
|
# Output should have fewer metadata entries (some are None now)
|
|
non_none_original = sum(1 for v in original_metadata.values() if v is not None)
|
|
non_none_output = sum(1 for v in output_metadata.values() if v is not None)
|
|
assert non_none_output <= non_none_original
|
|
except MetadataNotFoundError:
|
|
# If no metadata found, that's expected for fully stripped files
|
|
pass
|
|
|
|
# Cleanup
|
|
shutil.rmtree(output_dir)
|
|
|
|
|
|
def test_preserved_properties_not_deleted():
|
|
"""
|
|
Test that created, modified, and language properties are preserved.
|
|
"""
|
|
handler = ExcelHandler(XLSX_TEST_FILE)
|
|
handler.read()
|
|
|
|
# These should NOT be in keys_to_delete
|
|
assert "created" not in handler.keys_to_delete
|
|
assert "modified" not in handler.keys_to_delete
|
|
assert "language" not in handler.keys_to_delete
|
|
|
|
|
|
# ============== Error Case Tests ==============
|
|
|
|
|
|
def test_unsupported_format_raises_error(tmp_path):
|
|
"""
|
|
Test that non-Excel files raise UnsupportedFormatError.
|
|
"""
|
|
# Create a fake text file with .txt extension
|
|
fake_file = tmp_path / "test.txt"
|
|
fake_file.write_text("not an excel file")
|
|
|
|
handler = ExcelHandler(str(fake_file))
|
|
with pytest.raises(UnsupportedFormatError):
|
|
handler._detect_format()
|
|
|
|
|
|
def test_save_without_output_path_raises_error():
|
|
"""
|
|
Test that save() raises ValueError when output_path is None or empty.
|
|
"""
|
|
handler = ExcelHandler(XLSX_TEST_FILE)
|
|
handler.read()
|
|
handler.wipe()
|
|
with pytest.raises(ValueError):
|
|
handler.save("")
|
|
|
|
|
|
def test_save_with_none_raises_error():
|
|
"""
|
|
Test that save() raises ValueError when output_path is None.
|
|
"""
|
|
handler = ExcelHandler(XLSX_TEST_FILE)
|
|
handler.read()
|
|
handler.wipe()
|
|
with pytest.raises((ValueError, TypeError)):
|
|
handler.save(None)
|
|
|
|
|
|
# ============== Edge Case Tests ==============
|
|
|
|
|
|
def test_corrupted_excel_graceful_error(tmp_path):
|
|
"""
|
|
Test that corrupted Excel files are handled gracefully.
|
|
"""
|
|
# Create a corrupted Excel file (invalid structure)
|
|
corrupted_xlsx = tmp_path / "corrupted.xlsx"
|
|
corrupted_xlsx.write_bytes(b"not a valid xlsx content at all")
|
|
|
|
handler = ExcelHandler(str(corrupted_xlsx))
|
|
# Should raise InvalidFileException or similar from openpyxl
|
|
with pytest.raises((InvalidFileException, Exception)):
|
|
handler.read()
|
|
|
|
|
|
def test_format_detection_all_excel_types(tmp_path):
|
|
"""
|
|
Test format detection for all supported Excel extensions.
|
|
"""
|
|
for ext in ["xlsx", "xlsm", "xltx", "xltm"]:
|
|
fake_file = tmp_path / f"test.{ext}"
|
|
fake_file.write_bytes(b"dummy") # Not valid, but we're only testing detection
|
|
|
|
handler = ExcelHandler(str(fake_file))
|
|
detected = handler._detect_format()
|
|
assert detected == ext
|