django / channels

Developer-friendly asynchrony for Django
https://channels.readthedocs.io
BSD 3-Clause "New" or "Revised" License
6.08k stars 801 forks source link

How to test send_json() contents when passing close=True #2074

Closed MaZZly closed 7 months ago

MaZZly commented 7 months ago

Hello,

Apologies if this has been asked & answered before, but I didn't manage to find anything that would match. :innocent:

We have an AsyncJsonWebsocketConsumer that in some cases will close the connection with a JSON message:

await self.send_json({"type": "jwt-expired"}, close=True)

from what I can see the underlying code will first send the message, and directly after send a websocket.close command.

In our automated tests, how (if possible) can we check for the actual JSON message containing jwt-expired with WebsocketCommunicator ?

It seems that in our tests we can only receive the last WS message sent (i.e. {"type": "websocket.close"}, not the message that was sent just before it.

carltongibson commented 7 months ago

What's your test code look like?

With WebsocketCommunicator you'd use receive_json_from to check the messages as they come in...

MaZZly commented 7 months ago

Okay, so I managed to figure out when/why this happens...

The scenario is that the connection is closed during establishing of connection in AsyncJsonWebsocketConsumer.connect()

When we open the connection with WebsocketCommunicator.connect(), the first message is interpreted as "all good, we are connected", and then the second/remaining message in the pipeline is the websocket.close-one

communicator = WebsocketCommunicator(application, ws_url, headers)
connected, subprotocol = await communicator.connect()  # This one consumes the "jwt-expired"-message
msg = await communicator.receive_output()
self.assertEqual(msg, {"type": "websocket.close"})

To test that the jwt-expired-message is there during connect we have to manually send the websocket.connect-message:

communicator = WebsocketCommunicator(application, ws_url, headers)
await communicator.send_input({"type": "websocket.connect"})
msg = await communicator.receive_json_from()
self.assertEqual(msg, {"type": "jwt-expired"})
msg = await communicator.receive_output()
self.assertEqual(msg, {"type": "websocket.close"})
self.assertTrue(await communicator.receive_nothing())