Skip to content
Merged
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
7 changes: 6 additions & 1 deletion src/dockerflow/sanic/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,17 @@ async def check_redis_connected(redis):
"""
import aioredis

if aioredis.__version__.startswith("1."):
RedisConnectionError = aioredis.ConnectionClosedError
else:
RedisConnectionError = aioredis.ConnectionError

errors = []

try:
with await redis.conn as r:
result = await r.ping()
except aioredis.ConnectionClosedError as e:
except RedisConnectionError as e:
msg = "Could not connect to redis: {!s}".format(e)
errors.append(Error(msg, id=health.ERROR_CANNOT_CONNECT_REDIS))
except aioredis.RedisError as e:
Expand Down
4 changes: 3 additions & 1 deletion tests/constraints/sanic-20.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Sanic<21
Sanic>=20,<21
aioredis<2
sanic_redis<0.3.0
1 change: 1 addition & 0 deletions tests/constraints/sanic-21.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Sanic>=21,<22
1 change: 1 addition & 0 deletions tests/constraints/sanic-22.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Sanic>=22,<23
7 changes: 7 additions & 0 deletions tests/requirements/sanic-20.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# these are constrained by the files in tests/constraints/*.txt
Copy link
Member Author

Choose a reason for hiding this comment

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

sanic 20.* needs a separate requirements file to remove the sanic-testing requirement.

# to support a triple stack Django/Flask/Sanic
aiohttp
aioredis
Sanic
sanic_redis
uvloop>=0.14.0rc1
5 changes: 3 additions & 2 deletions tests/requirements/sanic.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# these are constrained by the files in tests/constraints/*.txt
# to support a triple stack Django/Flask/Sanic
aiohttp
aioredis<2.0.0
aioredis
Sanic
sanic_redis<0.3.0
sanic_redis
sanic-testing
uvloop>=0.14.0rc1
71 changes: 45 additions & 26 deletions tests/sanic/test_sanic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@
# file, you can obtain one at http://mozilla.org/MPL/2.0/.
import functools
import logging
import socket
import uuid

import aioredis
import pytest
import sanic.testing
import sanic
import sanic_redis.core
from sanic import Sanic, response
from sanic_redis import SanicRedis

from dockerflow import health
from dockerflow.sanic import Dockerflow, checks

if sanic.__version__.startswith("20."):
from sanic.testing import SanicTestClient
else:
from sanic_testing.testing import SanicTestClient


class FakeRedis:
def __init__(self, error=None, **kw):
def __init__(self, *args, error=None, **kw):
self.error = error

def __await__(self):
Expand All @@ -30,15 +35,19 @@ def __enter__(self):
def __exit__(self, *exc_info):
pass

def close(self):
async def close(self):
pass

async def wait_closed(self):
pass

async def ping(self):
if self.error == "connection":
raise aioredis.ConnectionClosedError("fake")
if aioredis.__version__.startswith("1."):
RedisConnectionError = aioredis.ConnectionClosedError
else:
RedisConnectionError = aioredis.ConnectionError
raise RedisConnectionError("fake")
elif self.error == "redis":
raise aioredis.RedisError("fake")
elif self.error == "malformed":
Expand All @@ -47,13 +56,20 @@ async def ping(self):
return b"PONG"


async def fake_redis(**kw):
return FakeRedis(**kw)
class FakeRedis1(FakeRedis):
def close(self):
pass


async def fake_redis(*args, **kw):
if aioredis.__version__.startswith("1."):
return FakeRedis1(*args, **kw)
return FakeRedis(*args, **kw)


@pytest.fixture(scope="function")
def app():
app = Sanic("dockerflow")
app = Sanic(f"dockerflow-{uuid.uuid4().hex}")
Copy link
Member

Choose a reason for hiding this comment

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

nice way to generate unique app IDs for tests!


@app.route("/")
async def root(request):
Expand All @@ -77,30 +93,27 @@ def dockerflow_redis(app):

@pytest.fixture
def test_client(app):
# Create SanicTestClient manually and provide a socket object instead of host
# and port when calling Sanic.run in order to avoid parallel test failures
# caused by Sanic.test_client bindngs to a static port
s = socket.socket()
s.bind((sanic.testing.HOST, 0))
try:
# initialize test_client with socket's port
test_client = sanic.testing.SanicTestClient(app, s.getsockname()[1])
# override app.run to drop host and port in favor of socket
run = app.run
app.run = lambda host, port, **kw: run(sock=s, **kw)
# yield test_client
yield test_client
finally:
s.close()
return SanicTestClient(app)
Copy link
Member

Choose a reason for hiding this comment

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

Love to see code removed! 😍



def test_instantiating(app):
@pytest.mark.skipif(not sanic.__version__.startswith("20."), reason="requires sanic 20")
def test_instantiating_sanic_20(app):
dockerflow = Dockerflow()
assert "dockerflow.heartbeat" not in app.router.routes_names
dockerflow.init_app(app)
assert "dockerflow.heartbeat" in app.router.routes_names


@pytest.mark.skipif(
sanic.__version__.startswith("20."), reason="requires sanic 21 or later"
)
def test_instantiating(app):
Dockerflow()
assert ("__heartbeat__",) not in app.router.routes_all
Dockerflow(app)
assert ("__heartbeat__",) in app.router.routes_all


def test_version_exists(dockerflow, mocker, test_client, version_content):
mocker.patch.object(dockerflow, "_version_callback", return_value=version_content)
_, response = test_client.get("/__version__")
Expand Down Expand Up @@ -178,7 +191,10 @@ async def warning_check2():

def test_redis_check(dockerflow_redis, mocker, test_client):
assert "check_redis_connected" in dockerflow_redis.checks
mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis)
if aioredis.__version__.startswith("1."):
mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis)
else:
mocker.patch.object(sanic_redis.core, "from_url", fake_redis)
_, response = test_client.get("/__heartbeat__")
assert response.status == 200
assert response.json["status"] == "ok"
Expand All @@ -198,7 +214,10 @@ def test_redis_check(dockerflow_redis, mocker, test_client):
def test_redis_check_error(dockerflow_redis, mocker, test_client, error, messages):
assert "check_redis_connected" in dockerflow_redis.checks
fake_redis_error = functools.partial(fake_redis, error=error)
mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis_error)
if aioredis.__version__.startswith("1."):
mocker.patch.object(sanic_redis.core, "create_redis_pool", fake_redis_error)
else:
mocker.patch.object(sanic_redis.core, "from_url", fake_redis_error)
_, response = test_client.get("/__heartbeat__")
assert response.status == 500
assert response.json["status"] == "error"
Expand Down
10 changes: 7 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ envlist =
py{37,38,39,310}-dj32
py{38,39,310}-dj{40}
py{37,38,39,310}-fl{012,10,11,20,21}
py{37,38,39}-s{20}
py{37,38,39}-s20
py{37,38,39,310}-s{21,22}

[gh-actions]
python =
Expand All @@ -26,7 +27,8 @@ deps =
-rtests/requirements/default.txt
dj{32,40}: -rtests/requirements/django.txt
fl{012,10,11,20,21}: -rtests/requirements/flask.txt
s{20}: -rtests/requirements/sanic.txt
s20: -rtests/requirements/sanic-20.txt
s{21,22}: -rtests/requirements/sanic.txt
dj32: -ctests/constraints/django-3.2.txt
dj40: -ctests/constraints/django-4.0.txt
fl012: -ctests/constraints/flask-0.12.txt
Expand All @@ -35,11 +37,13 @@ deps =
fl20: -ctests/constraints/flask-2.0.txt
fl21: -ctests/constraints/flask-2.0.txt
s20: -ctests/constraints/sanic-20.txt
s21: -ctests/constraints/sanic-21.txt
s22: -ctests/constraints/sanic-22.txt
commands =
python --version
dj{32,40}: pytest tests/core/ tests/django --nomigrations {posargs:}
fl{012,10,11,20,21}: pytest tests/core/ tests/flask/ {posargs:}
s{20}: pytest tests/core/ tests/sanic/ {posargs:}
s{20,21,22}: pytest tests/core/ tests/sanic/ {posargs:}

[testenv:py38-docs]
basepython = python3.8
Expand Down