Add public repository interfaces and subclassable adapter CRUD API.
Code Quality Pipeline / code-quality (pull_request) Failing after 19s
Test Python Package / test (pull_request) Successful in 50s

Define split JSON and object repository ABCs, promote adapter methods to public CRUD, add example domain repositories, modernize interface stubs, and make the package installable for tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Brian Bjarke Jensen
2026-06-28 10:58:33 +02:00
co-authored by Cursor
parent f4280f15c1
commit e98fd3b90d
21 changed files with 547 additions and 190 deletions
+30 -32
View File
@@ -1,6 +1,3 @@
# pylint: disable=protected-access
# The above line disables pylint's protected member access warnings for this file,
# allowing tests to access MinioAdapter's internal methods as needed for integration testing.
"""Integration tests for the MinioAdapter."""
from collections.abc import Generator
@@ -13,6 +10,7 @@ import os
import logging
from minio import S3Error
from python_repositories.adapters.minio_adapter import MinioAdapter
from python_repositories.interfaces import ObjectRepositoryInterface
def same_data(
@@ -103,7 +101,7 @@ def clear_minio(
def test_should_adhere_to_interface() -> None:
"""Test that the MinioAdapter adheres to the expected interface."""
# Instantiation fails if interface not adhered to
assert issubclass(MinioAdapter, ObjectRepositoryInterface)
_ = MinioAdapter()
@@ -187,7 +185,7 @@ def test_should_get_data(
# Arrange
object_name, expected_data = data_in_minio
# Act
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
# Assert
assert received_data is not None
assert same_data(expected_data, received_data)
@@ -200,7 +198,7 @@ def test_should_get_none_for_nonexistent_object(
# Arrange
object_name = "nonexistent_object"
# Act
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
# Assert
assert received_data is None
@@ -214,7 +212,7 @@ def test_should_raise_value_error_on_invalid_get_object_name(
# Act & Assert
for object_name in invalid_object_names:
with pytest.raises(ValueError):
minio_adapter._get(object_name) # type: ignore
minio_adapter.get(object_name) # type: ignore
def test_should_raise_connection_error_on_get_when_not_connected(
@@ -225,7 +223,7 @@ def test_should_raise_connection_error_on_get_when_not_connected(
adapter = MinioAdapter() # not connected
# Act & Assert
with pytest.raises(ConnectionError):
adapter._get("some_object")
adapter.get("some_object")
def test_should_log_warning_when_getting_nonexistent_object(
@@ -249,7 +247,7 @@ def test_should_log_warning_when_getting_nonexistent_object(
object_name = "missing-object"
# Act
with caplog.at_level("WARNING"):
result = adapter._get(object_name)
result = adapter.get(object_name)
# Assert
assert result is None
assert (
@@ -280,7 +278,7 @@ def test_should_log_error_when_getting_with_s3error_other_than_no_such_key(
object_name = "missing-object"
# Act
with caplog.at_level("ERROR"):
result = adapter._get(object_name)
result = adapter.get(object_name)
# Assert
assert result is None
assert repr(other_s3error) in caplog.text
@@ -299,7 +297,7 @@ def test_should_log_error_when_getting_with_general_exception(
object_name = "missing-object"
# Act
with caplog.at_level("ERROR"):
result = adapter._get(object_name)
result = adapter.get(object_name)
# Assert
assert result is None
assert repr(general_exception) in caplog.text
@@ -312,12 +310,12 @@ def test_should_put_data(
"""Test that the MinioAdapter can put data into a bucket."""
# Arrange
object_name = "new_test_object"
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
assert received_data is None # ensure object does not exist yet
# Act
minio_adapter._put(object_name, data)
minio_adapter.put(object_name, data)
# Assert
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
assert received_data is not None
assert same_data(data, received_data)
# Cleanup
@@ -333,13 +331,13 @@ def test_should_update_data(
# Arrange
object_name, _ = data_in_minio
new_data = BytesIO(random.randbytes(2**21)) # 2 MiB
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
assert received_data is not None
assert not same_data(received_data, new_data)
# Act
minio_adapter._put(object_name, new_data)
minio_adapter.put(object_name, new_data)
# Assert
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
assert received_data is not None
assert same_data(new_data, received_data)
@@ -354,7 +352,7 @@ def test_should_raise_value_error_on_invalid_put_object_name(
# Act & Assert
for object_name in invalid_object_names:
with pytest.raises(ValueError):
minio_adapter._put(object_name, data) # type: ignore
minio_adapter.put(object_name, data) # type: ignore
def test_should_raise_value_error_on_invalid_put_data(
@@ -367,7 +365,7 @@ def test_should_raise_value_error_on_invalid_put_data(
# Act & Assert
for data in invalid_data:
with pytest.raises(ValueError):
minio_adapter._put(object_name, data) # type: ignore
minio_adapter.put(object_name, data) # type: ignore
def test_should_raise_value_error_on_invalid_put_content_type(
@@ -381,7 +379,7 @@ def test_should_raise_value_error_on_invalid_put_content_type(
# Act & Assert
for content_type in invalid_content_types:
with pytest.raises(ValueError):
minio_adapter._put(object_name, data, content_type) # type: ignore
minio_adapter.put(object_name, data, content_type) # type: ignore
def test_should_raise_connection_error_on_put_when_not_connected(
@@ -393,7 +391,7 @@ def test_should_raise_connection_error_on_put_when_not_connected(
adapter = MinioAdapter() # not connected
# Act & Assert
with pytest.raises(ConnectionError):
adapter._put("some_object", data)
adapter.put("some_object", data)
def test_should_delete_object(
@@ -403,12 +401,12 @@ def test_should_delete_object(
"""Test that the MinioAdapter can delete an object from a bucket."""
# Arrange
object_name, _ = data_in_minio
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
assert received_data is not None # ensure object exists
# Act
minio_adapter._delete(object_name)
minio_adapter.delete(object_name)
# Assert
received_data = minio_adapter._get(object_name)
received_data = minio_adapter.get(object_name)
assert received_data is None
@@ -421,7 +419,7 @@ def test_should_raise_value_error_on_invalid_delete_object_name(
# Act & Assert
for object_name in invalid_object_names:
with pytest.raises(ValueError):
minio_adapter._delete(object_name) # type: ignore
minio_adapter.delete(object_name) # type: ignore
def test_should_raise_connection_error_on_delete_when_not_connected(
@@ -432,7 +430,7 @@ def test_should_raise_connection_error_on_delete_when_not_connected(
adapter = MinioAdapter() # not connected
# Act & Assert
with pytest.raises(ConnectionError):
adapter._delete("some_object")
adapter.delete("some_object")
def test_should_list_objects(
@@ -444,9 +442,9 @@ def test_should_list_objects(
object_name, _ = data_in_minio
new_data = BytesIO(random.randbytes(2**21)) # 2 MiB
new_data_name = "another_test_object"
minio_adapter._put(new_data_name, new_data)
minio_adapter.put(new_data_name, new_data)
# Act
objects = minio_adapter._list_objects()
objects = minio_adapter.list_objects()
# Assert
assert isinstance(objects, list)
assert len(objects) == 2
@@ -463,10 +461,10 @@ def test_should_list_objects_with_prefix(
object_name, _ = data_in_minio
new_data = BytesIO(random.randbytes(2**21)) # 2 MiB
new_data_name = "prefix_test_object"
minio_adapter._put(new_data_name, new_data)
minio_adapter.put(new_data_name, new_data)
prefix = "prefix_"
# Act
objects = minio_adapter._list_objects(prefix)
objects = minio_adapter.list_objects(prefix)
# Assert
assert isinstance(objects, list)
assert len(objects) == 1
@@ -483,7 +481,7 @@ def test_should_raise_value_error_on_invalid_list_objects_prefix(
# Act & Assert
for prefix in invalid_prefixes:
with pytest.raises(ValueError):
minio_adapter._list_objects(prefix) # type: ignore
minio_adapter.list_objects(prefix) # type: ignore
def test_should_raise_connection_error_on_list_objects_when_not_connected(
@@ -494,7 +492,7 @@ def test_should_raise_connection_error_on_list_objects_when_not_connected(
adapter = MinioAdapter() # not connected
# Act & Assert
with pytest.raises(ConnectionError):
adapter._list_objects()
adapter.list_objects()
# allows local debugging by running file as script