From c9defa5a81dc6333ec2f392a8598faeff45c9d04 Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Mon, 16 Mar 2026 18:44:06 -0400 Subject: [PATCH] daphne added to dependencies; still reliant on uvicorn, as the former is now used solely as a channels testing req'ment; new consumer model in apps.epic.consumers to handle _gatekeeper partial functionality, permitting access to room once token costs met; new .routing urlpattern to accomodate; new tests.integrated.test_consumer IT cases ensure this functionality --- requirements.dev.txt | 1 + requirements.txt | 1 + src/apps/epic/consumers.py | 19 +++++++++- src/apps/epic/routing.py | 9 ++++- .../epic/tests/integrated/test_consumers.py | 37 +++++++++++++++++++ 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/apps/epic/tests/integrated/test_consumers.py diff --git a/requirements.dev.txt b/requirements.dev.txt index cf6269b..fa9a2a4 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -7,6 +7,7 @@ channels-redis charset-normalizer==3.4.4 coverage cssselect==1.3.0 +daphne dj-database-url Django==6.0 django-compressor diff --git a/requirements.txt b/requirements.txt index 423d5da..c3451d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ celery channels channels-redis cssselect==1.3.0 +daphne Django==6.0 dj-database-url django-compressor diff --git a/src/apps/epic/consumers.py b/src/apps/epic/consumers.py index 60defaf..bc19fba 100644 --- a/src/apps/epic/consumers.py +++ b/src/apps/epic/consumers.py @@ -1 +1,18 @@ -# RoomConsumer goes here \ No newline at end of file +from channels.generic.websocket import AsyncJsonWebsocketConsumer + + +class RoomConsumer(AsyncJsonWebsocketConsumer): + async def connect(self): + self.room_slug = self.scope["url_route"]["kwargs"]["room_slug"] + self.group_name = f"room_{self.room_slug}" + await self.channel_layer.group_add(self.group_name, self.channel_name) + await self.accept() + + async def disconnect(self, close_code): + await self.channel_layer.group_discard(self.group_name, self.channel_name) + + async def receive_json(self, content): + pass # handlers added as events introduced + + async def gate_update(self, event): + await self.send_json(event) diff --git a/src/apps/epic/routing.py b/src/apps/epic/routing.py index b3600a3..e2d472c 100644 --- a/src/apps/epic/routing.py +++ b/src/apps/epic/routing.py @@ -1 +1,8 @@ -websocket_urlpatterns = [] +from django.urls import path + +from . import consumers + + +websocket_urlpatterns = [ + path('ws/room//', consumers.RoomConsumer.as_asgi()), +] diff --git a/src/apps/epic/tests/integrated/test_consumers.py b/src/apps/epic/tests/integrated/test_consumers.py new file mode 100644 index 0000000..53eda18 --- /dev/null +++ b/src/apps/epic/tests/integrated/test_consumers.py @@ -0,0 +1,37 @@ +from channels.testing.websocket import WebsocketCommunicator +from channels.layers import get_channel_layer +from django.test import SimpleTestCase, override_settings + +from core.asgi import application + + +TEST_CHANNEL_LAYERS = { + "default": { + "BACKEND": "channels.layers.InMemoryChannelLayer", + } +} + + +@override_settings(CHANNEL_LAYERS=TEST_CHANNEL_LAYERS) +class RoomConsumerTest(SimpleTestCase): + async def test_can_connect_and_disconnect(self): + communicator = WebsocketCommunicator(application, "/ws/room/test-room/") + connected, _ = await communicator.connect() + self.assertTrue(connected) + await communicator.disconnect() + + async def test_receives_gate_update_broadcast(self): + communicator = WebsocketCommunicator(application, "/ws/room/test-room/") + await communicator.connect() + + channel_layer = get_channel_layer() + await channel_layer.group_send( + "room_test-room", + {"type": "gate_update", "gate_state": "some_state"}, + ) + + response = await communicator.receive_json_from() + self.assertEqual(response["type"], "gate_update") + self.assertEqual(response["gate_state"], "some_state") + + await communicator.disconnect()