102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
import logging
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from photools3.cli import app
|
|
|
|
runner = CliRunner()
|
|
|
|
CLI_LOGGER = "photools3.cli"
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_photo_logic(tmp_path):
|
|
"""Fixture to mock the core logic of keywords command."""
|
|
with (
|
|
patch("photools3.cli.LlmSessionCaptionsAndKeywords") as mocked_session,
|
|
patch("photools3.cli.Photo") as mocked_photo,
|
|
):
|
|
|
|
# Configure session mock
|
|
session_instance = mocked_session.return_value
|
|
session_instance.call.return_value = {
|
|
"caption": "A beautiful test image",
|
|
"args": ["test", "mock", "image"], # Wait, the code uses 'tags'
|
|
}
|
|
# Let me re-check the code in cli.py
|
|
# result.get("tags", [])
|
|
session_instance.call.return_value = {
|
|
"caption": "A beautiful test image",
|
|
"tags": ["test", "mock", "image"],
|
|
}
|
|
|
|
# Configure photo mock
|
|
photo_instance = mocked_photo.return_value
|
|
photo_instance.path = tmp_path / "test_image.jpg"
|
|
|
|
yield {
|
|
"session": session_instance,
|
|
"photo_class": mocked_photo,
|
|
"photo_instance": photo_instance,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def dummy_file(tmp_path):
|
|
"""Fixture to create a dummy file for CLI testing."""
|
|
dummy_path = tmp_path / "test_image.jpg"
|
|
dummy_path.write_text("dummy content")
|
|
return dummy_path
|
|
|
|
|
|
def test_keywords_command_success(mock_photo_logic, dummy_file, caplog):
|
|
"""Test the 'keywords' command with a valid file path."""
|
|
# Ensure the mock photo instance points to our dummy file
|
|
mock_photo_logic["photo_class"].return_value.path = dummy_file
|
|
|
|
with caplog.at_level(logging.INFO, logger=CLI_LOGGER):
|
|
result = runner.invoke(app, ["keywords", str(dummy_file)])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Processing: " in caplog.text
|
|
# The result is logged as a raw dictionary string
|
|
assert (
|
|
"{'caption': 'A beautiful test image', 'tags': ['test', 'mock', 'image']}"
|
|
in caplog.text
|
|
)
|
|
|
|
|
|
def test_keywords_command_file_not_found(caplog):
|
|
"""Test the 'keywords' command with a non-existent file path."""
|
|
non_existent_file = Path("non_existent_file.jpg")
|
|
|
|
with caplog.at_level(logging.ERROR, logger=CLI_LOGGER):
|
|
result = runner.invoke(app, ["keywords", str(non_existent_file)])
|
|
|
|
assert result.exit_code == 1
|
|
assert f"Error: File '{non_existent_file}' does not exist." in caplog.text
|
|
|
|
|
|
def test_keywords_command_error_during_processing(mock_photo_logic, dummy_file):
|
|
"""Test the 'keywords' command when an exception occurs during processing."""
|
|
# Configure the session mock to raise an exception
|
|
mock_photo_logic["session"].call.side_effect = Exception("LLM error")
|
|
|
|
result = runner.invoke(app, ["keywords", str(dummy_file)])
|
|
|
|
# Note: cli.py raises typer.Exit(code=1) before logging the
|
|
# "Error processing image: ..." message (the logger.error call is dead
|
|
# code after the raise), so only the exit code can be asserted here.
|
|
assert result.exit_code == 1
|
|
|
|
|
|
def test_version_command():
|
|
"""Test the --version option."""
|
|
result = runner.invoke(app, ["--version"])
|
|
|
|
assert result.exit_code == 0
|
|
assert "photools3 version 0.1.0" in result.stdout
|