""" Tests which require user interaction to run for osxphotos import command; run with pytest --test-import """ import csv import json import os import os.path import pathlib import re import shutil import sqlite3 import time from datetime import datetime from tempfile import TemporaryDirectory from typing import Dict import pytest from click.testing import CliRunner from pytest import MonkeyPatch, approx from osxphotos import PhotosDB, QueryOptions from osxphotos._constants import UUID_PATTERN from osxphotos.datetime_utils import datetime_remove_tz from osxphotos.exiftool import get_exiftool_path from osxphotos.utils import is_macos from tests.conftest import get_os_version if is_macos: from photoscript import Photo from osxphotos.cli.import_cli import import_cli else: pytest.skip(allow_module_level=True) TERMINAL_WIDTH = 250 TEST_IMAGES_DIR = "tests/test-images" TEST_IMAGE_1 = "tests/test-images/IMG_4179.jpeg" TEST_IMAGE_2 = "tests/test-images/faceinfo/exif1.jpg" TEST_IMAGE_NO_EXIF = "tests/test-images/IMG_NO_EXIF.jpeg" TEST_VIDEO_1 = "tests/test-images/Jellyfish.mov" TEST_VIDEO_2 = "tests/test-images/IMG_0670B_NOGPS.MOV" TEST_DATA = { TEST_IMAGE_1: { "title": "Waves crashing on rocks", "description": "Used for testing osxphotos", "keywords": ["osxphotos"], "lat": 33.7150638888889, "lon": -118.319672222222, "check_templates": [ "exiftool title: Waves crashing on rocks", "exiftool description: Used for testing osxphotos", "exiftool keywords: ['osxphotos']", "exiftool location: (33.7150638888889, -118.319672222222)", "title: {exiftool:XMP:Title}: Waves crashing on rocks", "description: {exiftool:IPTC:Caption-Abstract}: Used for testing osxphotos", "keyword: {exiftool:IPTC:Keywords}: ['osxphotos']", "album: {filepath.parent}: test-images", ], }, TEST_VIDEO_1: { "title": "Jellyfish", "description": "Jellyfish Video", # "keywords": ["Travel"], # exiftool doesn't seem to support the binary QuickTime:Keywords "keywords": [], "lat": 34.0533, "lon": -118.2423, }, TEST_VIDEO_2: { "title": "", "description": "", "lat": None, "lon": None, }, TEST_IMAGE_2: { "albums": ["faceinfo"], }, } # set timezone to avoid issues with comparing dates os.environ["TZ"] = "US/Pacific" time.tzset() # determine if exiftool installed so exiftool tests can be skipped try: exiftool_path = get_exiftool_path() except FileNotFoundError: exiftool_path = None def prompt(message): """Helper function for tests that require user input""" message = f"\n{message}\nPlease answer y/n: " answer = input(message) return answer.lower() == "y" def say(msg: str) -> None: """Say message with text to speech""" os.system(f"say {msg}") def parse_import_output(output: str) -> Dict[str, str]: """Parse output of osxphotos import command and return dict of {image name: uuid} for imported photos""" # look for lines that look like this: # Imported IMG_4179.jpeg with UUID A62792F0-4524-4529-9931-56E52C95E873 results = {} for line in output.split("\n"): pattern = re.compile(r"Imported ([\w\.]+)\s.*UUID\s(" + UUID_PATTERN + r")") if match := re.match(pattern, line): file = match[1] uuid = match[2] results[file] = uuid return results ########## Interactive tests run first ########## @pytest.mark.test_import def test_import(): """Test basic import""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, ["--verbose", test_image_1], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 @pytest.mark.test_import def test_import_dup_check(): """Test basic import with --dup-check""" say("Please click Import when prompted by Photos to import duplicate photo.") cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, ["--verbose", "--dup-check", test_image_1], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 @pytest.mark.test_import def test_import_album(): """Test basic import to an album""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, ["--verbose", "--album", "My New Album", test_image_1], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 albums = photo_1.albums assert len(albums) == 1 assert albums[0].title == "My New Album" @pytest.mark.test_import def test_import_album_2(): """Test basic import to an album with a "/" in it""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, ["--verbose", "--album", "Folder/My New Album", test_image_1], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 albums = photo_1.albums assert len(albums) == 1 assert albums[0].title == "Folder/My New Album" assert albums[0].path_str() == "Folder/My New Album" @pytest.mark.test_import def test_import_album_slit_folder(): """Test basic import to an album with a "/" in it and --split-folder""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--album", "Folder/My New Album", "--split-folder", "/", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 albums = photo_1.albums assert len(albums) == 1 assert albums[0].title == "My New Album" assert albums[0].path_str() == "Folder/My New Album" @pytest.mark.test_import def test_import_album_relative_to(): """Test import with --relative-to""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--album", "{filepath.parent}", "--split-folder", "/", "--relative-to", cwd, test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 albums = photo_1.albums assert len(albums) == 1 assert albums[0].title == "test-images" assert albums[0].path_str() == "tests/test-images" @pytest.mark.test_import def test_import_clear_metadata(): """Test import with --clear-metadata""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert not photo_1.title assert not photo_1.description assert not photo_1.keywords @pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed") @pytest.mark.test_import def test_import_exiftool(): """Test import file with --exiftool""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", "--exiftool", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert photo_1.title == TEST_DATA[TEST_IMAGE_1]["title"] assert photo_1.description == TEST_DATA[TEST_IMAGE_1]["description"] assert photo_1.keywords == TEST_DATA[TEST_IMAGE_1]["keywords"] lat, lon = photo_1.location assert lat == approx(TEST_DATA[TEST_IMAGE_1]["lat"]) assert lon == approx(TEST_DATA[TEST_IMAGE_1]["lon"]) @pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed") @pytest.mark.test_import def test_import_exiftool_video(): """Test import video file with --exiftool""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_VIDEO_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", "--exiftool", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert photo_1.title == TEST_DATA[TEST_VIDEO_1]["title"] assert photo_1.description == TEST_DATA[TEST_VIDEO_1]["description"] assert photo_1.keywords == TEST_DATA[TEST_VIDEO_1]["keywords"] lat, lon = photo_1.location assert lat == approx(TEST_DATA[TEST_VIDEO_1]["lat"]) assert lon == approx(TEST_DATA[TEST_VIDEO_1]["lon"]) @pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed") @pytest.mark.test_import def test_import_exiftool_video_no_metadata(): """Test import video file with --exiftool that has no metadata""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_VIDEO_2) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", "--exiftool", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert photo_1.title == "" assert photo_1.description == "" assert photo_1.keywords == [] lat, lon = photo_1.location assert lat is None assert lon is None @pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed") @pytest.mark.test_import def test_import_title(): """Test import with --title""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", "--title", "{exiftool:XMP:Title|upper}", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert photo_1.title == TEST_DATA[TEST_IMAGE_1]["title"].upper() @pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed") @pytest.mark.test_import def test_import_description(): """Test import with --description""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", "--description", "{exiftool:XMP:Description|upper}", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert photo_1.description == TEST_DATA[TEST_IMAGE_1]["description"].upper() @pytest.mark.test_import def test_import_keyword(): """Test import with --keyword""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--keyword", "Bar", "--keyword", "Foo", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert sorted(photo_1.keywords) == ["Bar", "Foo"] @pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed") @pytest.mark.test_import def test_import_keyword_merge(): """Test import with --keyword and --merge-keywords""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", "--exiftool", "--keyword", "Bar", "--keyword", "Foo", "--merge-keywords", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert sorted(photo_1.keywords) == ["Bar", "Foo", "osxphotos"] @pytest.mark.test_import def test_import_location(): """Test import file with --location""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--clear-metadata", "--location", "-45.0", "-45.0", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 lat, lon = photo_1.location assert lat == approx(-45.0) assert lon == approx(-45.0) @pytest.mark.test_import def test_import_glob(): """Test import with --glob""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, ["--verbose", f"{cwd}/{TEST_IMAGES_DIR}/", "--walk", "--glob", "Pumpk*.jpg"], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 assert "imported 2 files" in result.output @pytest.mark.test_import def test_import_glob_walk(): """Test import with --walk --glob""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", f"{cwd}/{TEST_IMAGES_DIR}/", "--walk", "--glob", "exif*.jpg", "--album", "{filepath.parent.name}", "--relative-to", f"{cwd}/{TEST_IMAGES_DIR}", ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 assert "imported 4 files" in result.output import_data = parse_import_output(result.output) file_1 = pathlib.Path(TEST_IMAGE_2).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 assert [a.title for a in photo_1.albums] == TEST_DATA[TEST_IMAGE_2]["albums"] @pytest.mark.skipif(exiftool_path is None, reason="exiftool not installed") @pytest.mark.test_import def test_import_check_templates(): """Test import file with --check-templates""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--exiftool", "--title", "{exiftool:XMP:Title}", "--description", "{exiftool:IPTC:Caption-Abstract}", "--keyword", "{exiftool:IPTC:Keywords}", "--album", "{filepath.parent}", "--relative-to", f"{cwd}/tests", "--check-templates", test_image_1, ], terminal_width=TERMINAL_WIDTH, ) # assert result.output == "foo" assert result.exit_code == 0 output = result.output.splitlines() output.pop(0) for idx, line in enumerate(output): assert line == TEST_DATA[TEST_IMAGE_1]["check_templates"][idx] @pytest.mark.test_import def test_import_function_template(): """Test import with a function template""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) function = os.path.join(cwd, "examples/template_function_import.py") with TemporaryDirectory() as tempdir: test_image = shutil.copy( test_image_1, os.path.join(tempdir, "MyAlbum_IMG_0001.jpg") ) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--album", "{function:" + function + "::example}", test_image, ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 albums = [a.title for a in photo_1.albums] assert albums == ["MyAlbum"] @pytest.mark.test_import def test_import_report(): """test import with --report option""" runner = CliRunner() cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) with runner.isolated_filesystem(): result = runner.invoke( import_cli, [ test_image_1, "--report", "report.csv", "--verbose", ], ) assert result.exit_code == 0 assert "Wrote import report" in result.output assert os.path.exists("report.csv") with open("report.csv", "r") as f: reader = csv.DictReader(f) rows = list(reader) filenames = [str(pathlib.Path(row["filename"]).name) for row in rows] assert filenames == [pathlib.Path(TEST_IMAGE_1).name] # test report gets overwritten result = runner.invoke( import_cli, [ test_image_1, "--report", "report.csv", "--verbose", ], ) assert result.exit_code == 0 with open("report.csv", "r") as f: reader = csv.DictReader(f) rows = list(reader) filenames = [str(pathlib.Path(row["filename"]).name) for row in rows] assert filenames == [pathlib.Path(TEST_IMAGE_1).name] # test report with --append result = runner.invoke( import_cli, [ test_image_1, "--report", "report.csv", "--append", "--verbose", ], ) assert result.exit_code == 0 with open("report.csv", "r") as f: reader = csv.DictReader(f) rows = list(reader) filenames = [str(pathlib.Path(row["filename"]).name) for row in rows] assert filenames == [ pathlib.Path(TEST_IMAGE_1).name, pathlib.Path(TEST_IMAGE_1).name, ] @pytest.mark.test_import def test_import_report_json(): """test import with --report option with json output""" runner = CliRunner() cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) with runner.isolated_filesystem(): result = runner.invoke( import_cli, [ test_image_1, "--report", "report.json", "--verbose", ], ) assert result.exit_code == 0 assert "Wrote import report" in result.output assert os.path.exists("report.json") with open("report.json", "r") as f: rows = json.load(f) filenames = [str(pathlib.Path(row["filename"]).name) for row in rows] assert filenames == [pathlib.Path(TEST_IMAGE_1).name] # test report gets overwritten result = runner.invoke( import_cli, [ test_image_1, "--report", "report.json", "--verbose", ], ) assert result.exit_code == 0 assert "Wrote import report" in result.output assert os.path.exists("report.json") with open("report.json", "r") as f: rows = json.load(f) filenames = [str(pathlib.Path(row["filename"]).name) for row in rows] assert filenames == [pathlib.Path(TEST_IMAGE_1).name] # test report with --append result = runner.invoke( import_cli, [ test_image_1, "--report", "report.json", "--append", "--verbose", ], ) assert result.exit_code == 0 assert "Wrote import report" in result.output assert os.path.exists("report.json") with open("report.json", "r") as f: rows = json.load(f) filenames = [str(pathlib.Path(row["filename"]).name) for row in rows] assert filenames == [ pathlib.Path(TEST_IMAGE_1).name, pathlib.Path(TEST_IMAGE_1).name, ] @pytest.mark.test_import @pytest.mark.parametrize("report_file", ["report.db", "report.sqlite"]) def test_import_report_sqlite(report_file): """test import with --report option with sqlite output""" runner = CliRunner() cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) with runner.isolated_filesystem(): result = runner.invoke( import_cli, [ test_image_1, "--report", report_file, "--verbose", ], ) assert result.exit_code == 0 assert "Wrote import report" in result.output assert os.path.exists(report_file) conn = sqlite3.connect(report_file) c = conn.cursor() c.execute("SELECT filename FROM report") filenames = [str(pathlib.Path(row[0]).name) for row in c.fetchall()] assert filenames == [pathlib.Path(TEST_IMAGE_1).name] # test report gets overwritten result = runner.invoke( import_cli, [ test_image_1, "--report", report_file, "--verbose", ], ) assert result.exit_code == 0 assert "Wrote import report" in result.output assert os.path.exists(report_file) conn = sqlite3.connect(report_file) c = conn.cursor() c.execute("SELECT filename FROM report") filenames = [str(pathlib.Path(row[0]).name) for row in c.fetchall()] assert filenames == [pathlib.Path(TEST_IMAGE_1).name] # test report with --append result = runner.invoke( import_cli, [ test_image_1, "--report", report_file, "--append", "--verbose", ], ) assert result.exit_code == 0 assert "Wrote import report" in result.output assert os.path.exists(report_file) conn = sqlite3.connect(report_file) c = conn.cursor() c.execute("SELECT filename FROM report") filenames = [str(pathlib.Path(row[0]).name) for row in c.fetchall()] assert filenames == [ pathlib.Path(TEST_IMAGE_1).name, pathlib.Path(TEST_IMAGE_1).name, ] @pytest.mark.test_import def test_import_report_invalid_name(): """test import with --report option with invalid report""" runner = CliRunner() cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) with runner.isolated_filesystem(): result = runner.invoke( import_cli, [ test_image_1, "--report", "report", # invalid filename, no extension "--verbose", ], ) assert result.exit_code != 0 @pytest.mark.test_import def test_import_resume(monkeypatch: MonkeyPatch, tmpdir): """Test import with --resume""" monkeypatch.delenv("XDG_DATA_HOME", raising=False) monkeypatch.setenv("XDG_DATA_HOME", os.fspath(str(tmpdir))) cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() result = runner.invoke( import_cli, ["--verbose", test_image_1], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 import_data = parse_import_output(result.output) file_1 = pathlib.Path(test_image_1).name uuid_1 = import_data[file_1] photo_1 = Photo(uuid_1) assert photo_1.filename == file_1 # test resume test_image_2 = os.path.join(cwd, TEST_IMAGE_2) result = runner.invoke( import_cli, ["--verbose", "--resume", test_image_1, test_image_2], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 assert "Skipping" in result.output assert "1 skipped" in result.output assert "imported 1" in result.output @pytest.mark.test_import def test_import_parse_date(tmp_path: pathlib.Path): """Test import with --parse-date""" # set up test images os.environ["TZ"] = "US/Pacific" cwd = os.getcwd() test_image_source = os.path.join(cwd, TEST_IMAGE_NO_EXIF) default_date = datetime(1999, 1, 1, 0, 0, 0) test_data = [ ["img_1234_2020_11_22_12_34_56.jpg", datetime(2020, 11, 22, 12, 34, 56)], ["img_1234_20211122.jpg", datetime(2021, 11, 22, 0, 0, 0)], ["19991231_20221122.jpg", datetime(2022, 11, 22, 0, 0, 0)], ["test_parse_date.jpg", default_date], ] images = [] for img in [x[0] for x in test_data]: test_file = tmp_path / img shutil.copy(test_image_source, test_file) images.append(test_file) # set file time to default date os.utime(test_file, (default_date.timestamp(), default_date.timestamp())) runner = CliRunner() result = runner.invoke( import_cli, [ "--verbose", "--parse-date", "img_*_%Y_%m_%d_%H_%M_%S|img_{4}_%Y%m%d|_%Y%m%d.", *[str(x) for x in images], ], terminal_width=TERMINAL_WIDTH, ) assert result.exit_code == 0 # verify that the date was parsed correctly photosdb = PhotosDB() for test_case in test_data: photo = photosdb.query(QueryOptions(name=[test_case[0]]))[0] assert datetime_remove_tz(photo.date) == test_case[1] @pytest.mark.test_import def test_import_post_function(): """Test import with --post-function""" cwd = os.getcwd() test_image_1 = os.path.join(cwd, TEST_IMAGE_1) runner = CliRunner() # pylint: disable=not-context-manager with runner.isolated_filesystem(): with open("foo1.py", "w") as f: f.writelines( [ "def foo(photo, filepath, verbose, report_record, **kwargs):\n", " verbose('FOO BAR')\n", ] ) tempdir = os.getcwd() result = runner.invoke( import_cli, [ "import", "--verbose", test_image_1, "--post-function", f"{tempdir}/foo1.py::foo", ], ) assert result.exit_code == 0 assert "FOO BAR" in result.output