from __future__ import annotations import subprocess import unittest from pathlib import Path from unittest.mock import patch from tools.exiftool import run_exiftool_command, run_exiftool_write from tools.media_metadata import parse_exif_datetime from tools.metadata_copy import copy_meaningful_metadata class ExiftoolInvocationTests(unittest.TestCase): def test_windows_uses_utf8_argument_file_for_unicode_paths(self) -> None: captured: dict[str, object] = {} def fake_run(command, **kwargs): captured["command"] = command argument_file = Path(kwargs["cwd"]) / command[-1] captured["contents"] = argument_file.read_text(encoding="utf-8") return subprocess.CompletedProcess(command, 0) with ( patch("tools.exiftool._use_windows_argument_file", return_value=True), patch("tools.exiftool.subprocess.run", side_effect=fake_run), ): run_exiftool_command( "exiftool.exe", ["-j", "C:\\Photos\\M\u00e4rchen.jpg"], check=True, ) self.assertEqual( captured["command"], ["exiftool.exe", "-charset", "filename=UTF8", "-@", "arguments.txt"], ) self.assertEqual(captured["contents"], "-j\nC:\\Photos\\M\u00e4rchen.jpg\n") def test_write_captures_success_output(self) -> None: with patch( "tools.exiftool.subprocess.run", return_value=subprocess.CompletedProcess([], 0, "1 image files updated\n", ""), ) as run: run_exiftool_write("exiftool", Path("photo.jpg"), ["-Artist=Test"]) self.assertTrue(run.call_args.kwargs["capture_output"]) self.assertTrue(run.call_args.kwargs["text"]) def test_write_includes_exiftool_failure_output(self) -> None: with patch( "tools.exiftool.subprocess.run", return_value=subprocess.CompletedProcess([], 1, "", "Error renaming temporary file"), ): with self.assertRaisesRegex(RuntimeError, "Error renaming temporary file"): run_exiftool_write("exiftool", Path("photo.jpg"), ["-Artist=Test"]) def test_metadata_copy_uses_exiftool_for_photos(self) -> None: with patch("tools.metadata_copy.run_exiftool_command") as run: copy_meaningful_metadata("exiftool", Path("source.jpg"), Path("target.jpg")) args = run.call_args.args[1] self.assertEqual(args[:4], ["-overwrite_original", "-TagsFromFile", "source.jpg", "-all:all"]) self.assertEqual(args[-1], "target.jpg") def test_metadata_datetime_can_assume_utc(self) -> None: parsed = parse_exif_datetime("2026:07:04 14:13:36", assume_utc=True) self.assertEqual(str(parsed.tzinfo), "UTC") if __name__ == "__main__": unittest.main()