Model/tests/scripts/test_rename_sharepoint_files.py

168 lines
5.6 KiB
Python

from typing import Any
from unittest.mock import MagicMock
import pytest
from orchestration.sharepoint_renamer_orchestrator import (
SharepointRenamerOrchestrator,
build_canonical_filename,
)
def _make_file(name: str, item_id: str = "id-1") -> dict[str, Any]:
return {"name": name, "id": item_id, "file": {}}
def _make_folder(name: str) -> dict[str, Any]:
return {"name": name, "folder": {}}
def _make_package(name: str) -> dict[str, Any]:
return {"name": name, "package": {}}
def _make_orchestrator(sp: MagicMock, dry_run: bool = False) -> SharepointRenamerOrchestrator:
orchestrator = SharepointRenamerOrchestrator.__new__(SharepointRenamerOrchestrator)
orchestrator._sp_client = sp
orchestrator._dry_run = dry_run
return orchestrator
# ---------------------------------------------------------------------------
# build_canonical_filename
# ---------------------------------------------------------------------------
def test_already_canonical_returns_none() -> None:
assert build_canonical_filename("100", "1 High St", "AB1 2CD", "100_High St AB1 2CD_Report.pdf") is None
def test_strips_address_prefix_and_adds_uprn() -> None:
result = build_canonical_filename("100", "1 High St", "AB1 2CD", "1 High St AB1 2CD - Survey.pdf")
assert result == "100_1 High St AB1 2CD_Survey.pdf"
def test_no_prefix_still_canonical() -> None:
result = build_canonical_filename("100", "1 High St", "AB1 2CD", "Survey.pdf")
assert result == "100_1 High St AB1 2CD_Survey.pdf"
# ---------------------------------------------------------------------------
# _process_folder — files only at root level
# ---------------------------------------------------------------------------
def test_renames_top_level_files(caplog: pytest.LogCaptureFixture) -> None:
sp = MagicMock()
sp.get_folders_in_path.return_value = {
"value": [
_make_file("Survey.pdf", "id-1"),
_make_file("Report.docx", "id-2"),
]
}
_make_orchestrator(sp)._process_folder("some/path", "100", "1 High St", "AB1 2CD")
assert sp.rename_file.call_count == 2
sp.rename_file.assert_any_call("id-1", "100_1 High St AB1 2CD_Survey.pdf")
sp.rename_file.assert_any_call("id-2", "100_1 High St AB1 2CD_Report.docx")
# ---------------------------------------------------------------------------
# _process_folder — recursive two-level hierarchy
# ---------------------------------------------------------------------------
def test_recurses_into_subfolders_and_renames_all_files() -> None:
sp = MagicMock()
root_contents: dict[str, Any] = {
"value": [
_make_file("Root.pdf", "root-file"),
_make_folder("SubA"),
]
}
suba_contents: dict[str, Any] = {
"value": [
_make_file("Sub.pdf", "sub-file"),
]
}
sp.get_folders_in_path.side_effect = lambda path: (
root_contents if path == "base/path" else suba_contents
)
_make_orchestrator(sp)._process_folder("base/path", "200", "2 Main Rd", "XY9 8ZW")
assert sp.rename_file.call_count == 2
sp.rename_file.assert_any_call("root-file", "200_2 Main Rd XY9 8ZW_Root.pdf")
sp.rename_file.assert_any_call("sub-file", "200_2 Main Rd XY9 8ZW_Sub.pdf")
sp.get_folders_in_path.assert_any_call("base/path/SubA")
# ---------------------------------------------------------------------------
# _process_folder — non-file, non-folder items are skipped
# ---------------------------------------------------------------------------
def test_ignores_package_items() -> None:
sp = MagicMock()
sp.get_folders_in_path.return_value = {"value": [_make_package("Notebook")]}
_make_orchestrator(sp)._process_folder("some/path", "300", "3 Oak Ave", "ZZ1 1ZZ")
sp.rename_file.assert_not_called()
assert sp.get_folders_in_path.call_count == 1
# ---------------------------------------------------------------------------
# _process_folder — missing folder
# ---------------------------------------------------------------------------
def test_missing_folder_logs_warning_and_returns(caplog: pytest.LogCaptureFixture) -> None:
sp = MagicMock()
sp.get_folders_in_path.side_effect = ValueError("not found")
_make_orchestrator(sp)._process_folder("missing/path", "400", "4 Elm St", "AA2 2BB")
sp.rename_file.assert_not_called()
assert any("Missing folder" in r.message and "400" in r.message for r in caplog.records)
# ---------------------------------------------------------------------------
# _process_folder — already-canonical files are skipped
# ---------------------------------------------------------------------------
def test_skips_already_canonical_files() -> None:
sp = MagicMock()
sp.get_folders_in_path.return_value = {
"value": [_make_file("500_Pine Ln BB3 3CC_Doc.pdf", "id-y")]
}
_make_orchestrator(sp)._process_folder("some/path", "500", "5 Pine Ln", "BB3 3CC")
sp.rename_file.assert_not_called()
# ---------------------------------------------------------------------------
# _process_folder — dry_run=True logs intent but never calls rename_file
# ---------------------------------------------------------------------------
def test_dry_run_logs_would_rename_without_calling_api(
caplog: pytest.LogCaptureFixture,
) -> None:
sp = MagicMock()
sp.get_folders_in_path.return_value = {
"value": [_make_file("Survey.pdf", "id-1")]
}
_make_orchestrator(sp, dry_run=True)._process_folder(
"some/path", "100", "1 High St", "AB1 2CD"
)
sp.rename_file.assert_not_called()
assert any("Would rename" in r.message for r in caplog.records)