142 lines
5.4 KiB
Python
142 lines
5.4 KiB
Python
"""Unit tests for AnnotationRepository class."""
|
|
|
|
import unittest
|
|
from unittest.mock import patch, MagicMock, _patch_dict
|
|
from uuid import UUID
|
|
|
|
from data_store.repositories.annotation_repository import AnnotationRepository
|
|
from data_store.dto import (
|
|
Annotation,
|
|
AngleEnum,
|
|
ColourEnum,
|
|
ContactEnum,
|
|
DepthEnum,
|
|
DistanceEnum,
|
|
LightingEnum,
|
|
PointOfViewEnum,
|
|
)
|
|
|
|
|
|
class TestAnnotationRepository(unittest.TestCase):
|
|
"""Unit tests for AnnotationRepository class."""
|
|
|
|
patcher: _patch_dict
|
|
repo: AnnotationRepository
|
|
job_id: UUID
|
|
bad_job_id: str
|
|
annotation: Annotation
|
|
bad_annotation: dict
|
|
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
"""Set up test case with a mocked MinioAdapter."""
|
|
# Patch environment variables for MinioAdapter
|
|
cls.patcher = patch.dict(
|
|
"os.environ",
|
|
{
|
|
"MINIO_ENDPOINT": "localhost:9000",
|
|
"MINIO_ACCESS_KEY": "minioadmin",
|
|
"MINIO_SECRET_KEY": "minioadmin",
|
|
"MINIO_BUCKET": "test-bucket",
|
|
},
|
|
)
|
|
cls.patcher.start()
|
|
# Prepare AnnotationRepository
|
|
cls.repo = AnnotationRepository()
|
|
cls.repo._get = MagicMock() # type: ignore[method-assign]
|
|
cls.repo._put = MagicMock() # type: ignore[method-assign]
|
|
cls.repo._delete = MagicMock() # type: ignore[method-assign]
|
|
# Prepare other test variables
|
|
cls.job_id = UUID("d87022f8-4169-4d69-8512-bbd65cc1cb23")
|
|
cls.bad_job_id = "not-a-uuid"
|
|
cls.annotation = Annotation(
|
|
angle=AngleEnum.HIGH,
|
|
angle_uncertainty=True,
|
|
colour=ColourEnum.HIGH,
|
|
colour_uncertainty=False,
|
|
contact=ContactEnum.OFFER,
|
|
contact_uncertainty=True,
|
|
depth=DepthEnum.LOW,
|
|
depth_uncertainty=False,
|
|
distance=DistanceEnum.CLOSE,
|
|
distance_uncertainty=True,
|
|
lighting=LightingEnum.MEDIUM,
|
|
lighting_uncertainty=False,
|
|
point_of_view=PointOfViewEnum.FRONTAL,
|
|
point_of_view_uncertainty=True,
|
|
)
|
|
cls.bad_annotation = {"not_an_annotation": "irrelevant value"}
|
|
|
|
@classmethod
|
|
def tearDownClass(cls) -> None:
|
|
# Stop patching
|
|
cls.patcher.stop()
|
|
|
|
def setUp(self) -> None:
|
|
"""Set up each test with a fresh AnnotationRepository instance."""
|
|
self.repo._get.reset_mock() # type: ignore[attr-defined]
|
|
self.repo._put.reset_mock() # type: ignore[attr-defined]
|
|
self.repo._delete.reset_mock() # type: ignore[attr-defined]
|
|
|
|
def test_should_adhere_to_interface(self) -> None:
|
|
"""Test that AnnotationRepository adheres to AnnotationRepositoryInterface."""
|
|
# Instantiation fails if interface not adhered to
|
|
_ = AnnotationRepository()
|
|
|
|
def test_should_have_encoding_set(self) -> None:
|
|
"""Test that AnnotationRepository has encoding when instantiated."""
|
|
self.assertTrue(hasattr(self.repo, "encoding"))
|
|
|
|
def test_should_have_data_format_set(self) -> None:
|
|
"""Test that AnnotationRepository has data_format when instantiated."""
|
|
self.assertTrue(hasattr(self.repo, "data_format"))
|
|
|
|
def test_should_have_folder_name_set(self) -> None:
|
|
"""Test that AnnotationRepository has folder_name when instantiated."""
|
|
self.assertTrue(hasattr(self.repo, "folder_name"))
|
|
|
|
def test_object_name_invalid_job_id_raises_value_error(self) -> None:
|
|
"""Test that _object_name() raises ValueError for invalid job_id."""
|
|
with self.assertRaises(ValueError):
|
|
_ = self.repo._object_name(self.bad_job_id) # type: ignore
|
|
|
|
def test_get_invalid_job_id_raises_value_error(self) -> None:
|
|
"""Test that get() raises ValueError for invalid job_id."""
|
|
with self.assertRaises(ValueError):
|
|
self.repo.get(self.bad_job_id) # type: ignore
|
|
|
|
def test_get_data_that_cannot_be_converted_returns_none(self) -> None:
|
|
"""Test that get() returns None if retrieved data cannot be converted to Annotation."""
|
|
# Arrange
|
|
bad_data = b'{"not":"valid annotation data"}'
|
|
mock_buffer = MagicMock()
|
|
mock_buffer.read.return_value = bad_data
|
|
self.repo._get.return_value = mock_buffer # type: ignore[attr-defined]
|
|
# Act
|
|
result = self.repo.get(self.job_id)
|
|
# Assert
|
|
self.assertIsNone(result)
|
|
self.repo._get.assert_called_once_with( # type: ignore[attr-defined]
|
|
f"{self.repo.folder_name}/{self.job_id}.{self.repo.data_format}"
|
|
)
|
|
|
|
def test_put_invalid_job_id_raises_value_error(self) -> None:
|
|
"""Test that put() raises ValueError for invalid job_id."""
|
|
with self.assertRaises(ValueError):
|
|
self.repo.put(self.annotation, self.bad_job_id) # type: ignore
|
|
|
|
def test_put_invalid_annotation_raises_value_error(self) -> None:
|
|
"""Test that put() raises ValueError for invalid annotation."""
|
|
with self.assertRaises(ValueError):
|
|
self.repo.put(self.bad_annotation, self.job_id) # type: ignore
|
|
|
|
def test_delete_invalid_job_id_raises_value_error(self) -> None:
|
|
"""Test that delete() raises ValueError for invalid job_id."""
|
|
with self.assertRaises(ValueError):
|
|
self.repo.delete(self.bad_job_id) # type: ignore
|
|
|
|
|
|
# Allows local debugging by running file as script
|
|
if __name__ == "__main__":
|
|
unittest.main()
|