chore(lint): fixed pyright errors, tests

This commit is contained in:
2025-04-30 16:42:10 +03:00
parent a0b066cefe
commit 5961f4e65e
3 changed files with 274 additions and 349 deletions

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import argparse import argparse
import hashlib import hashlib
import logging import logging
@@ -41,6 +43,7 @@ class AeonViewImages:
self.project_path = project_path or None self.project_path = project_path or None
self.url = url or None self.url = url or None
self.args = args or {} self.args = args or {}
self.simulate = getattr(args, "simulate", False)
def get_image_paths( def get_image_paths(
self, url: str | None, destination_base: Path | None, date: datetime self, url: str | None, destination_base: Path | None, date: datetime
@@ -86,7 +89,7 @@ class AeonViewImages:
destination_file = AeonViewHelpers.build_path(destination, file_name) destination_file = AeonViewHelpers.build_path(destination, file_name)
if not destination.exists(): if not destination.exists():
if self.args.get("simulate", False): if getattr(self.args, "simulate", False):
logging.info("Simulate: would create %s", destination) logging.info("Simulate: would create %s", destination)
else: else:
AeonViewHelpers.mkdir_p(destination) AeonViewHelpers.mkdir_p(destination)
@@ -116,7 +119,7 @@ class AeonViewImages:
Download the image from the URL and save it to the project directory. Download the image from the URL and save it to the project directory.
""" """
date_param = self.args.get("date", None) date_param = getattr(self.args, "date", None)
if date_param is not None: if date_param is not None:
try: try:
@@ -143,7 +146,7 @@ class AeonViewImages:
logging.info("Saving image to %s", dest_file) logging.info("Saving image to %s", dest_file)
if not self.args.get("simulate", False): if not self.simulate:
AeonViewHelpers.mkdir_p(dest_dir) AeonViewHelpers.mkdir_p(dest_dir)
self.download_image(dest_file) self.download_image(dest_file)
else: else:
@@ -167,10 +170,7 @@ class AeonViewImages:
logging.error("Invalid destination path.") logging.error("Invalid destination path.")
sys.exit(1) sys.exit(1)
if ( if not getattr(self.args, "simulate", False):
self.args.get("simulate", False) is False
or self.args.get("simulate", None) is None
):
logging.info("Downloading image from %s", self.url) logging.info("Downloading image from %s", self.url)
response = requests.get(self.url, stream=True, timeout=10) response = requests.get(self.url, stream=True, timeout=10)
if response.status_code == 200: if response.status_code == 200:
@@ -210,14 +210,11 @@ class AeonViewVideos:
self.project_path = project_path self.project_path = project_path
self.args = args self.args = args
self.args["simulate"] = ( self.simulate = getattr(args, "simulate", False)
args.get("simulate", False) if isinstance(args, dict) else False self.fps = getattr(args, "fps", 10)
) self.day = getattr(args, "day", None)
self.args["fps"] = args.get("fps", 10) if isinstance(args, dict) else 10 self.month = getattr(args, "month", None)
self.year = getattr(args, "year", None)
self.day = args.get("day", None) if args else None
self.month = args.get("month", None) if args else None
self.year = args.get("year", None) if args else None
self.path_images = AeonViewHelpers.build_path(self.project_path, "img") self.path_images = AeonViewHelpers.build_path(self.project_path, "img")
self.path_videos = AeonViewHelpers.build_path(self.project_path, "vid") self.path_videos = AeonViewHelpers.build_path(self.project_path, "vid")
@@ -235,13 +232,13 @@ class AeonViewVideos:
output_dir = AeonViewHelpers.build_path(self.path_videos, year_month) output_dir = AeonViewHelpers.build_path(self.path_videos, year_month)
output_file = AeonViewHelpers.build_path(output_dir, f"{self.day}.mp4") output_file = AeonViewHelpers.build_path(output_dir, f"{self.day}.mp4")
ffmpeg_cmd = AeonViewHelpers.generate_ffmpeg_command( ffmpeg_cmd = AeonViewHelpers.generate_ffmpeg_command(
input_dir, output_file, self.args.fps input_dir, output_file, self.fps
) )
logging.info("Generating video from %s", input_dir) logging.info("Generating video from %s", input_dir)
logging.info("Output file will be %s", output_file) logging.info("Output file will be %s", output_file)
if not self.args.get("simulate", False): if not self.simulate:
logging.info("Running ffmpeg command: %s", " ".join(ffmpeg_cmd)) logging.info("Running ffmpeg command: %s", " ".join(ffmpeg_cmd))
if not os.path.exists(input_dir): if not os.path.exists(input_dir):
AeonViewHelpers.mkdir_p(output_dir) AeonViewHelpers.mkdir_p(output_dir)
@@ -418,7 +415,15 @@ class AeonViewApp:
""" """
def __init__(self): def __init__(self):
self.args, self.parser = AeonViewHelpers.parse_arguments() args, parser = AeonViewHelpers.parse_arguments()
self.args: argparse.Namespace = args
self.parser: argparse.ArgumentParser = parser
if self.args is None:
logging.error("No arguments provided.")
self.parser.print_help()
sys.exit(1)
AeonViewHelpers.setup_logger(self.args.verbose) AeonViewHelpers.setup_logger(self.args.verbose)
self.base_path = Path(self.args.dest).resolve() self.base_path = Path(self.args.dest).resolve()
@@ -480,6 +485,8 @@ class AeonViewApp:
logging.error("Project path %s does not exist.", project_path) logging.error("Project path %s does not exist.", project_path)
sys.exit(1) sys.exit(1)
generate_date = None
try: try:
generate_date = ( generate_date = (
datetime.strptime(self.args.generate, "%Y-%m-%d") datetime.strptime(self.args.generate, "%Y-%m-%d")
@@ -502,7 +509,9 @@ class AeonViewApp:
logging.error("Invalid date: %s-%s-%s", year, month, day) logging.error("Invalid date: %s-%s-%s", year, month, day)
sys.exit(1) sys.exit(1)
avm = AeonViewVideos(project_path, vars(self.args)) args: argparse.Namespace = self.args
avm = AeonViewVideos(project_path, args)
if self.args.timeframe == "daily": if self.args.timeframe == "daily":
avm.generate_daily_video() avm.generate_daily_video()

View File

@@ -5,7 +5,7 @@ import subprocess
import tempfile import tempfile
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from unittest import TestCase, mock from unittest import mock
import pytest import pytest
@@ -16,7 +16,6 @@ from aeonview import (
AeonViewVideos, AeonViewVideos,
) )
# Define values used in the tests
default_dest = str(Path.cwd() / "projects") default_dest = str(Path.cwd() / "projects")
default_project = "default" default_project = "default"
default_fps = 10 default_fps = 10
@@ -28,348 +27,264 @@ default_test_path = Path("/tmp/test_project").resolve()
tmp_images = Path("/tmp/images") tmp_images = Path("/tmp/images")
# Define the Helpers class with methods to be tested def test_check_date_valid():
class TestHelpers(TestCase): assert AeonViewHelpers.check_date(2023, 12, 31)
def test_check_date_valid(self):
self.assertTrue(AeonViewHelpers.check_date(2023, 12, 31))
def test_check_date_invalid(self):
self.assertFalse(AeonViewHelpers.check_date(2023, 2, 30))
def test_mkdir_p_creates_directory(self):
with tempfile.TemporaryDirectory() as tmp:
test_path = Path(tmp) / "a" / "b" / "c"
AeonViewHelpers.mkdir_p(test_path)
self.assertTrue(test_path.exists())
self.assertTrue(test_path.is_dir())
def test_get_extension_valid(self):
prefix = default_image_domain
types = ["jpg", "png", "gif", "webp"]
for ext in types:
self.assertEqual(
AeonViewHelpers.get_extension(f"{prefix}.{ext}"),
f".{ext}",
)
def test_get_extension_invalid(self):
self.assertEqual(
AeonViewHelpers.get_extension(default_image_domain), ".jpg"
) # Default behavior
self.assertIsNone(AeonViewHelpers.get_extension(None))
class TestFFmpegCommand(TestCase): def test_check_date_invalid():
def test_generate_ffmpeg_command(self): assert not AeonViewHelpers.check_date(2023, 2, 30)
input_dir = tmp_images
output_file = Path("/tmp/output.mp4")
fps = 24
cmd = AeonViewHelpers.generate_ffmpeg_command(
input_dir, output_file, fps
)
self.assertIn("ffmpeg", cmd[0])
self.assertIn(str(fps), cmd)
self.assertEqual(str(output_file), cmd[-1])
self.assertIn(str(input_dir / "*.{jpg,jpeg,png,gif,webp}"), cmd)
def test_generate_ffmpeg_command_output_format(self):
input_dir = tmp_images
output_file = Path("/tmp/video.mp4")
cmd = AeonViewHelpers.generate_ffmpeg_command(
input_dir, output_file, 30
)
self.assertIn("/tmp/images/*.{jpg,jpeg,png,gif,webp}", cmd)
self.assertIn("/tmp/video.mp4", cmd)
self.assertIn("-c:v", cmd)
self.assertIn("libx264", cmd)
self.assertIn("-pix_fmt", cmd)
self.assertIn("yuv420p", cmd)
@mock.patch("subprocess.run")
def test_simulate_ffmpeg_call(self, mock_run):
input_dir = tmp_images
output_file = Path("/tmp/out.mp4")
cmd = AeonViewHelpers.generate_ffmpeg_command(
input_dir, output_file, 10
)
subprocess.run(cmd, check=True)
mock_run.assert_called_once_with(cmd)
class TestAeonViewImages(TestCase): def test_mkdir_p_creates_directory():
def setUp(self): with tempfile.TemporaryDirectory() as tmp:
self.args = argparse.Namespace() test_path = Path(tmp) / "a" / "b" / "c"
self.args.simulate = False AeonViewHelpers.mkdir_p(test_path)
self.args.date = "2025-04-10 12:30:45" assert test_path.exists()
self.args.url = f"{default_image_domain}.jpg" assert test_path.is_dir()
self.args.dest = default_test_path
self.args.project = default_project
self.args.verbose = default_verbose
self.args.fps = default_fps
self.args.timeframe = default_timeframe
self.project_path = default_test_path
self.url = f"{default_image_domain}.jpg"
def test_get_image_paths_valid(self):
url = f"{default_image_domain}.jpg"
destination_base = default_test_path
date = datetime(2025, 4, 10, 12, 30, 45)
aeon_view_images = AeonViewImages(destination_base, url)
paths = aeon_view_images.get_image_paths(url, destination_base, date)
self.assertEqual(paths["url"], url)
self.assertEqual(paths["file"], "12-30-45.jpg")
self.assertEqual(
paths["destinations"]["file"],
destination_base / "2025-04" / "10" / "12-30-45.jpg",
)
def test_get_image_paths_invalid_url(self): @pytest.mark.parametrize("ext", ["jpg", "png", "gif", "webp"])
with pytest.raises(SystemExit): def test_get_extension_valid(ext):
with self.assertLogs(level="ERROR") as log: assert AeonViewHelpers.get_extension(f"{default_image_domain}.{ext}") == f".{ext}"
aeon_view_images = AeonViewImages(
default_test_path, "invalid-url"
)
aeon_view_images.get_image_paths(
"invalid-url",
default_test_path,
datetime(2025, 4, 10),
)
self.assertIn(AeonViewMessages.INVALID_URL, log.output[0])
def test_get_image_paths_invalid_date(self):
with pytest.raises(SystemExit):
with self.assertLogs(level="ERROR") as log:
aeon_view_images = AeonViewImages(
default_test_path, f"{default_image_domain}.jpg"
)
aeon_view_images.get_image_paths(
f"{default_image_domain}.jpg",
default_test_path,
"invalid-date",
)
self.assertIn(AeonViewMessages.INVALID_DATE, log.output[0])
@mock.patch("aeonview.AeonViewHelpers.mkdir_p") def test_get_extension_invalid():
@mock.patch("aeonview.AeonViewImages.download_image") assert AeonViewHelpers.get_extension(default_image_domain) == ".jpg"
def test_get_current_image(self, mock_download_image, mock_mkdir_p): assert AeonViewHelpers.get_extension(None) is None
project_path = default_test_path
url = f"{default_image_domain}.jpg"
args = argparse.Namespace(simulate=False, date="2025-04-10 12:30:45") def test_generate_ffmpeg_command():
avi = AeonViewImages(project_path, url, args) input_dir = tmp_images
output_file = Path("/tmp/output.mp4")
fps = 24
cmd = AeonViewHelpers.generate_ffmpeg_command(input_dir, output_file, fps)
assert "ffmpeg" in cmd[0]
assert str(fps) in cmd
assert str(output_file) == cmd[-1]
assert str(input_dir / "*.{jpg,jpeg,png,gif,webp}") in cmd
def test_generate_ffmpeg_command_output_format():
input_dir = tmp_images
output_file = Path("/tmp/video.mp4")
cmd = AeonViewHelpers.generate_ffmpeg_command(input_dir, output_file, 30)
assert "/tmp/images/*.{jpg,jpeg,png,gif,webp}" in cmd
assert "/tmp/video.mp4" in cmd
assert "-c:v" in cmd
assert "libx264" in cmd
assert "-pix_fmt" in cmd
assert "yuv420p" in cmd
@mock.patch("subprocess.run")
def test_simulate_ffmpeg_call(mock_run):
input_dir = tmp_images
output_file = Path("/tmp/out.mp4")
cmd = AeonViewHelpers.generate_ffmpeg_command(input_dir, output_file, 10)
subprocess.run(cmd, check=True)
mock_run.assert_called_once_with(cmd, check=True)
def test_get_image_paths_valid():
url = f"{default_image_domain}.jpg"
destination_base = default_test_path
date = datetime(2025, 4, 10, 12, 30, 45)
aeon_view_images = AeonViewImages(destination_base, url)
paths = aeon_view_images.get_image_paths(url, destination_base, date)
assert paths["url"] == url
assert paths["file"] == "12-30-45.jpg"
assert paths["destinations"]["file"] == destination_base / "2025-04" / "10" / "12-30-45.jpg"
def test_get_image_paths_invalid_url():
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
aeon_view_images = AeonViewImages(default_test_path, "invalid-url")
aeon_view_images.get_image_paths("invalid-url", default_test_path, datetime(2025, 4, 10))
assert AeonViewMessages.INVALID_URL in log.call_args[0][0]
def test_get_image_paths_invalid_date():
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
aeon_view_images = AeonViewImages(default_test_path, f"{default_image_domain}.jpg")
aeon_view_images.get_image_paths(f"{default_image_domain}.jpg", default_test_path, "invalid-date") # pyright: ignore [reportArgumentType]
assert AeonViewMessages.INVALID_DATE in log.call_args[0][0]
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
@mock.patch("aeonview.AeonViewImages.download_image")
def test_get_current_image(mock_download_image, mock_mkdir_p):
args = argparse.Namespace(simulate=False, date="2025-04-10 12:30:45")
avi = AeonViewImages(default_test_path, f"{default_image_domain}.jpg", args)
avi.get_current_image()
mock_mkdir_p.assert_called()
mock_download_image.assert_called()
@mock.patch("aeonview.requests.get")
def test_download_image_success(mock_get):
mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.iter_content = mock.Mock(return_value=[b"data"])
mock_get.return_value = mock_response
args = argparse.Namespace(simulate=False)
avi = AeonViewImages(default_test_path, f"{default_image_domain}.jpg", args)
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
destination = Path(temp_file.name)
avi.download_image(destination)
mock_get.assert_called_once_with(f"{default_image_domain}.jpg", stream=True, timeout=10)
assert destination.exists()
@mock.patch("aeonview.requests.get")
def test_download_image_failure(mock_get):
mock_response = mock.Mock()
mock_response.status_code = 404
mock_get.return_value = mock_response
args = argparse.Namespace(simulate=False)
avi = AeonViewImages(default_test_path, f"{default_image_domain}.jpg", args)
destination = Path("/tmp/image.jpg")
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
avi.download_image(destination)
assert AeonViewMessages.DOWNLOAD_FAILURE in log.call_args[0][0]
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
@mock.patch("subprocess.run")
def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", year="2025")
avv = AeonViewVideos(default_test_path, args)
with mock.patch("aeonview.logging.info") as log:
avv.generate_daily_video()
last_call_args = log.call_args_list[-1][0]
assert last_call_args[0] == "%s: %s"
assert last_call_args[1] == AeonViewMessages.VIDEO_GENERATION_SUCCESS
assert last_call_args[2] == default_test_path / "vid/2025-04/01.mp4"
mock_mkdir_p.assert_called()
mock_subprocess_run.assert_called()
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
def test_generate_daily_video_simulate(mock_mkdir_p):
args = argparse.Namespace(simulate=True, fps=10, day="01", month="04", year="2025")
avv = AeonViewVideos(default_test_path, args)
avv.generate_daily_video()
mock_mkdir_p.assert_not_called()
def test_generate_monthly_video_not_implemented():
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", year="2025")
avv = AeonViewVideos(default_test_path, args)
with pytest.raises(NotImplementedError):
avv.generate_monthly_video(Path("/tmp"))
def test_generate_yearly_video_not_implemented():
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", year="2025")
avv = AeonViewVideos(default_test_path, args)
with pytest.raises(NotImplementedError):
avv.generate_yearly_video(Path("/tmp"))
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--url", f"{default_image_domain}.jpg"])
def test_parse_arguments_image_mode():
args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "image"
assert args.url == f"{default_image_domain}.jpg"
assert args.dest == default_dest
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--project", f"{default_project}"])
def test_parse_arguments_video_mode():
args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "video"
assert args.project == default_project
assert args.dest == default_dest
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--simulate"])
def test_parse_arguments_simulate_mode():
args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "image"
assert args.simulate
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--fps", "30"])
def test_parse_arguments_fps():
args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "video"
assert args.project == default_project
assert args.dest == default_dest
assert args.fps == 30
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--generate", "2023-10-01"])
def test_parse_arguments_generate_date():
args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "video"
assert args.generate == "2023-10-01"
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--verbose"])
def test_parse_arguments_verbose():
args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "image"
assert args.verbose
@mock.patch("sys.argv", ["aeonview.py"])
def test_parse_arguments_defaults():
args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "image"
assert args.project == default_project
assert args.dest == default_dest
assert args.fps == 10
assert args.timeframe == "daily"
assert not args.simulate
assert not args.verbose
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
@mock.patch("aeonview.AeonViewImages.download_image")
def test_image_simulation(mock_download_image, mock_mkdir_p):
args = mock.MagicMock()
args.simulate = True
args.date = "2025-04-10 12:30:45"
avi = AeonViewImages(default_test_path, f"{default_image_domain}.jpg", args)
with mock.patch("aeonview.logging.info") as log:
avi.get_current_image() avi.get_current_image()
mock_mkdir_p.assert_called() mock_mkdir_p.assert_not_called()
mock_download_image.assert_called() mock_download_image.assert_not_called()
assert any("Saving image to" in str(call) for call in log.call_args_list)
@mock.patch("aeonview.requests.get")
def test_download_image_success(self, mock_get):
mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.iter_content = mock.Mock(return_value=[b"data"])
mock_get.return_value = mock_response
project_path = default_test_path
url = f"{default_image_domain}.jpg"
args = argparse.Namespace(simulate=False)
avi = AeonViewImages(project_path, url, args)
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
destination = Path(temp_file.name)
avi.download_image(destination)
mock_get.assert_called_once_with(url, stream=True)
self.assertTrue(destination.exists())
@mock.patch("aeonview.requests.get")
def test_download_image_failure(self, mock_get):
mock_response = mock.Mock()
mock_response.status_code = 404
mock_get.return_value = mock_response
project_path = default_test_path
url = f"{default_image_domain}.jpg"
args = argparse.Namespace(simulate=False)
avi = AeonViewImages(project_path, url, args)
destination = Path("/tmp/image.jpg")
with pytest.raises(SystemExit):
with self.assertLogs(level="ERROR") as log:
avi.download_image(destination)
self.assertIn(AeonViewMessages.DOWNLOAD_FAILURE, log.output[0])
class TestAeonViewVideos(TestCase): @mock.patch("aeonview.AeonViewHelpers.mkdir_p")
@mock.patch("aeonview.AeonViewHelpers.mkdir_p") @mock.patch("subprocess.run")
@mock.patch("subprocess.run") def test_video_simulation(mock_subprocess_run, mock_mkdir_p):
def test_generate_daily_video(self, mock_subprocess_run, mock_mkdir_p): args = mock.MagicMock()
project_path = default_test_path args.simulate = True
args = argparse.Namespace( args.fps = 10
simulate=False, fps=10, day="01", month="04", year="2025" args.day = "01"
) args.month = "01"
avv = AeonViewVideos(project_path, args) args.year = "2023"
with self.assertLogs(level="INFO") as log: avv = AeonViewVideos(default_test_path, args)
avv.generate_daily_video() with mock.patch("aeonview.logging.info") as log:
msg = AeonViewMessages.VIDEO_GENERATION_SUCCESS
expected_message = (
f"{msg}: {default_test_path / 'vid/2025-04/01.mp4'}"
)
self.assertIn(
expected_message, log.output[-1]
) # Ensure it's the last log entry
mock_mkdir_p.assert_called()
mock_subprocess_run.assert_called()
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
def test_generate_daily_video_simulate(self, mock_mkdir_p):
project_path = default_test_path
args = argparse.Namespace(
simulate=True, fps=10, day="01", month="04", year="2025"
)
avv = AeonViewVideos(project_path, args)
avv.generate_daily_video() avv.generate_daily_video()
mock_mkdir_p.assert_not_called() mock_mkdir_p.assert_not_called()
mock_subprocess_run.assert_not_called()
def test_generate_monthly_video_not_implemented(self): assert any("Generating video from" in str(call) for call in log.call_args_list)
project_path = default_test_path
args = argparse.Namespace(
simulate=False, fps=10, day="01", month="04", year="2025"
)
avv = AeonViewVideos(project_path, args)
with pytest.raises(NotImplementedError):
avv.generate_monthly_video(Path("/tmp"))
def test_generate_yearly_video_not_implemented(self):
project_path = default_test_path
args = argparse.Namespace(
simulate=False, fps=10, day="01", month="04", year="2025"
)
avv = AeonViewVideos(project_path, args)
with pytest.raises(NotImplementedError):
avv.generate_yearly_video(Path("/tmp"))
class TestHelpersArguments(TestCase): @mock.patch("logging.basicConfig")
def setUp(self): def test_setup_logger_verbose(mock_basic_config):
self.default_dest = str(Path.cwd() / "projects") AeonViewHelpers.setup_logger(verbose=True)
mock_basic_config.assert_called_once_with(level=logging.DEBUG, format="[%(levelname)s] %(message)s")
@mock.patch(
"sys.argv",
[
"aeonview.py",
"--mode",
"image",
"--url",
f"{default_image_domain}.jpg",
],
)
def test_parse_arguments_image_mode(self):
args, _ = AeonViewHelpers.parse_arguments()
self.assertEqual(args.mode, "image")
self.assertEqual(args.url, f"{default_image_domain}.jpg")
self.assertEqual(args.dest, self.default_dest)
@mock.patch(
"sys.argv",
["aeonview.py", "--mode", "video", "--project", f"{default_project}"],
)
def test_parse_arguments_video_mode(self):
args, _ = AeonViewHelpers.parse_arguments()
self.assertEqual(args.mode, "video")
self.assertEqual(args.project, f"{default_project}")
self.assertEqual(args.dest, self.default_dest)
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--simulate"])
def test_parse_arguments_simulate_mode(self):
args, _ = AeonViewHelpers.parse_arguments()
self.assertEqual(args.mode, "image")
self.assertTrue(args.simulate)
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--fps", "30"])
def test_parse_arguments_fps(self):
args, _ = AeonViewHelpers.parse_arguments()
self.assertEqual(args.mode, "video")
self.assertEqual(args.project, f"{default_project}")
self.assertEqual(args.dest, self.default_dest)
self.assertEqual(args.fps, 30)
@mock.patch(
"sys.argv",
["aeonview.py", "--mode", "video", "--generate", "2023-10-01"],
)
def test_parse_arguments_generate_date(self):
args, _ = AeonViewHelpers.parse_arguments()
self.assertEqual(args.mode, "video")
self.assertEqual(args.generate, "2023-10-01")
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--verbose"])
def test_parse_arguments_verbose(self):
args, _ = AeonViewHelpers.parse_arguments()
self.assertEqual(args.mode, "image")
self.assertTrue(args.verbose)
@mock.patch("sys.argv", ["aeonview.py"])
def test_parse_arguments_defaults(self):
args, _ = AeonViewHelpers.parse_arguments()
self.assertEqual(args.mode, "image")
self.assertEqual(args.project, f"{default_project}")
self.assertEqual(args.dest, self.default_dest)
self.assertEqual(args.fps, 10)
self.assertEqual(args.timeframe, "daily")
self.assertFalse(args.simulate)
self.assertFalse(args.verbose)
class TestAeonViewSimulation(TestCase): @mock.patch("logging.basicConfig")
@mock.patch("aeonview.AeonViewHelpers.mkdir_p") def test_setup_logger_non_verbose(mock_basic_config):
@mock.patch("aeonview.AeonViewImages.download_image") AeonViewHelpers.setup_logger(verbose=False)
def test_image_simulation(self, mock_download_image, mock_mkdir_p): mock_basic_config.assert_called_once_with(level=logging.INFO, format="[%(levelname)s] %(message)s")
args = mock.MagicMock()
args.simulate = True
args.date = "2025-04-10 12:30:45"
url = f"{default_image_domain}.jpg"
project_path = Path("/tmp/test_project").resolve()
avi = AeonViewImages(project_path, url, args)
with mock.patch("aeonview.logging.info") as mock_logging:
avi.get_current_image()
mock_mkdir_p.assert_not_called()
mock_download_image.assert_not_called()
mock_logging.assert_any_call(
f"Saving image to {project_path}/img/2025-04/10/12-30-45.jpg"
)
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
@mock.patch("subprocess.run")
def test_video_simulation(self, mock_subprocess_run, mock_mkdir_p):
args = mock.MagicMock()
args.simulate = True
args.fps = 10
args.day = "01"
args.month = "01"
args.year = "2023"
project_path = Path("/tmp/test_project").resolve()
avv = AeonViewVideos(project_path, args)
with mock.patch("aeonview.logging.info") as mock_logging:
avv.generate_daily_video()
mock_mkdir_p.assert_not_called()
mock_subprocess_run.assert_not_called()
mock_logging.assert_any_call(
f"Generating video from {project_path}/vid/2023-01/01"
)
class TestSetupLogger(TestCase):
@mock.patch("logging.basicConfig")
def test_setup_logger_verbose(self, mock_basic_config):
AeonViewHelpers.setup_logger(verbose=True)
mock_basic_config.assert_called_once_with(
level=logging.DEBUG, format="[%(levelname)s] %(message)s"
)
@mock.patch("logging.basicConfig")
def test_setup_logger_non_verbose(self, mock_basic_config):
AeonViewHelpers.setup_logger(verbose=False)
mock_basic_config.assert_called_once_with(
level=logging.INFO, format="[%(levelname)s] %(message)s"
)
mock_basic_config.reset_mock()

View File

@@ -7,5 +7,6 @@ pre-commit>=3.5.0
# Testing # Testing
pytest>=8.0.0 pytest>=8.0.0
pytest-cov>=4.1.0 pytest-cov>=4.1.0
# Linting & formatting # Linting & formatting
ruff>=0.3.3 ruff>=0.3.3