Files
python-repositories/tests/integration/redis_adapter_test.py
T
Brian Bjarke Jensen 0e47db238a
Code Quality Pipeline / code-quality (pull_request) Failing after 14s
Test Python Package / test (pull_request) Failing after 9s
fuxed mypy issues
2025-09-14 20:08:30 +02:00

324 lines
9.5 KiB
Python

# pylint: disable=protected-access
# The above line disables pylint's protected member access warnings for this file,
# allowing tests to access RedisAdapter's internal methods as needed for integration testing.
"""Integration tests for the RedisAdapter."""
from typing import Generator
import pytest
import redis
from redis.commands.json.path import Path as RedisPath
from python_repositories.adapters.redis_adapter import RedisAdapter
@pytest.fixture(scope="session")
def data() -> Generator[dict[str, str], None, None]:
"""Provide a sample data dictionary for tests."""
yield {"foo": "bar"}
@pytest.fixture(scope="function")
def data_in_redis(
raw_redis_client: redis.Redis,
data: dict[str, str],
) -> Generator[tuple[str, dict[str, str]], None, None]:
"""Fixture to set up a known value in Redis before each test."""
key = "test_key"
path = RedisPath.root_path()
raw_redis_client.json().set(key, path, data)
yield key, data
# Cleanup
raw_redis_client.delete(key)
@pytest.fixture(scope="module")
def redis_adapter(redis_container: str) -> Generator[RedisAdapter, None, None]:
"""Fixture to provide a connected RedisAdapter instance."""
adapter = RedisAdapter()
adapter.connect()
yield adapter
adapter.disconnect()
@pytest.fixture(scope="function", autouse=True)
def clear_redis(raw_redis_client: redis.Redis) -> None:
"""Fixture to clear all Redis keys before each test."""
# Clear all keys before each test
raw_redis_client.flushall()
def test_should_adhere_to_interface(
redis_container: str
) -> None:
"""Test that the RedisAdapter adheres to the expected interface."""
# Instantiation fails if interface not adhered to
_ = RedisAdapter()
def test_should_have_logger_when_instantiated(
redis_container: str
) -> None:
"""Test that the RedisAdapter has a logger when instantiated."""
adapter = RedisAdapter()
assert hasattr(adapter, "logger")
assert adapter.logger is not None
def test_should_not_be_connected_when_instantiated(
redis_container: str
) -> None:
"""Test that the RedisAdapter is not connected when instantiated."""
adapter = RedisAdapter()
assert adapter._client is None
assert not adapter.is_connected
def test_should_raise_connection_error_when_unable_to_connect(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test that the RedisAdapter raises ConnectionError when unable to connect."""
# Arrange
monkeypatch.setenv("REDIS_URI", "redis://invalid:6379")
adapter = RedisAdapter()
# Act & Assert
with pytest.raises(ConnectionError):
adapter.connect()
assert adapter._client is None
assert not adapter.is_connected
def test_connect_raises_connection_error_when_unable_to_ping(
monkeypatch: pytest.MonkeyPatch
) -> None:
"""Test that the RedisAdapter raises ConnectionError when ping fails."""
# Set an invalid URI
monkeypatch.setenv("REDIS_URI", "redis://invalid:6379")
# Monkeypatch redis.Redis.from_url to return a mock client
class MockRedis:
"""A mock Redis client that simulates a failed ping."""
def ping(self) -> bool:
"""Simulate a failed ping."""
return False # Simulate failed ping
monkeypatch.setattr("redis.Redis.from_url", lambda *a, **kw: MockRedis())
adapter = RedisAdapter()
with pytest.raises(ConnectionError, match="Could not connect to Redis"):
adapter.connect()
def test_should_log_error_on_exception_during_exit(
redis_container: str,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that the RedisAdapter logs an error when an exception occurs during context exit."""
try:
with RedisAdapter() as adapter:
assert adapter.is_connected
raise ValueError("Simulated error")
except ValueError:
pass # Expected
# Assert error was logged
assert "Error while exiting context" in caplog.text
def test_should_have_context_manager(
redis_container: str
) -> None:
"""Test that the RedisAdapter can be used as a context manager."""
with RedisAdapter() as adapter:
assert adapter._client is not None
assert adapter._client is None
def test_should_get_value(
data_in_redis: tuple[str, dict[str, str]],
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter can get a value."""
# Arrange
key, data = data_in_redis
# Act
value = redis_adapter._get(key)
# Assert
assert value == data
def test_should_get_none_for_missing_key(
redis_adapter: RedisAdapter,
) -> None:
"""Test that getting a non-existent key returns None."""
# Act
value = redis_adapter._get("nonexistent_key")
# Assert
assert value is None
def test_should_raise_value_error_on_invalid_get_key(
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter raises ValueError when getting with an invalid key."""
# Arrange
invalid_keys = ["", 123, None]
# Act & Assert
for key in invalid_keys:
with pytest.raises(ValueError):
redis_adapter._get(key) # type: ignore
def test_should_raise_connection_error_on_get_when_not_connected(
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter raises ConnectionError when getting while not connected."""
# Arrange
adapter = RedisAdapter()
# Act & Assert
with pytest.raises(ConnectionError):
adapter._get("some_key")
def test_should_set_value(
data: dict[str, str],
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter can set a value."""
# Arrange
key = "test_key"
# Act
redis_adapter._set(key, data)
# Assert
assert redis_adapter._get(key) == data
def test_should_update_value(
data_in_redis: tuple[str, dict[str, str]],
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter can update an existing value."""
# Arrange
key, _ = data_in_redis
new_data = {"new_key": "new_value"}
# Act
redis_adapter._set(key, new_data)
# Assert
assert redis_adapter._get(key) == new_data
def test_should_raise_value_error_on_invalid_set_key(
redis_adapter: RedisAdapter,
data: dict[str, str],
) -> None:
"""Test that the RedisAdapter raises ValueError when setting with an invalid key."""
# Arrange
invalid_keys = ["", 123, None]
# Act & Assert
for key in invalid_keys:
with pytest.raises(ValueError):
redis_adapter._set(key, data) # type: ignore
def test_should_raise_value_error_on_invalid_set_data(
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter raises ValueError when setting with invalid data."""
# Arrange
key = "test_key"
invalid_data = ["", 123, None, [], {}]
# Act & Assert
for data in invalid_data:
with pytest.raises(ValueError):
redis_adapter._set(key, data) # type: ignore
def test_should_raise_connection_error_on_set_when_not_connected(
redis_adapter: RedisAdapter,
data: dict[str, str],
) -> None:
"""Test that the RedisAdapter raises ConnectionError when setting while not connected."""
# Arrange
adapter = RedisAdapter()
key = "test_key"
# Act & Assert
with pytest.raises(ConnectionError):
adapter._set(key, data)
def test_should_delete_key(
data_in_redis: tuple[str, dict[str, str]],
redis_adapter: RedisAdapter,
) -> None:
"""Test that deleting a key removes it from Redis."""
# Arrange
key, _ = data_in_redis
# Act
redis_adapter._delete(key)
# Assert
assert redis_adapter._get(key) is None
def test_should_raise_value_error_on_invalid_delete_key(
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter raises ValueError when deleting with an invalid key."""
# Arrange
invalid_keys = ["", 123, None]
# Act & Assert
for key in invalid_keys:
with pytest.raises(ValueError):
redis_adapter._delete(key) # type: ignore
def test_should_raise_connection_error_on_delete_when_not_connected(
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter raises ConnectionError when deleting while not connected."""
# Arrange
adapter = RedisAdapter()
# Act & Assert
with pytest.raises(ConnectionError):
adapter._delete("some_key")
def test_should_list_keys(
redis_adapter: RedisAdapter,
) -> None:
"""Test listing keys matching a pattern returns correct keys."""
# Arrange
redis_adapter._set("key1", {"a": 1})
redis_adapter._set("key2", {"b": 2})
# Act
keys = redis_adapter._list_keys("key*")
# Assert
assert set(keys) == {"key1", "key2"}
def test_should_raise_value_error_on_invalid_list_keys_pattern(
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter raises ValueError when listing keys with an invalid pattern."""
# Arrange
invalid_patterns = ["", 123, None]
# Act & Assert
for pattern in invalid_patterns:
with pytest.raises(ValueError):
redis_adapter._list_keys(pattern) # type: ignore
def test_should_raise_connection_error_on_list_keys_when_not_connected(
redis_adapter: RedisAdapter,
) -> None:
"""Test that the RedisAdapter raises ConnectionError when listing keys while not connected."""
# Arrange
adapter = RedisAdapter()
# Act & Assert
with pytest.raises(ConnectionError):
adapter._list_keys("some_pattern")
# allows local debugging by running file as script
if __name__ == "__main__":
pytest.main(["-s", "-v", __file__])