Skip to content

Commit 80d21ae

Browse files
authored
Tests should only run on empty Redis databases (#748)
1 parent 7b8f7f9 commit 80d21ae

File tree

9 files changed

+228
-97
lines changed

9 files changed

+228
-97
lines changed

tests/redis_config.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from dataclasses import dataclass
5+
from typing import Sequence
6+
7+
from redis import Redis
8+
from redis.exceptions import RedisError
9+
10+
11+
@dataclass(frozen=True)
12+
class RedisConfig:
13+
host: str
14+
port: int
15+
db: int
16+
17+
18+
def _find_empty_databases(host: str, port: int, *, required: int) -> Sequence[int]:
19+
"""
20+
Find a set of empty Redis databases that can be used for tests.
21+
22+
Raises RuntimeError if we cannot connect or if we cannot find the requested
23+
number of empty databases. Sentinel ports are intentionally not probed.
24+
"""
25+
# try:
26+
# redis = Redis(host=host, port=port, db=0)
27+
# except Exception as exc: # pragma: no cover - defensive guard
28+
# raise RuntimeError(f"Refusing to run tests: cannot connect to Redis at {host}:{port}") from exc
29+
30+
empty_databases: list[int] = []
31+
32+
for db in range(16):
33+
try:
34+
if Redis(host=host, port=port, db=db).dbsize() == 0:
35+
empty_databases.append(db)
36+
if len(empty_databases) == required:
37+
break
38+
except RedisError as exc: # pragma: no cover - defensive guard
39+
raise RuntimeError(
40+
f"Refusing to run tests: unable to inspect Redis database {db} on {host}:{port}"
41+
) from exc
42+
43+
if len(empty_databases) < required:
44+
raise RuntimeError(
45+
f"Refusing to run tests: need {required} empty Redis databases on {host}:{port}, "
46+
f"but only found {len(empty_databases)}."
47+
)
48+
49+
return empty_databases
50+
51+
52+
REDIS_HOST = 'localhost'
53+
REDIS_PORT = 6379
54+
databases = _find_empty_databases(REDIS_HOST, REDIS_PORT, required=3)
55+
56+
REDIS_CONFIG_1 = RedisConfig(
57+
host=REDIS_HOST,
58+
port=REDIS_PORT,
59+
db=databases[0],
60+
)
61+
REDIS_CONFIG_2 = RedisConfig(
62+
host=REDIS_HOST,
63+
port=REDIS_PORT,
64+
db=databases[1],
65+
)
66+
REDIS_CONFIG_3 = RedisConfig(
67+
host=REDIS_HOST,
68+
port=REDIS_PORT,
69+
db=databases[2],
70+
)

tests/settings.py

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22

3-
REDIS_HOST = os.environ.get("REDIS_HOST", 'localhost')
3+
from tests.redis_config import REDIS_CONFIG_1, REDIS_CONFIG_2
44

55
SECRET_KEY = 'a'
66

@@ -40,7 +40,7 @@
4040
CACHES = {
4141
'default': {
4242
'BACKEND': 'django_redis.cache.RedisCache',
43-
'LOCATION': "redis://127.0.0.1:6379/0",
43+
'LOCATION': f"redis://{REDIS_CONFIG_1.host}:{REDIS_CONFIG_1.port}/{REDIS_CONFIG_1.db}",
4444
'OPTIONS': {
4545
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
4646
},
@@ -50,10 +50,10 @@
5050
CACHES = {
5151
'default': {
5252
'BACKEND': 'redis_cache.cache.RedisCache',
53-
'LOCATION': f'{REDIS_HOST}:6379',
53+
'LOCATION': f'{REDIS_CONFIG_1.host}:{REDIS_CONFIG_1.port}',
5454
'KEY_PREFIX': 'django-rq-tests',
5555
'OPTIONS': {
56-
'DB': 2,
56+
'DB': REDIS_CONFIG_2.db,
5757
'MAX_ENTRIES': 5000,
5858
},
5959
},
@@ -89,48 +89,48 @@
8989

9090
RQ_QUEUES = {
9191
'default': {
92-
'HOST': REDIS_HOST,
93-
'PORT': 6379,
94-
'DB': 0,
92+
'HOST': REDIS_CONFIG_1.host,
93+
'PORT': REDIS_CONFIG_1.port,
94+
'DB': REDIS_CONFIG_1.db,
9595
'DEFAULT_TIMEOUT': 500,
9696
'DEFAULT_RESULT_TTL': 500,
9797
},
9898
'test': {
99-
'HOST': REDIS_HOST,
100-
'PORT': 1,
101-
'DB': 1,
99+
'HOST': REDIS_CONFIG_2.host,
100+
'PORT': REDIS_CONFIG_2.port,
101+
'DB': REDIS_CONFIG_2.db,
102102
},
103103
'sentinel': {
104-
'SENTINELS': [(REDIS_HOST, 26736), (REDIS_HOST, 26737)],
104+
'SENTINELS': [(REDIS_CONFIG_1.host, 26736), (REDIS_CONFIG_1.host, 26737)],
105105
'MASTER_NAME': 'testmaster',
106-
'DB': 1,
106+
'DB': REDIS_CONFIG_2.db,
107107
'USERNAME': 'redis-user',
108108
'PASSWORD': 'secret',
109109
'SOCKET_TIMEOUT': 10,
110110
'SENTINEL_KWARGS': {},
111111
},
112112
'test1': {
113-
'HOST': REDIS_HOST,
114-
'PORT': 1,
115-
'DB': 1,
113+
'HOST': REDIS_CONFIG_2.host,
114+
'PORT': REDIS_CONFIG_2.port,
115+
'DB': REDIS_CONFIG_2.db,
116116
'DEFAULT_TIMEOUT': 400,
117117
'QUEUE_CLASS': 'tests.fixtures.DummyQueue',
118118
},
119119
'test2': {
120-
'HOST': REDIS_HOST,
121-
'PORT': 1,
122-
'DB': 1,
120+
'HOST': REDIS_CONFIG_2.host,
121+
'PORT': REDIS_CONFIG_2.port,
122+
'DB': REDIS_CONFIG_2.db,
123123
},
124124
'test3': {
125-
'HOST': REDIS_HOST,
126-
'PORT': 6379,
127-
'DB': 1,
125+
'HOST': REDIS_CONFIG_2.host,
126+
'PORT': REDIS_CONFIG_2.port,
127+
'DB': REDIS_CONFIG_2.db,
128128
'DEFAULT_RESULT_TTL': 800,
129129
},
130130
'async': {
131-
'HOST': REDIS_HOST,
132-
'PORT': 6379,
133-
'DB': 1,
131+
'HOST': REDIS_CONFIG_2.host,
132+
'PORT': REDIS_CONFIG_2.port,
133+
'DB': REDIS_CONFIG_2.db,
134134
'ASYNC': False,
135135
},
136136
'url': {
@@ -144,52 +144,52 @@
144144
'URL': 'redis://username:password@host:1234',
145145
},
146146
'django_rq_test': {
147-
'HOST': REDIS_HOST,
148-
'PORT': 6379,
149-
'DB': 0,
147+
'HOST': REDIS_CONFIG_1.host,
148+
'PORT': REDIS_CONFIG_1.port,
149+
'DB': REDIS_CONFIG_1.db,
150150
},
151151
'scheduler_scheduler_active_test': {
152-
'HOST': REDIS_HOST,
153-
'PORT': 6379,
154-
'DB': 0,
152+
'HOST': REDIS_CONFIG_1.host,
153+
'PORT': REDIS_CONFIG_1.port,
154+
'DB': REDIS_CONFIG_1.db,
155155
'ASYNC': False,
156156
},
157157
'scheduler_scheduler_inactive_test': {
158-
'HOST': REDIS_HOST,
159-
'PORT': 6379,
160-
'DB': 0,
158+
'HOST': REDIS_CONFIG_1.host,
159+
'PORT': REDIS_CONFIG_1.port,
160+
'DB': REDIS_CONFIG_1.db,
161161
'ASYNC': False,
162162
},
163163
'worker_scheduler_active_test': {
164-
'HOST': REDIS_HOST,
165-
'PORT': 6379,
166-
'DB': 0,
164+
'HOST': REDIS_CONFIG_1.host,
165+
'PORT': REDIS_CONFIG_1.port,
166+
'DB': REDIS_CONFIG_1.db,
167167
'ASYNC': False,
168168
},
169169
'worker_scheduler_inactive_test': {
170-
'HOST': REDIS_HOST,
171-
'PORT': 6379,
172-
'DB': 0,
170+
'HOST': REDIS_CONFIG_1.host,
171+
'PORT': REDIS_CONFIG_1.port,
172+
'DB': REDIS_CONFIG_1.db,
173173
'ASYNC': False,
174174
},
175175
'django-redis': {
176176
'USE_REDIS_CACHE': 'default',
177177
},
178178
'django_rq_test2': {
179-
'HOST': REDIS_HOST,
180-
'PORT': 6379,
181-
'DB': 0,
179+
'HOST': REDIS_CONFIG_1.host,
180+
'PORT': REDIS_CONFIG_1.port,
181+
'DB': REDIS_CONFIG_1.db,
182182
},
183183
'test_scheduler': {
184-
'HOST': REDIS_HOST,
185-
'PORT': 6379,
186-
'DB': 0,
184+
'HOST': REDIS_CONFIG_1.host,
185+
'PORT': REDIS_CONFIG_1.port,
186+
'DB': REDIS_CONFIG_1.db,
187187
'DEFAULT_TIMEOUT': 400,
188188
},
189189
'test_serializer': {
190-
'HOST': REDIS_HOST,
191-
'PORT': 6379,
192-
'DB': 0,
190+
'HOST': REDIS_CONFIG_1.host,
191+
'PORT': REDIS_CONFIG_1.port,
192+
'DB': REDIS_CONFIG_1.db,
193193
'SERIALIZER': 'rq.serializers.JSONSerializer',
194194
},
195195
}

tests/test_admin_integration.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@
1313
from django_rq import get_queue
1414

1515
from .fixtures import say_hello
16+
from .redis_config import REDIS_CONFIG_1
1617
from .utils import get_queue_index
1718

1819

1920
@override_settings(
2021
RQ={'AUTOCOMMIT': True},
21-
RQ_QUEUES={
22-
'default': {
23-
'HOST': 'localhost',
24-
'PORT': 6379,
25-
'DB': 0,
22+
RQ_QUEUES={
23+
'default': {
24+
'HOST': REDIS_CONFIG_1.host,
25+
'PORT': REDIS_CONFIG_1.port,
26+
'DB': REDIS_CONFIG_1.db,
2627
'DEFAULT_TIMEOUT': 500,
2728
}
2829
},
@@ -108,11 +109,11 @@ def test_url_names_still_work(self):
108109

109110
@override_settings(
110111
RQ={'AUTOCOMMIT': True},
111-
RQ_QUEUES={
112-
'default': {
113-
'HOST': 'localhost',
114-
'PORT': 6379,
115-
'DB': 0,
112+
RQ_QUEUES={
113+
'default': {
114+
'HOST': REDIS_CONFIG_1.host,
115+
'PORT': REDIS_CONFIG_1.port,
116+
'DB': REDIS_CONFIG_1.db,
116117
'DEFAULT_TIMEOUT': 500,
117118
}
118119
},

tests/test_connections.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from django_rq.queues import get_queue
1515
from tests.base import DjangoRQTestCase
1616
from tests.fixtures import access_self
17+
from tests.redis_config import REDIS_CONFIG_1, REDIS_CONFIG_2, REDIS_CONFIG_3
1718

1819
QUEUES = settings.RQ_QUEUES
1920

@@ -92,14 +93,14 @@ def test_sentinel_class_initialized_with_kw_args(self, sentinel_class_mock):
9293

9394
def test_get_unique_connection_configs(self):
9495
connection_params_1 = {
95-
'HOST': 'localhost',
96-
'PORT': 6379,
97-
'DB': 0,
96+
'HOST': REDIS_CONFIG_1.host,
97+
'PORT': REDIS_CONFIG_1.port,
98+
'DB': REDIS_CONFIG_1.db,
9899
}
99100
connection_params_2 = {
100-
'HOST': 'localhost',
101-
'PORT': 6379,
102-
'DB': 1,
101+
'HOST': REDIS_CONFIG_2.host,
102+
'PORT': REDIS_CONFIG_2.port,
103+
'DB': REDIS_CONFIG_2.db,
103104
}
104105
config = {'default': connection_params_1, 'test': connection_params_2}
105106
unique_configs = get_unique_connection_configs(config)
@@ -115,14 +116,14 @@ def test_get_unique_connection_configs(self):
115116

116117
def test_get_unique_connection_configs_with_different_timeout(self):
117118
connection_params_1 = {
118-
'HOST': 'localhost',
119-
'PORT': 6379,
120-
'DB': 0,
119+
'HOST': REDIS_CONFIG_1.host,
120+
'PORT': REDIS_CONFIG_1.port,
121+
'DB': REDIS_CONFIG_1.db,
121122
}
122123
connection_params_2 = {
123-
'HOST': 'localhost',
124-
'PORT': 6379,
125-
'DB': 1,
124+
'HOST': REDIS_CONFIG_2.host,
125+
'PORT': REDIS_CONFIG_2.port,
126+
'DB': REDIS_CONFIG_2.db,
126127
}
127128
queue_params_a = dict(connection_params_1)
128129
queue_params_b = dict(connection_params_2)
@@ -147,19 +148,19 @@ def test_get_unique_connection_configs_deterministic_order(self):
147148
import random
148149

149150
connection_params_1 = {
150-
'HOST': 'localhost',
151-
'PORT': 6379,
152-
'DB': 0,
151+
'HOST': REDIS_CONFIG_1.host,
152+
'PORT': REDIS_CONFIG_1.port,
153+
'DB': REDIS_CONFIG_1.db,
153154
}
154155
connection_params_2 = {
155-
'HOST': 'localhost',
156-
'PORT': 6379,
157-
'DB': 1,
156+
'HOST': REDIS_CONFIG_2.host,
157+
'PORT': REDIS_CONFIG_2.port,
158+
'DB': REDIS_CONFIG_2.db,
158159
}
159160
connection_params_3 = {
160-
'HOST': 'localhost',
161-
'PORT': 6379,
162-
'DB': 2,
161+
'HOST': REDIS_CONFIG_3.host,
162+
'PORT': REDIS_CONFIG_3.port,
163+
'DB': REDIS_CONFIG_3.db,
163164
}
164165

165166
# Test with duplicates: zebra and alpha share connection_params_1

tests/test_cron.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_first_register_initializes_connection(self):
4343

4444
def test_connection_validation(self):
4545
"""Test connection validation for same, compatible, and incompatible queues."""
46-
# Start with test3 queue (localhost:6379, DB=1)
46+
# Start with test3 queue (secondary Redis DB configured for tests)
4747
scheduler = DjangoCronScheduler()
4848

4949
# Same queue multiple times should work
@@ -55,13 +55,12 @@ def test_connection_validation(self):
5555
self.assertEqual(job2.queue_name, "test3")
5656

5757
# Compatible queues (same Redis connection) should work
58-
# Both 'test3' and 'async' use localhost:6379 with DB=1
58+
# Both 'test3' and 'async' share the same Redis connection settings
5959
job3 = scheduler.register(say_hello, "async", interval=180)
6060
self.assertEqual(len(scheduler.get_jobs()), 3)
6161
self.assertEqual(job3.queue_name, "async")
6262

6363
# Queues having different Redis connections should fail
64-
# 'default' uses DB=0 while test3/async use DB=1
6564
with self.assertRaises(ValueError):
6665
scheduler.register(say_hello, "default", interval=240)
6766

@@ -79,7 +78,7 @@ def test_connection_index_property(self):
7978
with self.assertRaises(ValueError):
8079
_ = scheduler.connection_index
8180

82-
# Register a job with 'test3' queue (localhost:6379, DB=1)
81+
# Register a job with 'test3' queue (secondary Redis DB configured for tests)
8382
scheduler.register(say_hello, "test3", interval=60)
8483

8584
# Now connection_index should return a valid index

0 commit comments

Comments
 (0)