Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds protocol support for redis and rediss #27

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* Add 'redis://' and 'rediss://' protocol support
* Update `ResponseT` type hint
* Allow to control the minimum SSL version
* Add an optional lock_name attribute to LockError.
Expand Down
173 changes: 104 additions & 69 deletions tests/test_asyncio/test_connection_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import valkey.asyncio as valkey
from tests.conftest import skip_if_server_version_lt
from valkey.asyncio.connection import Connection, to_bool
from valkey.utils import SSL_AVAILABLE


from .compat import aclosing, mock
from .conftest import asynccontextmanager
Expand Down Expand Up @@ -302,47 +304,54 @@ def test_repr_contains_db_info_unix(self):
assert expected in repr(pool)


@pytest.mark.parametrize("connection_protocol", ["valkey", "redis"])
class TestConnectionPoolURLParsing:
def test_hostname(self):
pool = valkey.ConnectionPool.from_url("valkey://my.host")
def test_hostname(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(f"{connection_protocol}://my.host")
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "my.host"}

def test_quoted_hostname(self):
pool = valkey.ConnectionPool.from_url("valkey://my %2F host %2B%3D+")
def test_quoted_hostname(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://my %2F host %2B%3D+"
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "my / host +=+"}

def test_port(self):
pool = valkey.ConnectionPool.from_url("valkey://localhost:6380")
def test_port(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(f"{connection_protocol}://localhost:6380")
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "localhost", "port": 6380}

@skip_if_server_version_lt("6.0.0")
def test_username(self):
pool = valkey.ConnectionPool.from_url("valkey://myuser:@localhost")
def test_username(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://myuser:@localhost"
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "localhost", "username": "myuser"}

