added infrastructure code
Code Quality Pipeline / code-quality (pull_request) Failing after 1m19s
Test Python Package / test (pull_request) Failing after 50s

This commit is contained in:
Brian Bjarke Jensen
2025-09-18 12:13:39 +02:00
parent 9b4f0daf58
commit 0ba860b892
34 changed files with 3390 additions and 1 deletions
@@ -0,0 +1,110 @@
"""Integration tests for AnnotationRepositoryInterface."""
from uuid import UUID
import pytest
from data_store.interfaces import AnnotationRepositoryInterface
from data_store.dto import Annotation
def test_get_raises_not_implemented_if_not_overwritten(
job_id_list: list[UUID],
) -> None:
"""Test that calling get on a subclass without get implemented raises NotImplementedError."""
class Incomplete(AnnotationRepositoryInterface):
"""A class missing the get method."""
def get(self, job_id: UUID) -> Annotation | None:
return super().get(job_id) # type: ignore
def put(self, annotation: Annotation, job_id: UUID) -> None:
return None
def delete(self, job_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.get(job_id_list[0])
def test_put_raises_not_implemented_if_not_overwritten(
annotation: Annotation,
job_id_list: list[UUID],
) -> None:
"""Test that calling put on a subclass without put implemented raises NotImplementedError."""
class Incomplete(AnnotationRepositoryInterface):
"""A class missing the put method."""
def get(self, job_id: UUID) -> Annotation | None:
return None
def put(self, annotation: Annotation, job_id: UUID) -> None:
return super().put(annotation, job_id) # type: ignore
def delete(self, job_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.put(annotation, job_id_list[0])
def test_delete_raises_not_implemented_if_not_overwritten(
job_id_list: list[UUID],
) -> None:
"""Test that calling delete on a subclass without delete implemented raises NotImplementedError."""
class Incomplete(AnnotationRepositoryInterface):
"""A class missing the delete method."""
def get(self, job_id: UUID) -> Annotation | None:
return None
def put(self, annotation: Annotation, job_id: UUID) -> None:
return None
def delete(self, job_id: UUID) -> None:
return super().delete(job_id) # type: ignore
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.delete(job_id_list[0])
def test_list_all_raises_not_implemented_if_not_overwritten() -> None:
"""Test that calling list_all on a subclass without list_all implemented raises NotImplementedError."""
class Incomplete(AnnotationRepositoryInterface):
"""A class missing the list_all method."""
def get(self, job_id: UUID) -> Annotation | None:
return None
def put(self, annotation: Annotation, job_id: UUID) -> None:
return None
def delete(self, job_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return super().list_all() # type: ignore
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.list_all()
# Allows local debugging by running file as script
if __name__ == "__main__":
pytest.main(["-s", "-v", __file__])
@@ -0,0 +1,86 @@
"""Integration tests for AnnotationRepository."""
from uuid import UUID
import pytest
from data_store.repositories.annotation_repository import AnnotationRepository
from data_store.dto import (
Annotation,
)
def test_get_annotation(
annotation_in_minio: tuple[UUID, Annotation],
annotation_repository: AnnotationRepository,
) -> None:
"""Test retrieving an annotation from AnnotationRepository."""
# Arrange
job_id, expected_annotation = annotation_in_minio
# Act
retrieved_annotation = annotation_repository.get(job_id)
# Assert
assert retrieved_annotation is not None
assert isinstance(retrieved_annotation, Annotation)
assert retrieved_annotation == expected_annotation
def test_put_annotation(
annotation_repository: AnnotationRepository,
annotation: Annotation,
job_id_list: list[UUID],
) -> None:
"""Test storing an annotation in AnnotationRepository."""
# Arrange
job_id = job_id_list[0]
assert annotation_repository.get(job_id) is None
# Act
annotation_repository.put(annotation, job_id)
# Assert
received_annotation = annotation_repository.get(job_id)
assert received_annotation is not None
assert isinstance(received_annotation, Annotation)
assert received_annotation == annotation
def test_delete_annotation(
annotation_in_minio: tuple[UUID, Annotation],
annotation_repository: AnnotationRepository,
) -> None:
"""Test deleting an annotation from AnnotationRepository."""
# Arrange
job_id, _ = annotation_in_minio
assert annotation_repository.get(job_id) is not None
# Act
annotation_repository.delete(job_id)
# Assert
assert annotation_repository.get(job_id) is None
def test_list_all_annotations(
annotation_in_minio: tuple[UUID, Annotation],
annotation_repository: AnnotationRepository,
) -> None:
"""Test listing all annotations in AnnotationRepository."""
# Arrange
job_id, _ = annotation_in_minio
# Act
annotation_ids = annotation_repository.list_all()
# Assert
assert isinstance(annotation_ids, list)
assert all(isinstance(i, UUID) for i in annotation_ids)
assert job_id in annotation_ids
def test_list_all_annotations_empty(
annotation_repository: AnnotationRepository,
) -> None:
"""Test listing all annotations in an empty AnnotationRepository."""
# Act
annotation_ids = annotation_repository.list_all()
# Assert
assert isinstance(annotation_ids, list)
assert len(annotation_ids) == 0
# Allows local debugging by running file as script
if __name__ == "__main__":
pytest.main(["-s", "-v", __file__])
+340
View File
@@ -0,0 +1,340 @@
"""Integration test configuration."""
import logging
import os
import shutil
import tempfile
from pathlib import Path
import json
from collections.abc import Generator
from uuid import UUID
from io import BytesIO
import pandas as pd
from PIL import Image
import pytest
import structlog
from minio import Minio, S3Error
from testcontainers.minio import MinioContainer
from python_repositories.adapters import MinioAdapter
from data_store.repositories import (
ImageRepository,
AnnotationRepository,
JobRepository,
)
from data_store.dto import (
Annotation,
DistanceEnum,
AngleEnum,
PointOfViewEnum,
ContactEnum,
Job,
)
# Last Docker image with a useful WebUI
MINIO_DOCKER_IMAGE = "minio/minio:RELEASE.2023-03-20T20-16-18Z"
@pytest.fixture(scope="session", autouse=True)
def configure_logging() -> None:
"""Configure logging for the test session."""
# Configure structlog
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# Set up basic logging configuration
logging.basicConfig(level=logging.ERROR)
@pytest.fixture(scope="session")
def minio_container() -> Generator[dict[str, str]]:
"""Set up MinIO container and provide credentials for testing."""
# Set variables
access_key = "minioadmin"
secret_key = "minioadmin"
bucket_name = "test-bucket"
# Start container
container = MinioContainer(
image=MINIO_DOCKER_IMAGE,
access_key=access_key,
secret_key=secret_key,
).with_bind_ports( # expose webUI on 9001
container=9001,
host=9001,
)
container.start()
# Build environment variables dictionary
minio_host = container.get_container_host_ip()
minio_port = container.get_exposed_port(9000)
minio_endpoint = f"{minio_host}:{minio_port}"
env_vars = {
"MINIO_ENDPOINT": minio_endpoint,
"MINIO_ACCESS_KEY": access_key,
"MINIO_SECRET_KEY": secret_key,
"MINIO_BUCKET": bucket_name,
}
yield env_vars
# Stop container
container.stop()
@pytest.fixture(scope="session", autouse=True)
def set_environment_variables(
minio_container: dict[str, str],
) -> Generator[dict[str, str]]:
"""Set environment variables needed for tests."""
# Build environment variables dictionary
env_vars = {}
env_vars.update(minio_container)
# Set environment variables
for key, value in env_vars.items():
os.environ[key] = value
yield env_vars
# Cleanup
for key in env_vars:
_ = os.environ.pop(key, default=None)
@pytest.fixture(scope="session")
def job_id_list() -> Generator[list[UUID]]:
"""Provide a list of job IDs for testing."""
yield [
UUID("d358cbba-75be-4c38-becd-7abf338ca941"),
UUID("56d20d25-b733-4ed3-880c-732d7257dcaa"),
UUID("8019292f-8353-43d7-a189-076c6630de7b"),
]
@pytest.fixture(scope="session")
def csv_data_file(
job_id_list: list[UUID],
) -> Generator[Path]:
"""Create a CSV data file for testing."""
# Create csv content
csv_data = pd.DataFrame(
{
"job_id": job_id_list,
"distance": [
"close",
"long",
"wrong",
],
"angle": ["high", "low", "wrong"],
"point_of_view": ["frontal", "oblique", "wrong"],
"contact": ["offer", "demand", "wrong"],
}
)
# Create data directory and file path
data_dir = Path(tempfile.mkdtemp())
data_path = data_dir / "annotations.csv"
# Write CSV content to file
csv_data.to_csv(data_path, header=True, index=False)
yield data_path
# Cleanup
shutil.rmtree(data_dir)
@pytest.fixture(scope="session")
def raw_minio_client(
set_environment_variables: dict[str, str],
) -> Generator[Minio]:
"""Provide a MinIO client for testing."""
# Create MinIO client
client = Minio(
endpoint=set_environment_variables["MINIO_ENDPOINT"],
access_key=set_environment_variables["MINIO_ACCESS_KEY"],
secret_key=set_environment_variables["MINIO_SECRET_KEY"],
secure=False,
)
# Create bucket if it doesn't exist
bucket_name = set_environment_variables["MINIO_BUCKET"]
try:
if not client.bucket_exists(bucket_name):
client.make_bucket(bucket_name)
except S3Error as e:
raise RuntimeError(f"Failed to create or access bucket: {e}")
yield client
# No explicit cleanup needed for MinIO client
@pytest.fixture(scope="session")
def image_repository() -> Generator[ImageRepository]:
"""Provide an ImageRepository instance for testing."""
# Instantiate and connect repository
repo = ImageRepository()
repo.connect()
yield repo
# Cleanup
repo.disconnect()
@pytest.fixture(scope="session")
def image() -> Generator[Image.Image]:
"""Provide a sample image for testing."""
img = Image.new("RGB", (10, 10))
yield img
@pytest.fixture(scope="function")
def image_in_minio(
set_environment_variables: dict[str, str],
raw_minio_client: Minio,
image: Image.Image,
job_id_list: list[UUID],
) -> Generator[tuple[UUID, Image.Image]]:
"""Upload a sample image to MinIO and provide its ID for testing."""
# Prepare object name and buffer
bucket_name = set_environment_variables["MINIO_BUCKET"]
image_id = job_id_list[0]
object_name = ImageRepository._object_name(image_id)
buffer = BytesIO()
image.save(buffer, ImageRepository.image_format)
num_bytes = buffer.getbuffer().nbytes
buffer.seek(0)
# Upload image to MinIO
raw_minio_client.put_object(
bucket_name=bucket_name,
object_name=object_name,
data=buffer,
length=num_bytes,
part_size=MinioAdapter.chunk_size,
)
buffer.close()
yield image_id, image
# Cleanup: remove image from MinIO
try:
raw_minio_client.remove_object(
bucket_name=bucket_name,
object_name=object_name,
)
except S3Error as e:
raise RuntimeError(f"Failed to remove object from bucket: {e}")
@pytest.fixture(scope="session")
def annotation_repository() -> Generator[AnnotationRepository]:
"""Provide an AnnotationRepository instance for testing."""
# Instantiate and connect repository
repo = AnnotationRepository()
repo.connect()
yield repo
# Cleanup
repo.disconnect()
@pytest.fixture(scope="session")
def annotation() -> Generator[Annotation]:
"""Provide a sample annotation for testing."""
annotation = Annotation(
distance=DistanceEnum.CLOSE,
angle=AngleEnum.HIGH,
point_of_view=PointOfViewEnum.FRONTAL,
contact=ContactEnum.OFFER,
)
yield annotation
@pytest.fixture(scope="function")
def annotation_in_minio(
set_environment_variables: dict[str, str],
raw_minio_client: Minio,
annotation: Annotation,
job_id_list: list[UUID],
) -> Generator[tuple[UUID, Annotation]]:
"""Upload a sample annotation to MinIO and provide its ID for testing."""
# Prepare object name and buffer
bucket_name = set_environment_variables["MINIO_BUCKET"]
job_id = job_id_list[0]
object_name = AnnotationRepository._object_name(job_id)
data_json = annotation.model_dump(mode="json")
data_bytes = json.dumps(data_json).encode(AnnotationRepository.encoding)
buffer = BytesIO(data_bytes)
num_bytes = buffer.getbuffer().nbytes
buffer.seek(0)
# Upload annotation to MinIO
raw_minio_client.put_object(
bucket_name=bucket_name,
object_name=object_name,
data=buffer,
length=num_bytes,
part_size=MinioAdapter.chunk_size,
)
buffer.close()
yield job_id, annotation
# Cleanup: remove annotation from MinIO
try:
raw_minio_client.remove_object(
bucket_name=bucket_name,
object_name=object_name,
)
except S3Error as e:
raise RuntimeError(f"Failed to remove object from bucket: {e}")
@pytest.fixture(scope="session")
def job_repository() -> Generator[JobRepository]:
"""Provide a JobRepository instance for testing."""
# Instantiate and connect repository
repo = JobRepository()
repo.connect()
yield repo
# Cleanup
repo.disconnect()
@pytest.fixture(scope="session")
def job(
job_id_list: list[UUID],
image: Image.Image,
annotation: Annotation,
) -> Generator[Job]:
"""Provide a sample job for testing."""
job = Job(
job_id=job_id_list[0],
image=image,
annotation=annotation,
)
yield job
@pytest.fixture(scope="function")
def job_in_minio(
job: Job,
image_in_minio: tuple[UUID, Image.Image],
annotation_in_minio: tuple[UUID, Annotation],
) -> Generator[Job]:
"""Provide a sample job with its components already in MinIO for testing."""
# A job consists of an image and an annotation and both must be in MinIO
# before the job can be considered to be in MinIO.
# Ensure the job_id matches those of the image and annotation
image_id, _ = image_in_minio
annotation_id, _ = annotation_in_minio
assert job.job_id == annotation_id
assert job.job_id == image_id
yield job
@@ -0,0 +1,110 @@
"""Integration tests for ImageRepositoryInterface."""
from uuid import UUID
import pytest
from PIL import Image
from data_store.interfaces import ImageRepositoryInterface
def test_get_raises_not_implemented_if_not_overwritten(
job_id_list: list[UUID],
) -> None:
"""Test that calling get on a subclass without get implemented raises NotImplementedError."""
class Incomplete(ImageRepositoryInterface):
"""A class missing the get method."""
def get(self, image_id: UUID) -> Image.Image | None:
return super().get(image_id) # type: ignore
def put(self, image: Image.Image, image_id: UUID) -> None:
return None
def delete(self, image_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.get(job_id_list[0])
def test_put_raises_not_implemented_if_not_overwritten(
image: Image.Image,
job_id_list: list[UUID],
) -> None:
"""Test that calling put on a subclass without put implemented raises NotImplementedError."""
class Incomplete(ImageRepositoryInterface):
"""A class missing the put method."""
def get(self, image_id: UUID) -> Image.Image | None:
return None
def put(self, image: Image.Image, image_id: UUID) -> None:
return super().put(image, image_id) # type: ignore
def delete(self, image_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.put(image, job_id_list[0])
def test_delete_raises_not_implemented_if_not_overwritten(
job_id_list: list[UUID],
) -> None:
"""Test that calling delete on a subclass without delete implemented raises NotImplementedError."""
class Incomplete(ImageRepositoryInterface):
"""A class missing the delete method."""
def get(self, image_id: UUID) -> Image.Image | None:
return None
def put(self, image: Image.Image, image_id: UUID) -> None:
return None
def delete(self, image_id: UUID) -> None:
return super().delete(image_id) # type: ignore
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.delete(job_id_list[0])
def test_list_all_raises_not_implemented_if_not_overwritten() -> None:
"""Test that calling list_all on a subclass without list_all implemented raises NotImplementedError."""
class Incomplete(ImageRepositoryInterface):
"""A class missing the list_all method."""
def get(self, image_id: UUID) -> Image.Image | None:
return None
def put(self, image: Image.Image, image_id: UUID) -> None:
return None
def delete(self, image_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return super().list_all() # type: ignore
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.list_all()
# Allows local debugging by running file as script
if __name__ == "__main__":
pytest.main(["-s", "-v", __file__])
@@ -0,0 +1,95 @@
"""Integration tests for ImageRepository."""
from uuid import UUID
import pytest
from PIL import Image
from data_store.repositories.image_repository import ImageRepository
def same_image(img1: Image.Image, img2: Image.Image) -> bool:
"""Check if two images are the same based on their attributes."""
return (
img1.size == img2.size
and img1.mode == img2.mode
and list(img1.getdata()) == list(img2.getdata())
)
def test_get_image(
image_in_minio: tuple[UUID, Image.Image],
image_repository: ImageRepository,
) -> None:
"""Test retrieving an image from ImageRepository."""
# Arrange
image_id, expected_image = image_in_minio
# Act
retrieved_image = image_repository.get(image_id)
# Assert
assert retrieved_image is not None
assert isinstance(retrieved_image, Image.Image)
assert same_image(retrieved_image, expected_image)
def test_put_image(
image_repository: ImageRepository,
image: Image.Image,
job_id_list: list[UUID],
) -> None:
"""Test storing an image in ImageRepository."""
# Arrange
image_id = job_id_list[0]
assert image_repository.get(image_id) is None
# Act
image_repository.put(image, image_id)
# Assert
received_image = image_repository.get(image_id)
assert received_image is not None
assert isinstance(received_image, Image.Image)
assert received_image.size == image.size
assert received_image.mode == image.mode
assert list(received_image.getdata()) == list(image.getdata())
def test_delete_image(
image_in_minio: tuple[UUID, Image.Image],
image_repository: ImageRepository,
) -> None:
"""Test deleting an image from ImageRepository."""
# Arrange
image_id, _ = image_in_minio
assert image_repository.get(image_id) is not None
# Act
image_repository.delete(image_id)
# Assert
assert image_repository.get(image_id) is None
def test_list_all_images(
image_in_minio: tuple[UUID, Image.Image],
image_repository: ImageRepository,
) -> None:
"""Test listing all images in ImageRepository."""
# Arrange
image_id, _ = image_in_minio
# Act
image_ids = image_repository.list_all()
# Assert
assert isinstance(image_ids, list)
assert all(isinstance(i, UUID) for i in image_ids)
assert image_id in image_ids
def test_list_all_images_empty(
image_repository: ImageRepository,
) -> None:
"""Test listing all images in an empty ImageRepository."""
# Act
image_ids = image_repository.list_all()
# Assert
assert isinstance(image_ids, list)
assert len(image_ids) == 0
# Allows local debugging by running file as script
if __name__ == "__main__":
pytest.main(["-s", "-v", __file__])
@@ -0,0 +1,109 @@
"""Integration tests for the JobRepositoryInterface."""
from uuid import UUID
import pytest
from data_store.interfaces import JobRepositoryInterface
from data_store.dto import Job
def test_get_raises_not_implemented_if_not_overwritten(
job_id_list: list[UUID],
) -> None:
"""Test that calling get on a subclass without get implemented raises NotImplementedError."""
class Incomplete(JobRepositoryInterface):
"""A class missing the get method."""
def get(self, job_id: UUID) -> Job | None:
return super().get(job_id) # type: ignore
def put(self, job: Job) -> None:
return None
def delete(self, job_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.get(job_id_list[0])
def test_put_raises_not_implemented_if_not_overwritten(
job: Job,
) -> None:
"""Test that calling put on a subclass without put implemented raises NotImplementedError."""
class Incomplete(JobRepositoryInterface):
"""A class missing the put method."""
def get(self, job_id: UUID) -> Job | None:
return None
def put(self, job: Job) -> None:
return super().put(job) # type: ignore
def delete(self, job_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.put(job)
def test_delete_raises_not_implemented_if_not_overwritten(
job_id_list: list[UUID],
) -> None:
"""Test that calling delete on a subclass without delete implemented raises NotImplementedError."""
class Incomplete(JobRepositoryInterface):
"""A class missing the delete method."""
def get(self, job_id: UUID) -> Job | None:
return None
def put(self, job: Job) -> None:
return None
def delete(self, job_id: UUID) -> None:
return super().delete(job_id) # type: ignore
def list_all(self) -> list[UUID]:
return []
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.delete(job_id_list[0])
def test_list_all_raises_not_implemented_if_not_overwritten() -> None:
"""Test that calling list_all on a subclass without list_all implemented raises NotImplementedError."""
class Incomplete(JobRepositoryInterface):
"""A class missing the list_all method."""
def get(self, job_id: UUID) -> Job | None:
return None
def put(self, job: Job) -> None:
return None
def delete(self, job_id: UUID) -> None:
return None
def list_all(self) -> list[UUID]:
return super().list_all() # type: ignore
instance = Incomplete()
with pytest.raises(NotImplementedError):
instance.list_all()
# Allows local debugging by running file as script
if __name__ == "__main__":
pytest.main(["-s", "-v", __file__])
+116
View File
@@ -0,0 +1,116 @@
"""Integration tests for JobRepository."""
from uuid import UUID
import pytest
from PIL import Image
from data_store.repositories.job_repository import JobRepository
from data_store.dto import Job
def same_image(img1: Image.Image, img2: Image.Image) -> bool:
"""Check if two images are the same based on their attributes."""
assert img1.size == img2.size
assert img1.mode == img2.mode
assert list(img1.getdata()) == list(img2.getdata())
return True
def same_job(job1: Job, job2: Job) -> bool:
"""Check if two Job instances are the same based on their attributes."""
assert job1.job_id == job2.job_id
assert same_image(job1.image, job2.image)
assert job1.annotation == job2.annotation
return True
def test_get_job(
job_in_minio: Job,
job_repository: JobRepository,
) -> None:
"""Test retrieving a job from JobRepository."""
# Arrange
job_id = job_in_minio.job_id
# Act
retrieved_job = job_repository.get(job_id)
# Assert
assert retrieved_job is not None
assert isinstance(retrieved_job, Job)
assert same_job(retrieved_job, job_in_minio)
def test_get_job_without_image_returns_none(
annotation_in_minio: tuple[UUID, object],
job_repository: JobRepository,
) -> None:
"""Test retrieving a job without an image returns None."""
# Arrange
job_id, _ = annotation_in_minio
# Act
retrieved_job = job_repository.get(job_id)
# Assert
assert retrieved_job is None
def test_get_job_without_annotation_returns_none(
image_in_minio: tuple[UUID, object],
job_repository: JobRepository,
) -> None:
"""Test retrieving a job without an annotation returns None."""
# Arrange
job_id, _ = image_in_minio
# Act
retrieved_job = job_repository.get(job_id)
# Assert
assert retrieved_job is None
def test_put_job(
job_repository: JobRepository,
job: Job,
) -> None:
"""Test storing a job in JobRepository."""
# Arrange
job_id = job.job_id
assert job_repository.get(job_id) is None
# Act
job_repository.put(job)
# Assert
received_job = job_repository.get(job_id)
assert received_job is not None
assert isinstance(received_job, Job)
assert same_job(received_job, job)
def test_delete_job(
job_in_minio: Job,
job_repository: JobRepository,
) -> None:
"""Test deleting a job from JobRepository."""
# Arrange
job_id = job_in_minio.job_id
assert job_repository.get(job_id) is not None
# Act
job_repository.delete(job_id)
# Assert
assert job_repository.get(job_id) is None
def test_list_all_jobs(
job_in_minio: Job,
job_repository: JobRepository,
) -> None:
"""Test listing all job IDs from JobRepository."""
# Arrange
job_id = job_in_minio.job_id
# Act
job_ids = job_repository.list_all()
# Assert
assert isinstance(job_ids, list)
assert all(isinstance(jid, UUID) for jid in job_ids)
assert job_id in job_ids
# Allows local debugging by running file as script
if __name__ == "__main__":
pytest.main(["-s", "-v", __file__])