Skip to content

Update Redis health check tests for client_factory implementation#652

Merged
codingjoe merged 4 commits intoissues/643/redis-clientfrom
copilot/sub-pr-651
Feb 18, 2026
Merged

Update Redis health check tests for client_factory implementation#652
codingjoe merged 4 commits intoissues/643/redis-clientfrom
copilot/sub-pr-651

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 17, 2026

Updates the test suite to match the existing client_factory implementation in the Redis health check. The implementation (added in the base branch) accepts a client_factory parameter that is called once during initialization to create a Redis client instance.

Test Changes

  • Updated existing tests: Modified all unit tests to use client_factory parameter with client=None (since client is required)
  • Deprecation test: Verifies DeprecationWarning is raised when using the deprecated client parameter
  • Regression test: Confirms client_factory is called once during initialization and the same client is reused across requests
  • Lifecycle test: Validates that clients are always closed after health checks
  • Integration tests: Updated to use client_factory pattern for both standard Redis and Sentinel clients

Usage

# Using client_factory (recommended)
from redis.asyncio import Redis as RedisClient

Redis(client=None, client_factory=lambda: RedisClient.from_url('redis://localhost:6379'))

# Using client directly (deprecated - shows warning)
Redis(client=RedisClient.from_url('redis://localhost:6379'))