@skip_if_server_version_lt("6.0.0")
def test_quoted_username(self):
def test_quoted_username(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
"valkey://%2Fmyuser%2F%2B name%3D%24+:@localhost"
f"{connection_protocol}://%2Fmyuser%2F%2B name%3D%24+:@localhost"
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {
"host": "localhost",
"username": "/myuser/+ name=$+",
}

def test_password(self):
pool = valkey.ConnectionPool.from_url("valkey://:mypassword@localhost")
def test_password(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://:mypassword@localhost"
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "localhost", "password": "mypassword"}

def test_quoted_password(self):
def test_quoted_password(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
"valkey://:%2Fmypass%2F%2B word%3D%24+@localhost"
f"{connection_protocol}://:%2Fmypass%2F%2B word%3D%24+@localhost"
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {
Expand All @@ -351,33 +360,41 @@ def test_quoted_password(self):
}

@skip_if_server_version_lt("6.0.0")
def test_username_and_password(self):
pool = valkey.ConnectionPool.from_url("valkey://myuser:mypass@localhost")
def test_username_and_password(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://myuser:mypass@localhost"
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {
"host": "localhost",
"username": "myuser",
"password": "mypass",
}

def test_db_as_argument(self):
pool = valkey.ConnectionPool.from_url("valkey://localhost", db=1)
def test_db_as_argument(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://localhost", db=1
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "localhost", "db": 1}

def test_db_in_path(self):
pool = valkey.ConnectionPool.from_url("valkey://localhost/2", db=1)
def test_db_in_path(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://localhost/2", db=1
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "localhost", "db": 2}

def test_db_in_querystring(self):
pool = valkey.ConnectionPool.from_url("valkey://localhost/2?db=3", db=1)
def test_db_in_querystring(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://localhost/2?db=3", db=1
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "localhost", "db": 3}

def test_extra_typed_querystring_options(self):
def test_extra_typed_querystring_options(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
"valkey://localhost/2?socket_timeout=20&socket_connect_timeout=10"
f"{connection_protocol}://localhost/2?socket_timeout=20&socket_connect_timeout=10"
"&socket_keepalive=&retry_on_timeout=Yes&max_connections=10"
)

Expand All @@ -391,59 +408,65 @@ def test_extra_typed_querystring_options(self):
}
assert pool.max_connections == 10

def test_boolean_parsing(self):
for expected, value in (
(None, None),
(None, ""),
(False, 0),
(False, "0"),
(False, "f"),
(False, "F"),
(False, "False"),
(False, "n"),
(False, "N"),
(False, "No"),
(True, 1),
(True, "1"),
(True, "y"),
(True, "Y"),
(True, "Yes"),
):
assert expected is to_bool(value)

def test_client_name_in_querystring(self):
def test_client_name_in_querystring(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
"valkey://location?client_name=test-client"
f"{connection_protocol}://location?client_name=test-client"
)
assert pool.connection_kwargs["client_name"] == "test-client"

def test_invalid_extra_typed_querystring_options(self):
def test_invalid_extra_typed_querystring_options(self, connection_protocol):
with pytest.raises(ValueError):
valkey.ConnectionPool.from_url(
"valkey://localhost/2?socket_timeout=_&socket_connect_timeout=abc"
f"{connection_protocol}://localhost/2?socket_timeout=_&socket_connect_timeout=abc"
)

def test_extra_querystring_options(self):
pool = valkey.ConnectionPool.from_url("valkey://localhost?a=1&b=2")
def test_extra_querystring_options(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(
f"{connection_protocol}://localhost?a=1&b=2"
)
assert pool.connection_class == valkey.Connection
assert pool.connection_kwargs == {"host": "localhost", "a": "1", "b": "2"}

def test_calling_from_subclass_returns_correct_instance(self):
pool = valkey.BlockingConnectionPool.from_url("valkey://localhost")
def test_calling_from_subclass_returns_correct_instance(self, connection_protocol):
pool = valkey.BlockingConnectionPool.from_url(
f"{connection_protocol}://localhost"
)
assert isinstance(pool, valkey.BlockingConnectionPool)

def test_client_creates_connection_pool(self):
r = valkey.Valkey.from_url("valkey://myhost")
def test_client_creates_connection_pool(self, connection_protocol):
r = valkey.Valkey.from_url(f"{connection_protocol}://myhost")
assert r.connection_pool.connection_class == valkey.Connection
assert r.connection_pool.connection_kwargs == {"host": "myhost"}

def test_invalid_scheme_raises_error(self):
with pytest.raises(ValueError) as cm:
valkey.ConnectionPool.from_url("localhost")
assert str(cm.value) == (
"Valkey URL must specify one of the following schemes "
"(valkey://, valkeys://, unix://)"
)

def test_invalid_scheme_raises_error():
with pytest.raises(ValueError) as cm:
valkey.ConnectionPool.from_url("localhost")
assert str(cm.value) == (
"Valkey URL must specify one of the following schemes "
"(valkey://, valkeys://, redis://, rediss://, unix://)"
)


def test_boolean_parsing():
for expected, value in (
(None, None),
(None, ""),
(False, 0),
(False, "0"),
(False, "f"),
(False, "F"),
(False, "False"),
(False, "n"),
(False, "N"),
(False, "No"),
(True, 1),
(True, "1"),
(True, "y"),
(True, "Y"),
(True, "Yes"),
):
assert expected is to_bool(value)


class TestBlockingConnectionPoolURLParsing:
Expand Down Expand Up @@ -541,32 +564,44 @@ def test_extra_querystring_options(self):
assert pool.connection_kwargs == {"path": "/socket", "a": "1", "b": "2"}


@pytest.mark.skipif(not SSL_AVAILABLE, reason="SSL not installed")
@pytest.mark.parametrize("connection_protocol", ["valkeys", "rediss"])
class TestSSLConnectionURLParsing:
def test_host(self):
pool = valkey.ConnectionPool.from_url("valkeys://my.host")
def test_host(self, connection_protocol):
pool = valkey.ConnectionPool.from_url(f"{connection_protocol}://my.host")
assert pool.connection_class == valkey.SSLConnection
assert pool.connection_kwargs == {"host": "my.host"}

def test_cert_reqs_options(self):
def test_cert_reqs_options(self, connection_protocol):
import ssl

class DummyConnectionPool(valkey.ConnectionPool):
def get_connection(self, *args, **kwargs):
return self.make_connection()

pool = DummyConnectionPool.from_url("valkeys://?ssl_cert_reqs=none")
pool = DummyConnectionPool.from_url(
f"{connection_protocol}://?ssl_cert_reqs=none"
)
assert pool.get_connection("_").cert_reqs == ssl.CERT_NONE

pool = DummyConnectionPool.from_url("valkeys://?ssl_cert_reqs=optional")
pool = DummyConnectionPool.from_url(
f"{connection_protocol}://?ssl_cert_reqs=optional"
)
assert pool.get_connection("_").cert_reqs == ssl.CERT_OPTIONAL

pool = DummyConnectionPool.from_url("valkeys://?ssl_cert_reqs=required")
pool = DummyConnectionPool.from_url(
f"{connection_protocol}://?ssl_cert_reqs=required"
)
assert pool.get_connection("_").cert_reqs == ssl.CERT_REQUIRED

pool = DummyConnectionPool.from_url("valkeys://?ssl_check_hostname=False")
pool = DummyConnectionPool.from_url(
f"{connection_protocol}://?ssl_check_hostname=False"
)
assert pool.get_connection("_").check_hostname is False

pool = DummyConnectionPool.from_url("valkeys://?ssl_check_hostname=True")
pool = DummyConnectionPool.from_url(
f"{connection_protocol}://?ssl_check_hostname=True"
)
assert pool.get_connection("_").check_hostname is True


Expand Down
Loading