Scope integration fixtures per backend subdirectory.
PR Title Check / check-title (pull_request) Successful in 7s
Test Python Package / unit-tests (pull_request) Successful in 13s
Test Python Package / integration-tests (pull_request) Successful in 23s
Test Python Package / coverage-report (pull_request) Successful in 10s
Code Quality Pipeline / code-quality (pull_request) Successful in 49s
PR Title Check / check-title (pull_request) Successful in 7s
Test Python Package / unit-tests (pull_request) Successful in 13s
Test Python Package / integration-tests (pull_request) Successful in 23s
Test Python Package / coverage-report (pull_request) Successful in 10s
Code Quality Pipeline / code-quality (pull_request) Successful in 49s
Split Redis and MinIO container setup into backend-specific conftest modules so only the containers needed for collected tests are imported and started. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
co-authored by
Cursor
parent
6c66fe4e21
commit
b28ac6e803
@@ -0,0 +1,299 @@
|
||||
"""Integration tests for the RedisAdapter."""
|
||||
|
||||
from collections.abc import Generator
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
import redis
|
||||
from redis.commands.json.path import Path as RedisPath
|
||||
|
||||
from python_repositories.adapters.redis_adapter import RedisAdapter
|
||||
from python_repositories.config import RedisConfig
|
||||
|
||||
pytestmark = [pytest.mark.integration, pytest.mark.needs_redis]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
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
|
||||
|
||||
raw_redis_client.delete(key)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def redis_adapter(redis_config: RedisConfig) -> Generator[RedisAdapter, None, None]:
|
||||
"""Fixture to provide a connected RedisAdapter instance."""
|
||||
adapter = RedisAdapter(config=redis_config)
|
||||
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."""
|
||||
raw_redis_client.flushall()
|
||||
|
||||
|
||||
def test_should_log_info_when_already_connected(
|
||||
redis_adapter: RedisAdapter,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter logs info when connect is called while already connected."""
|
||||
with caplog.at_level(logging.INFO):
|
||||
redis_adapter.connect()
|
||||
assert "Already connected" in caplog.text
|
||||
assert "Redis" in caplog.text
|
||||
|
||||
|
||||
def test_should_raise_connection_error_when_unable_to_connect() -> None:
|
||||
"""Test that the RedisAdapter raises ConnectionError when unable to connect."""
|
||||
adapter = RedisAdapter(config=RedisConfig(uri="redis://invalid:6379"))
|
||||
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."""
|
||||
|
||||
class MockRedis:
|
||||
"""A mock Redis client that simulates a failed ping."""
|
||||
|
||||
def ping(self) -> bool:
|
||||
return False
|
||||
|
||||
monkeypatch.setattr("redis.Redis.from_url", lambda *a, **kw: MockRedis())
|
||||
|
||||
adapter = RedisAdapter(config=RedisConfig(uri="redis://invalid:6379"))
|
||||
with pytest.raises(ConnectionError, match="Could not connect to Redis"):
|
||||
adapter.connect()
|
||||
|
||||
|
||||
def test_should_log_error_on_exception_during_exit(
|
||||
redis_config: RedisConfig,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter logs an error when an exception occurs during context exit."""
|
||||
try:
|
||||
with RedisAdapter(config=redis_config) as adapter:
|
||||
assert adapter.is_connected()
|
||||
raise ValueError("Simulated error")
|
||||
except ValueError:
|
||||
pass
|
||||
assert "Error while exiting context" in caplog.text
|
||||
|
||||
|
||||
def test_should_have_context_manager(redis_config: RedisConfig) -> None:
|
||||
"""Test that the RedisAdapter can be used as a context manager."""
|
||||
with RedisAdapter(config=redis_config) 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."""
|
||||
key, data = data_in_redis
|
||||
value = redis_adapter.get(key)
|
||||
assert value is not None
|
||||
assert value == data
|
||||
|
||||
|
||||
def test_should_get_none_for_missing_key(
|
||||
redis_adapter: RedisAdapter,
|
||||
) -> None:
|
||||
"""Test that getting a non-existent key returns None."""
|
||||
value = redis_adapter.get("nonexistent_key")
|
||||
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."""
|
||||
invalid_keys = ["", 123, None]
|
||||
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_config: RedisConfig,
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter raises ConnectionError when getting while not connected."""
|
||||
adapter = RedisAdapter(config=redis_config)
|
||||
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."""
|
||||
key = "test_key"
|
||||
received_data = redis_adapter.get(key)
|
||||
assert received_data is None
|
||||
redis_adapter.set(key, data)
|
||||
received_data = redis_adapter.get(key)
|
||||
assert received_data is not None
|
||||
assert received_data == 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."""
|
||||
key, _ = data_in_redis
|
||||
new_data = {"new_key": "new_value"}
|
||||
received_data = redis_adapter.get(key)
|
||||
assert received_data is not None
|
||||
assert received_data != new_data
|
||||
redis_adapter.set(key, new_data)
|
||||
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."""
|
||||
invalid_keys = ["", 123, None]
|
||||
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."""
|
||||
key = "test_key"
|
||||
invalid_data = ["", 123, None, [], {}]
|
||||
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_config: RedisConfig,
|
||||
data: dict[str, str],
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter raises ConnectionError when setting while not connected."""
|
||||
adapter = RedisAdapter(config=redis_config)
|
||||
with pytest.raises(ConnectionError):
|
||||
adapter.set("test_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."""
|
||||
key, _ = data_in_redis
|
||||
received_data = redis_adapter.get(key)
|
||||
assert received_data is not None
|
||||
redis_adapter.delete(key)
|
||||
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."""
|
||||
invalid_keys = ["", 123, None]
|
||||
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_config: RedisConfig,
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter raises ConnectionError when deleting while not connected."""
|
||||
adapter = RedisAdapter(config=redis_config)
|
||||
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."""
|
||||
redis_adapter.set("key1", {"a": 1})
|
||||
redis_adapter.set("key2", {"b": 2})
|
||||
keys = redis_adapter.list_keys("key*")
|
||||
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."""
|
||||
invalid_patterns = ["", 123, None]
|
||||
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_config: RedisConfig,
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter raises ConnectionError when listing keys while not connected."""
|
||||
adapter = RedisAdapter(config=redis_config)
|
||||
with pytest.raises(ConnectionError):
|
||||
adapter.list_keys("some_pattern")
|
||||
|
||||
|
||||
def test_should_scan_keys(
|
||||
redis_adapter: RedisAdapter,
|
||||
) -> None:
|
||||
"""Test scanning keys matching a pattern returns correct keys."""
|
||||
redis_adapter.set("key1", {"a": 1})
|
||||
redis_adapter.set("key2", {"b": 2})
|
||||
keys = set(redis_adapter.scan_keys("key*"))
|
||||
assert keys == {"key1", "key2"}
|
||||
|
||||
|
||||
def test_should_raise_value_error_on_invalid_scan_keys_pattern(
|
||||
redis_adapter: RedisAdapter,
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter raises ValueError when scanning keys with an invalid pattern."""
|
||||
invalid_patterns = ["", 123, None]
|
||||
for pattern in invalid_patterns:
|
||||
with pytest.raises(ValueError):
|
||||
list(redis_adapter.scan_keys(pattern)) # type: ignore[arg-type]
|
||||
|
||||
|
||||
def test_should_raise_connection_error_on_scan_keys_when_not_connected(
|
||||
redis_config: RedisConfig,
|
||||
) -> None:
|
||||
"""Test that the RedisAdapter raises ConnectionError when scanning keys while not connected."""
|
||||
adapter = RedisAdapter(config=redis_config)
|
||||
with pytest.raises(ConnectionError):
|
||||
list(adapter.scan_keys("some_pattern"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main(["-s", "-v", __file__])
|
||||
Reference in New Issue
Block a user