Skip to content

Commit 0406e85

Browse files
Fix PubSub client health check handling for sync client implementation (#3870)
* PubSub client health check handling for sync client implementation * Update redis/client.py Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 0894218 commit 0406e85

File tree

3 files changed

+134
-4
lines changed

3 files changed

+134
-4
lines changed

redis/client.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,10 +1009,22 @@ def is_health_check_response(self, response) -> bool:
10091009
If there are no subscriptions redis responds to PING command with a
10101010
bulk response, instead of a multi-bulk with "pong" and the response.
10111011
"""
1012-
return response in [
1013-
self.health_check_response, # If there was a subscription
1014-
self.health_check_response_b, # If there wasn't
1015-
]
1012+
if self.encoder.decode_responses:
1013+
return (
1014+
response
1015+
in [
1016+
self.health_check_response, # If there is a subscription
1017+
self.HEALTH_CHECK_MESSAGE, # If there are no subscriptions and decode_responses=True
1018+
]
1019+
)
1020+
else:
1021+
return (
1022+
response
1023+
in [
1024+
self.health_check_response, # If there is a subscription
1025+
self.health_check_response_b, # If there isn't a subscription and decode_responses=False
1026+
]
1027+
)
10161028

10171029
def check_health(self) -> None:
10181030
conn = self.connection

tests/test_asyncio/test_pubsub.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,63 @@ async def test_send_pubsub_ping_message(self, r: redis.Redis):
671671
await p.aclose()
672672

673673

674+
@pytest.mark.onlynoncluster
675+
class TestPubSubHealthCheckResponse:
676+
"""Tests for health check response validation with different decode_responses settings"""
677+
678+
async def test_health_check_response_decode_false_list_format(self, r: redis.Redis):
679+
"""Test health_check_response includes list format with decode_responses=False"""
680+
p = r.pubsub()
681+
# List format: [b"pong", b"redis-py-health-check"]
682+
assert [b"pong", b"redis-py-health-check"] in p.health_check_response
683+
await p.aclose()
684+
685+
async def test_health_check_response_decode_false_bytes_format(
686+
self, r: redis.Redis
687+
):
688+
"""Test health_check_response includes bytes format with decode_responses=False"""
689+
p = r.pubsub()
690+
# Bytes format: b"redis-py-health-check"
691+
assert b"redis-py-health-check" in p.health_check_response
692+
await p.aclose()
693+
694+
async def test_health_check_response_decode_true_list_format(self, create_redis):
695+
"""Test health_check_response includes list format with decode_responses=True"""
696+
r = await create_redis(decode_responses=True)
697+
p = r.pubsub()
698+
# List format: ["pong", "redis-py-health-check"]
699+
assert ["pong", "redis-py-health-check"] in p.health_check_response
700+
await p.aclose()
701+
await r.aclose()
702+
703+
async def test_health_check_response_decode_true_string_format(self, create_redis):
704+
"""Test health_check_response includes string format with decode_responses=True"""
705+
r = await create_redis(decode_responses=True)
706+
p = r.pubsub()
707+
# String format: "redis-py-health-check" (THE FIX!)
708+
assert "redis-py-health-check" in p.health_check_response
709+
await p.aclose()
710+
await r.aclose()
711+
712+
async def test_health_check_response_decode_false_excludes_string(
713+
self, r: redis.Redis
714+
):
715+
"""Test health_check_response excludes string format with decode_responses=False"""
716+
p = r.pubsub()
717+
# String format should NOT be in the list when decode_responses=False
718+
assert "redis-py-health-check" not in p.health_check_response
719+
await p.aclose()
720+
721+
async def test_health_check_response_decode_true_excludes_bytes(self, create_redis):
722+
"""Test health_check_response excludes bytes format with decode_responses=True"""
723+
r = await create_redis(decode_responses=True)
724+
p = r.pubsub()
725+
# Bytes format should NOT be in the list when decode_responses=True
726+
assert b"redis-py-health-check" not in p.health_check_response
727+
await p.aclose()
728+
await r.aclose()
729+
730+
674731
@pytest.mark.onlynoncluster
675732
class TestPubSubConnectionKilled:
676733
@skip_if_server_version_lt("3.0.0")

tests/test_pubsub.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,67 @@ def test_send_pubsub_ping_message(self, r):
892892
)
893893

894894

895+
@pytest.mark.onlynoncluster
896+
class TestPubSubHealthCheckResponse:
897+
"""Tests for health check response validation with different decode_responses settings"""
898+
899+
def test_is_health_check_response_decode_false_list_format(self, r):
900+
"""Test is_health_check_response recognizes list format with decode_responses=False"""
901+
p = r.pubsub()
902+
# List format: [b"pong", b"redis-py-health-check"]
903+
assert p.is_health_check_response([b"pong", b"redis-py-health-check"])
904+
905+
def test_is_health_check_response_decode_false_bytes_format(self, r):
906+
"""Test is_health_check_response recognizes bytes format with decode_responses=False"""
907+
p = r.pubsub()
908+
# Bytes format: b"redis-py-health-check"
909+
assert p.is_health_check_response(b"redis-py-health-check")
910+
911+
def test_is_health_check_response_decode_false_rejects_string(self, r):
912+
"""Test is_health_check_response rejects string format with decode_responses=False"""
913+
p = r.pubsub()
914+
# String format should NOT be recognized when decode_responses=False
915+
assert not p.is_health_check_response("redis-py-health-check")
916+
917+
def test_is_health_check_response_decode_true_list_format(self, request):
918+
"""Test is_health_check_response recognizes list format with decode_responses=True"""
919+
r = _get_client(redis.Redis, request, decode_responses=True)
920+
p = r.pubsub()
921+
# List format: ["pong", "redis-py-health-check"]
922+
assert p.is_health_check_response(["pong", "redis-py-health-check"])
923+
924+
def test_is_health_check_response_decode_true_string_format(self, request):
925+
"""Test is_health_check_response recognizes string format with decode_responses=True"""
926+
r = _get_client(redis.Redis, request, decode_responses=True)
927+
p = r.pubsub()
928+
# String format: "redis-py-health-check" (THE FIX!)
929+
assert p.is_health_check_response("redis-py-health-check")
930+
931+
def test_is_health_check_response_decode_true_rejects_bytes(self, request):
932+
"""Test is_health_check_response rejects bytes format with decode_responses=True"""
933+
r = _get_client(redis.Redis, request, decode_responses=True)
934+
p = r.pubsub()
935+
# Bytes format should NOT be recognized when decode_responses=True
936+
assert not p.is_health_check_response(b"redis-py-health-check")
937+
938+
def test_is_health_check_response_decode_true_rejects_invalid(self, request):
939+
"""Test is_health_check_response rejects invalid responses with decode_responses=True"""
940+
r = _get_client(redis.Redis, request, decode_responses=True)
941+
p = r.pubsub()
942+
# Invalid responses should be rejected
943+
assert not p.is_health_check_response("invalid-response")
944+
assert not p.is_health_check_response(["pong", "invalid-response"])
945+
assert not p.is_health_check_response(None)
946+
947+
def test_is_health_check_response_decode_false_rejects_invalid(self, r):
948+
"""Test is_health_check_response rejects invalid responses with decode_responses=False"""
949+
p = r.pubsub()
950+
# Invalid responses should be rejected
951+
assert not p.is_health_check_response(b"invalid-response")
952+
assert not p.is_health_check_response([b"pong", b"invalid-response"])
953+
assert not p.is_health_check_response(None)
954+
955+
895956
@pytest.mark.onlynoncluster
896957
class TestPubSubConnectionKilled:
897958
@skip_if_server_version_lt("3.0.0")

0 commit comments

Comments
 (0)