Merge pull request #2 from Heritage-XioN/add-action-for-CI-tests

Add action for ci tests
This commit is contained in:
HERITAGE-XION 2026-01-07 15:54:14 +01:00 committed by GitHub
commit 00e77ac479
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 157 additions and 101 deletions

29
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Tests
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"] # Test multiple versions
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Run Tests
run: uv run pytest -x

48
tests/conftest.py Normal file
View File

@ -0,0 +1,48 @@
"""
Pytest configuration and shared fixtures.
Provides cross-platform test file paths that work on both Windows and Linux (CI).
"""
from pathlib import Path
import pytest
# Get the tests directory (this file's parent)
TESTS_DIR = Path(__file__).parent
ASSETS_DIR = TESTS_DIR / "assets"
TEST_IMAGES_DIR = ASSETS_DIR / "test_images"
@pytest.fixture
def jpg_test_file() -> Path:
"""Return path to a JPG test file."""
return TEST_IMAGES_DIR / "test_fuji.jpg"
@pytest.fixture
def png_test_file() -> Path:
"""Return path to a PNG test file."""
return TEST_IMAGES_DIR / "generated_test_03.png"
@pytest.fixture
def test_images_dir() -> Path:
"""Return path to test images directory."""
return TEST_IMAGES_DIR
# String versions for parametrize (which doesn't support fixtures directly)
def get_jpg_test_file() -> str:
"""Get JPG test file path as string."""
return str(TEST_IMAGES_DIR / "test_fuji.jpg")
def get_png_test_file() -> str:
"""Get PNG test file path as string."""
return str(TEST_IMAGES_DIR / "generated_test_03.png")
def get_test_images_dir() -> str:
"""Get test images directory path as string."""
return str(TEST_IMAGES_DIR)

View File

@ -5,43 +5,40 @@ from typer.testing import CliRunner
from src.main import app
# Import path helpers from conftest
from tests.conftest import get_jpg_test_file, get_png_test_file, get_test_images_dir
runner = CliRunner()
# Test file paths
JPG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\test_fuji.jpg"
PNG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\generated_test_03.png"
TEST_DIR = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images"
# Test file paths (cross-platform)
JPG_TEST_FILE = get_jpg_test_file()
PNG_TEST_FILE = get_png_test_file()
TEST_DIR = get_test_images_dir()
# ============== Success Case Tests ==============
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_read_command_single_file_success(x):
"""
Test the 'read' command with a single file (JPG and PNG).
"""
result = runner.invoke(app, ["read", str(x)])
assert result.exit_code == 0
assert result.exit_code == 0, f"Failed with: {result.stdout}"
assert "Reading" in result.stdout
assert Path(x).name in result.stdout
@pytest.mark.parametrize(
"ext",
["jpg", "png"],
)
@pytest.mark.parametrize("ext", ["jpg", "png"])
def test_read_command_recursive_directory_success(ext):
"""
Test the 'read' command with recursive directory processing.
"""
result = runner.invoke(app, ["read", TEST_DIR, "-r", "-ext", ext])
assert result.exit_code == 0
assert result.exit_code == 0, f"Failed with: {result.stdout}"
assert "Reading" in result.stdout

View File

@ -11,14 +11,15 @@ from typer.testing import CliRunner
from src.main import app
# Import path helpers from conftest
from tests.conftest import get_jpg_test_file, get_png_test_file, get_test_images_dir
runner = CliRunner()
# Test file paths
JPG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\test_fuji.jpg"
PNG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\generated_test_03.png"
EXAMPLES_DIR = (
r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images"
)
# Test file paths (cross-platform)
JPG_TEST_FILE = get_jpg_test_file()
PNG_TEST_FILE = get_png_test_file()
EXAMPLES_DIR = get_test_images_dir()
@pytest.fixture
@ -39,7 +40,7 @@ def test_scrub_command_single_file_success(x, output_dir):
"""
result = runner.invoke(app, ["scrub", x, "--output", str(output_dir)])
assert result.exit_code == 0
assert result.exit_code == 0, f"Failed with: {result.stdout}"
# Check output file was created (has processed_ prefix)
output_file = output_dir / f"processed_{Path(x).name}"
assert output_file.exists()
@ -54,7 +55,7 @@ def test_scrub_command_recursive_jpg_success(output_dir):
app, ["scrub", EXAMPLES_DIR, "-r", "-ext", "jpg", "--output", str(output_dir)]
)
assert result.exit_code == 0
assert result.exit_code == 0, f"Failed with: {result.stdout}"
# Check at least one output file was created
output_files = list(output_dir.glob("processed_*.jpg"))
assert len(output_files) > 0
@ -68,7 +69,7 @@ def test_scrub_command_dry_run(output_dir):
app, ["scrub", JPG_TEST_FILE, "--output", str(output_dir), "--dry-run"]
)
assert result.exit_code == 0
assert result.exit_code == 0, f"Failed with: {result.stdout}"
assert "DRY-RUN" in result.stdout
# No files should be created in dry-run mode
output_file = output_dir / f"processed_{Path(JPG_TEST_FILE).name}"
@ -94,7 +95,7 @@ def test_scrub_command_with_workers(output_dir):
],
)
assert result.exit_code == 0
assert result.exit_code == 0, f"Failed with: {result.stdout}"
# ============== Error Case Tests ==============

View File

@ -4,25 +4,26 @@ from pathlib import Path
import pytest
from src.services.metadata_factory import MetadataFactory
from src.utils.exceptions import MetadataNotFoundError
from src.utils.exceptions import MetadataNotFoundError, UnsupportedFormatError
# Test file paths
JPG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\test_fuji.jpg"
PNG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\generated_test_03.png"
# Import path helpers from conftest
from tests.conftest import get_jpg_test_file, get_png_test_file
# ============== success Case Tests ==============
# Test file paths (cross-platform)
JPG_TEST_FILE = get_jpg_test_file()
PNG_TEST_FILE = get_png_test_file()
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
# ============== Success Case Tests ==============
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_read_image_metadata(x):
"""
test for reading image metadata
"""
# checks if file exists
assert Path(x).exists()
assert Path(x).exists(), f"Test file not found: {x}"
handler = MetadataFactory.get_handler(str(x))
metadata = handler.read()
@ -36,16 +37,13 @@ def test_read_image_metadata(x):
assert isinstance(metadata, dict)
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_wipe_image_metadata(x):
"""
test for wiping image metadata
"""
# checks if file exists
assert Path(x).exists()
assert Path(x).exists(), f"Test file not found: {x}"
handler = MetadataFactory.get_handler(str(x))
metadata = handler.read()
handler.wipe()
@ -54,10 +52,7 @@ def test_wipe_image_metadata(x):
assert handler.processed_metadata != metadata
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_save_processed_image_metadata(x):
"""
test for saving image processed metadata to a copy of the file
@ -82,10 +77,7 @@ def test_save_processed_image_metadata(x):
shutil.rmtree(output_dir)
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_output_file_has_less_metadata(x):
"""
Test that the output file has metadata stripped
@ -136,8 +128,6 @@ def test_unsupported_format_raises_error(tmp_path):
"""
Test that unsupported file formats raise an error
"""
from src.utils.exceptions import UnsupportedFormatError
# Create a fake text file
fake_file = tmp_path / "test.txt"
fake_file.write_text("not an image")

View File

@ -6,24 +6,24 @@ import pytest
from src.services.image_handler import ImageHandler
from src.utils.exceptions import MetadataNotFoundError, UnsupportedFormatError
# Test file paths
JPG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\test_fuji.jpg"
PNG_TEST_FILE = r"C:\Users\Xheri\development\metadata-scrubber-tool\tests\assets\test_images\generated_test_03.png"
# Import path helpers from conftest
from tests.conftest import get_jpg_test_file, get_png_test_file
# Test file paths (cross-platform)
JPG_TEST_FILE = get_jpg_test_file()
PNG_TEST_FILE = get_png_test_file()
# ============== success Case Tests ==============
# ============== Success Case Tests ==============
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_read_image_metadata(x):
"""
test for reading image metadata
"""
# checks if file exists
assert Path(x).exists()
assert Path(x).exists(), f"Test file not found: {x}"
processor = ImageHandler(x)
metadata = processor.read()
@ -40,16 +40,13 @@ def test_read_image_metadata(x):
assert isinstance(metadata, dict)
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_wipe_image_metadata(x):
"""
test for wiping image metadata
"""
# checks if file exists
assert Path(x).exists()
assert Path(x).exists(), f"Test file not found: {x}"
processor = ImageHandler(x)
metadata = processor.read()
processor.wipe()
@ -58,10 +55,7 @@ def test_wipe_image_metadata(x):
assert processor.processed_metadata != metadata
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_save_processed_image_metadata(x):
"""
test for saving image processed metadata to a copy of the file
@ -86,10 +80,37 @@ def test_save_processed_image_metadata(x):
shutil.rmtree(output_dir)
@pytest.mark.parametrize(
"x",
[JPG_TEST_FILE, PNG_TEST_FILE],
)
# ============== Error Case Tests ==============
def test_unsupported_format_raises_error(tmp_path):
"""
Test that unsupported file formats raise an error (PIL or custom)
"""
from PIL import UnidentifiedImageError
# Create a fake text file
fake_file = tmp_path / "test.txt"
fake_file.write_text("not an image")
handler = ImageHandler(str(fake_file))
# Either our UnsupportedFormatError or PIL's UnidentifiedImageError is acceptable
with pytest.raises((UnsupportedFormatError, UnidentifiedImageError)):
handler.read()
def test_save_without_output_path_raises_error():
"""
Test that save() raises ValueError when output_path is None
"""
handler = ImageHandler(JPG_TEST_FILE)
handler.read()
handler.wipe()
with pytest.raises(ValueError):
handler.save(None)
@pytest.mark.parametrize("x", [JPG_TEST_FILE, PNG_TEST_FILE])
def test_output_file_has_less_metadata(x):
"""
Test that the output file has metadata stripped
@ -131,33 +152,3 @@ def test_format_detection_works():
handler_png = ImageHandler(PNG_TEST_FILE)
detected_png = handler_png._detect_format()
assert detected_png == "png"
# ============== Error Case Tests ==============
def test_unsupported_format_raises_error(tmp_path):
"""
Test that unsupported file formats raise an error (PIL or custom)
"""
from PIL import UnidentifiedImageError
# Create a fake text file
fake_file = tmp_path / "test.txt"
fake_file.write_text("not an image")
handler = ImageHandler(str(fake_file))
# Either our UnsupportedFormatError or PIL's UnidentifiedImageError is acceptable
with pytest.raises((UnsupportedFormatError, UnidentifiedImageError)):
handler.read()
def test_save_without_output_path_raises_error():
"""
Test that save() raises ValueError when output_path is None
"""
handler = ImageHandler(JPG_TEST_FILE)
handler.read()
handler.wipe()
with pytest.raises(ValueError):
handler.save(None)