Skip to content

Commit

Permalink
Don't actually close DB connections during tests (#2101)
Browse files Browse the repository at this point in the history
  • Loading branch information
bigfootjon authored Jul 10, 2024
1 parent 5d8ddd9 commit aa91c28
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 7 deletions.
3 changes: 1 addition & 2 deletions channels/testing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from asgiref.testing import ApplicationCommunicator # noqa

from .application import ApplicationCommunicator # noqa
from .http import HttpCommunicator # noqa
from .live import ChannelsLiveServerTestCase # noqa
from .websocket import WebsocketCommunicator # noqa
Expand Down
17 changes: 17 additions & 0 deletions channels/testing/application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from unittest import mock

from asgiref.testing import ApplicationCommunicator as BaseApplicationCommunicator


def no_op():
pass


class ApplicationCommunicator(BaseApplicationCommunicator):
async def send_input(self, message):
with mock.patch("channels.db.close_old_connections", no_op):
return await super().send_input(message)

async def receive_output(self, timeout=1):
with mock.patch("channels.db.close_old_connections", no_op):
return await super().receive_output(timeout)
2 changes: 1 addition & 1 deletion channels/testing/http.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from urllib.parse import unquote, urlparse

from asgiref.testing import ApplicationCommunicator
from channels.testing.application import ApplicationCommunicator


class HttpCommunicator(ApplicationCommunicator):
Expand Down
2 changes: 1 addition & 1 deletion channels/testing/websocket.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
from urllib.parse import unquote, urlparse

from asgiref.testing import ApplicationCommunicator
from channels.testing.application import ApplicationCommunicator


class WebsocketCommunicator(ApplicationCommunicator):
Expand Down
4 changes: 2 additions & 2 deletions docs/topics/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ you might need to fall back to it if you are testing things like HTTP chunked
responses or long-polling, which aren't supported in ``HttpCommunicator`` yet.

.. note::
``ApplicationCommunicator`` is actually provided by the base ``asgiref``
package, but we let you import it from ``channels.testing`` for convenience.
``ApplicationCommunicator`` extends the class provided by the base ``asgiref``
package. Channels adds support for running unit tests with async consumers.

To construct it, pass it an application and a scope:

Expand Down
9 changes: 8 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@

def pytest_configure():
settings.configure(
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3"}},
DATABASES={
"default": {
"ENGINE": "django.db.backends.sqlite3",
# Override Django’s default behaviour of using an in-memory database
# in tests for SQLite, since that avoids connection.close() working.
"TEST": {"NAME": "test_db.sqlite3"},
}
},
INSTALLED_APPS=[
"django.contrib.auth",
"django.contrib.contenttypes",
Expand Down
55 changes: 55 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from django import db
from django.test import TestCase

from channels.db import database_sync_to_async
from channels.generic.http import AsyncHttpConsumer
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.testing import HttpCommunicator, WebsocketCommunicator


@database_sync_to_async
def basic_query():
with db.connections["default"].cursor() as cursor:
cursor.execute("SELECT 1234")
return cursor.fetchone()[0]


class WebsocketConsumer(AsyncWebsocketConsumer):
async def connect(self):
await basic_query()
await self.accept("fun")


class HttpConsumer(AsyncHttpConsumer):
async def handle(self, body):
await basic_query()
await self.send_response(
200,
b"",
headers={b"Content-Type": b"text/plain"},
)


class ConnectionClosingTests(TestCase):
async def test_websocket(self):
self.assertNotRegex(
db.connections["default"].settings_dict.get("NAME"),
"memorydb",
"This bug only occurs when the database is materialized on disk",
)
communicator = WebsocketCommunicator(WebsocketConsumer.as_asgi(), "/")
connected, subprotocol = await communicator.connect()
self.assertTrue(connected)
self.assertEqual(subprotocol, "fun")

async def test_http(self):
self.assertNotRegex(
db.connections["default"].settings_dict.get("NAME"),
"memorydb",
"This bug only occurs when the database is materialized on disk",
)
communicator = HttpCommunicator(
HttpConsumer.as_asgi(), method="GET", path="/test/"
)
connected = await communicator.get_response()
self.assertTrue(connected)

0 comments on commit aa91c28

Please sign in to comment.