Skip to content

Commit 37cc20b

Browse files
committed
Use common parser and server for non-async test_connect.py
1 parent 8868195 commit 37cc20b

File tree

2 files changed

+48
-43
lines changed

2 files changed

+48
-43
lines changed

tests/resp.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,3 +388,25 @@ def parse_chunks(buffers: List[bytes]) -> Tuple[List[Any], bytes]:
388388
except NeedMoreData:
389389
break
390390
return result, parser.get_unparsed()
391+
392+
393+
class RespServer:
394+
"""A simple, dummy, REDIS server for unit tests.
395+
Accepts RESP commands and returns RESP responses.
396+
"""
397+
398+
_CLIENT_NAME = "test-suite-client"
399+
_SUCCESS_RESP = b"+OK" + CRNL
400+
_ERROR_RESP = b"-ERR" + CRNL
401+
_SUPPORTED_CMDS = {f"CLIENT SETNAME {_CLIENT_NAME}": _SUCCESS_RESP}
402+
403+
def command(self, cmd: Any) -> bytes:
404+
"""Process a single command and return the response"""
405+
if not isinstance(cmd, list):
406+
return f"-ERR unknown command {cmd!r}\r\n".encode()
407+
408+
# currently supports only a single command
409+
command = " ".join(cmd)
410+
if command in self._SUPPORTED_CMDS:
411+
return self._SUPPORTED_CMDS[command]
412+
return self._ERROR_RESP

tests/test_connect.py

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import logging
2-
import re
32
import socket
43
import socketserver
54
import ssl
@@ -8,16 +7,13 @@
87
import pytest
98
from redis.connection import Connection, SSLConnection, UnixDomainSocketConnection
109

10+
from . import resp
1111
from .ssl_utils import get_ssl_filename
1212

1313
_logger = logging.getLogger(__name__)
1414

1515

1616
_CLIENT_NAME = "test-suite-client"
17-
_CMD_SEP = b"\r\n"
18-
_SUCCESS_RESP = b"+OK" + _CMD_SEP
19-
_ERROR_RESP = b"-ERR" + _CMD_SEP
20-
_SUPPORTED_CMDS = {f"CLIENT SETNAME {_CLIENT_NAME}": _SUCCESS_RESP}
2117

2218

2319
@pytest.fixture
@@ -148,44 +144,31 @@ def finish(self):
148144
_logger.info("%s disconnected", self.client_address)
149145

150146
def handle(self):
147+
parser = resp.RespParser()
148+
server = resp.RespServer()
151149
buffer = b""
152-
command = None
153-
command_ptr = None
154-
fragment_length = None
155-
while self.server.is_serving() or buffer:
156-
try:
157-
buffer += self.request.recv(1024)
158-
except socket.timeout:
159-
continue
160-
if not buffer:
161-
continue
162-
parts = re.split(_CMD_SEP, buffer)
163-
buffer = parts[-1]
164-
for fragment in parts[:-1]:
165-
fragment = fragment.decode()
166-
_logger.info("Command fragment: %s", fragment)
167-
168-
if fragment.startswith("*") and command is None:
169-
command = [None for _ in range(int(fragment[1:]))]
170-
command_ptr = 0
171-
fragment_length = None
172-
continue
173-
174-
if fragment.startswith("$") and command[command_ptr] is None:
175-
fragment_length = int(fragment[1:])
176-
continue
177-
178-
assert len(fragment) == fragment_length
179-
command[command_ptr] = fragment
180-
command_ptr += 1
181-
182-
if command_ptr < len(command):
150+
try:
151+
# if client performs pipelining, we may need
152+
# to adjust this code to not block when sending
153+
# responses.
154+
while self.server.is_serving():
155+
try:
156+
command = parser.parse(buffer)
157+
buffer = b""
158+
except resp.NeedMoreData:
159+
try:
160+
buffer = self.request.recv(1024)
161+
except socket.timeout:
162+
buffer = b""
163+
continue
164+
if not buffer:
165+
break # EOF
183166
continue
184-
185-
command = " ".join(command)
186167
_logger.info("Command %s", command)
187-
resp = _SUPPORTED_CMDS.get(command, _ERROR_RESP)
188-
_logger.info("Response %s", resp)
189-
self.request.sendall(resp)
190-
command = None
191-
_logger.info("Exit handler")
168+
response = server.command(command)
169+
_logger.info("Response %s", response)
170+
self.request.sendall(response)
171+
except Exception:
172+
_logger.exception("Exception in handler")
173+
finally:
174+
_logger.info("Exit handler")

0 commit comments

Comments
 (0)