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

Don't actually close DB connections during tests #2101

Merged
merged 1 commit into from
Jul 10, 2024
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
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)