97 lines
3.9 KiB
Python
97 lines
3.9 KiB
Python
|
|
"""Unit tests for redis_repository.RedisRepository.
|
||
|
|
|
||
|
|
Regression coverage for QA-round-3 B21: user state must live on a dedicated
|
||
|
|
Redis logical DB (default db3) with all keys prefixed by ``wrongmove:user:``.
|
||
|
|
"""
|
||
|
|
from datetime import timedelta
|
||
|
|
from unittest import mock
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
|
||
|
|
def _patched_app() -> mock.MagicMock:
|
||
|
|
"""Return a MagicMock standing in for celery_app.app.broker_connection()."""
|
||
|
|
fake_app = mock.MagicMock()
|
||
|
|
fake_app.broker_connection.return_value.info.return_value = {
|
||
|
|
"hostname": "redis.test",
|
||
|
|
"port": 6379,
|
||
|
|
}
|
||
|
|
return fake_app
|
||
|
|
|
||
|
|
|
||
|
|
class TestRedisRepositoryDbSelection:
|
||
|
|
"""B21 regression: RedisRepository writes to a dedicated DB (default 3)."""
|
||
|
|
|
||
|
|
def test_defaults_to_db_3(self):
|
||
|
|
import redis_repository
|
||
|
|
with mock.patch.dict("os.environ", {}, clear=False) as _env:
|
||
|
|
_env.pop("REDIS_USER_DB", None)
|
||
|
|
with mock.patch.object(redis_repository, "redis") as mock_redis, \
|
||
|
|
mock.patch.object(redis_repository, "app", _patched_app()):
|
||
|
|
redis_repository.RedisRepository()
|
||
|
|
|
||
|
|
kwargs = mock_redis.Redis.call_args.kwargs
|
||
|
|
assert kwargs["db"] == 3
|
||
|
|
assert kwargs["socket_keepalive"] is True
|
||
|
|
assert kwargs["health_check_interval"] == 25
|
||
|
|
|
||
|
|
def test_honours_REDIS_USER_DB_env_var(self):
|
||
|
|
import redis_repository
|
||
|
|
with mock.patch.dict("os.environ", {"REDIS_USER_DB": "7"}):
|
||
|
|
with mock.patch.object(redis_repository, "redis") as mock_redis, \
|
||
|
|
mock.patch.object(redis_repository, "app", _patched_app()):
|
||
|
|
redis_repository.RedisRepository()
|
||
|
|
|
||
|
|
kwargs = mock_redis.Redis.call_args.kwargs
|
||
|
|
assert kwargs["db"] == 7
|
||
|
|
|
||
|
|
|
||
|
|
class TestRedisRepositoryKeyPrefix:
|
||
|
|
"""B21 regression: all keys written via set_key/get_key are namespaced
|
||
|
|
with the ``wrongmove:user:`` prefix to avoid collisions on a shared
|
||
|
|
Redis instance."""
|
||
|
|
|
||
|
|
def _fresh_repo(self) -> tuple[object, mock.MagicMock]:
|
||
|
|
import redis_repository
|
||
|
|
with mock.patch.object(redis_repository, "redis"), \
|
||
|
|
mock.patch.object(redis_repository, "app", _patched_app()):
|
||
|
|
repo = redis_repository.RedisRepository()
|
||
|
|
fake_client = mock.MagicMock()
|
||
|
|
repo.redis_client = fake_client
|
||
|
|
return repo, fake_client
|
||
|
|
|
||
|
|
def test_set_key_prepends_prefix(self):
|
||
|
|
repo, fake_client = self._fresh_repo()
|
||
|
|
repo.set_key("webauthn:challenge:abc", {"x": 1})
|
||
|
|
# First positional arg to .set is the key.
|
||
|
|
set_call_key = fake_client.set.call_args.args[0]
|
||
|
|
assert set_call_key == "wrongmove:user:webauthn:challenge:abc"
|
||
|
|
# expire uses the same prefixed key.
|
||
|
|
expire_call_key = fake_client.expire.call_args.args[0]
|
||
|
|
assert expire_call_key == "wrongmove:user:webauthn:challenge:abc"
|
||
|
|
|
||
|
|
def test_get_key_uses_prefix(self):
|
||
|
|
repo, fake_client = self._fresh_repo()
|
||
|
|
fake_client.get.return_value = '{"hello": "world"}'
|
||
|
|
result = repo.get_key("webauthn:challenge:abc")
|
||
|
|
fake_client.get.assert_called_once_with(
|
||
|
|
"wrongmove:user:webauthn:challenge:abc"
|
||
|
|
)
|
||
|
|
assert result == {"hello": "world"}
|
||
|
|
|
||
|
|
def test_does_not_double_prefix_already_prefixed_key(self):
|
||
|
|
repo, fake_client = self._fresh_repo()
|
||
|
|
repo.set_key("wrongmove:user:already-prefixed", {"x": 1})
|
||
|
|
set_call_key = fake_client.set.call_args.args[0]
|
||
|
|
assert set_call_key == "wrongmove:user:already-prefixed"
|
||
|
|
|
||
|
|
def test_add_task_for_user_uses_prefixed_key(self):
|
||
|
|
repo, fake_client = self._fresh_repo()
|
||
|
|
fake_client.get.return_value = None # no prior tasks
|
||
|
|
from api.auth import User
|
||
|
|
user = User(sub="", email="test@example.com", name="")
|
||
|
|
repo.add_task_for_user(user, "task-xyz")
|
||
|
|
# The redis SET should target the namespaced key.
|
||
|
|
set_call_key = fake_client.set.call_args.args[0]
|
||
|
|
assert set_call_key == "wrongmove:user:test@example.com/tasks"
|