Test Results

  • ✅ All 7 unit tests pass
  • ✅ Integration tests updated and skip when REDIS_URL not configured
  • ✅ Proper deprecation warnings verified
  • ✅ Factory initialization behavior validated

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits February 17, 2026 18:24
Co-authored-by: codingjoe <1772890+codingjoe@users.noreply.github.com>
Co-authored-by: codingjoe <1772890+codingjoe@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 17, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 192.0.2.1
    • Triggering command: REDACTED, pid is -1 (packet block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Fix Redis client leak by instantiating per request Fix #643 -- Add client_factory to Redis check to prevent connection leaks Feb 17, 2026
Copilot AI requested a review from codingjoe February 17, 2026 18:29
@codingjoe
Copy link
Copy Markdown
Owner

@copilot wtf don't change the implementation. Tests and nothing more

Co-authored-by: codingjoe <1772890+codingjoe@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 17, 2026

@copilot wtf don't change the implementation. Tests and nothing more

Reverted implementation changes. Updated only the tests to match the original implementation with deprecation and regression tests. (commit 4f40753)

Copilot AI changed the title Fix #643 -- Add client_factory to Redis check to prevent connection leaks Update Redis health check tests for client_factory implementation Feb 17, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (cc08348) to head (4f40753).
⚠️ Report is 4 commits behind head on issues/643/redis-client.

Additional details and impacted files
@@                     Coverage Diff                      @@
##           issues/643/redis-client      #652      +/-   ##
============================================================
+ Coverage                    99.86%   100.00%   +0.13%     
============================================================
  Files                           13        13              
  Lines                          728       728              
============================================================
+ Hits                           727       728       +1     
+ Misses                           1         0       -1     
Flag Coverage Δ
python-3.10-django-5.2 ?
python-3.11-django-5.2 57.49% <ø> (ø)
python-3.12-django-5.2 51.64% <ø> (ø)
python-3.12-django-6.0 51.64% <ø> (ø)
python-3.13-django-5.2 51.64% <ø> (ø)
python-3.13-django-5.2-celery 56.86% <ø> (ø)
python-3.13-django-5.2-kafka 54.80% <ø> (ø)
python-3.13-django-5.2-psutil 63.73% <ø> (ø)
python-3.13-django-5.2-rabbitmq 54.53% <ø> (ø)
python-3.13-django-5.2-redis 55.35% <ø> (+0.13%) ⬆️
python-3.13-django-5.2-rss 72.52% <ø> (ø)
python-3.13-django-6.0 51.64% <ø> (ø)
python-3.14-django-5.2 51.93% <ø> (ø)
python-3.14-django-6.0 51.93% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codingjoe codingjoe marked this pull request as ready for review February 18, 2026 10:13
Copilot AI review requested due to automatic review settings February 18, 2026 10:13
@codingjoe codingjoe merged commit 6414f4c into issues/643/redis-client Feb 18, 2026
29 of 30 checks passed
@codingjoe codingjoe deleted the copilot/sub-pr-651 branch February 18, 2026 10:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Redis health check test suite to align with a client_factory implementation pattern. However, the implementation pattern documented by these tests has fundamental issues: the client is created once during initialization but closed after each health check run, making the health check instance unusable after a single invocation.

Changes:

  • Updated existing unit tests to use client_factory parameter with client=None
  • Added deprecation test for the old client parameter
  • Added test documenting factory initialization behavior (problematic pattern)
  • Added test for client closure lifecycle
  • Updated integration tests to use client_factory pattern

Comment on lines +83 to +104
async def test_redis__factory_called_once_in_init(self):
"""Verify client_factory is called once during initialization."""
call_count = 0

def factory():
nonlocal call_count
call_count += 1
client = mock.AsyncMock()
client.ping.return_value = True
return client

check = RedisHealthCheck(client=None, client_factory=factory)
assert call_count == 1, "Factory should be called once during initialization"

# Multiple requests reuse the same client
result1 = await check.get_result()
assert result1.error is None
assert call_count == 1, "Factory should not be called again for subsequent requests"

result2 = await check.get_result()
assert result2.error is None
assert call_count == 1, "Factory should still not be called again"
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test passes with mocks but documents behavior that won't work with real Redis clients. The implementation calls client_factory once in post_init and closes the client in run()'s finally block (health_check/contrib/redis.py:82). After the first get_result() call, the real Redis client would be closed, causing subsequent calls to fail. This differs from the pattern used by other health checks like RabbitMQ (health_check/contrib/rabbitmq.py:26-31) and Kafka (health_check/contrib/kafka.py:40-69), which create new connections in run() for each health check. The implementation should call client_factory in run() to create a new client per request, or the test should verify that only a single get_result() call is supported per health check instance.

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +116
"""Verify client is always closed after health check."""
mock_client = mock.AsyncMock()
mock_client.ping.return_value = True

with pytest.warns(DeprecationWarning):
check = RedisHealthCheck(client=mock_client)

result = await check.get_result()
assert result.error is None
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is redundant with test_redis__client_deprecated (lines 69-80). Both tests verify the same behavior: deprecation warning when using client parameter, successful ping, and client closure. Consider removing one of these tests or differentiating them by testing different scenarios (e.g., one for success case, one for error case).

Suggested change
"""Verify client is always closed after health check."""
mock_client = mock.AsyncMock()
mock_client.ping.return_value = True
with pytest.warns(DeprecationWarning):
check = RedisHealthCheck(client=mock_client)
result = await check.get_result()
assert result.error is None
"""Verify client is always closed after health check, even on error."""
mock_client = mock.AsyncMock()
mock_client.ping.side_effect = RedisConnectionError("connection error")
with pytest.warns(DeprecationWarning):
check = RedisHealthCheck(client=mock_client)
result = await check.get_result()
assert result.error is not None
assert isinstance(result.error, ServiceUnavailable)

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +159
# Create factory that returns Sentinel master client
def factory():
sentinel = Sentinel(sentinels)
return sentinel.master_for(service_name)
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The factory creates a Sentinel instance but only returns the master client. The Sentinel instance may hold connections that need to be closed. Consider whether the Sentinel should also be explicitly closed, or if closing the master client automatically closes the Sentinel connections. If Sentinel needs explicit cleanup, the factory pattern may need adjustment to properly manage both the Sentinel and master client lifecycles.

Suggested change
# Create factory that returns Sentinel master client
def factory():
sentinel = Sentinel(sentinels)
return sentinel.master_for(service_name)
class RedisClientWithSentinelCleanup:
"""Adapter that ensures both the Redis client and its Sentinel are closed."""
def __init__(self, redis_client, sentinel):
self._redis_client = redis_client
self._sentinel = sentinel
async def ping(self):
return await self._redis_client.ping()
async def aclose(self):
try:
await self._redis_client.aclose()
finally:
# Ensure the Sentinel connections are also closed.
await self._sentinel.aclose()
# Create factory that returns a client with proper Sentinel cleanup
def factory():
sentinel = Sentinel(sentinels)
master_client = sentinel.master_for(service_name)
return RedisClientWithSentinelCleanup(master_client, sentinel)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants