def test_too_many_initialisation_requests(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json(ConnectionInitMessage().as_dict()) data = ws.receive() assert data["type"] == "websocket.close" assert data["code"] == 4429
def test_subscription_syntax_error(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload(query="subscription { INVALID_SYNTAX "), ).as_dict() ) response = ws.receive_json() assert response["type"] == ErrorMessage.type assert response["id"] == "sub1" assert len(response["payload"]) == 1 assert response["payload"][0]["path"] is None assert response["payload"][0]["locations"] == [{"line": 1, "column": 31}] assert ( response["payload"][0]["message"] == "Syntax Error: Expected Name, found <EOF>." ) ws.close()
def test_simple_subscription(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi") }' ), ).as_dict() ) response = ws.receive_json() assert ( response == NextMessage(id="sub1", payload={"data": {"echo": "Hi"}}).as_dict() ) ws.send_json(CompleteMessage(id="sub1").as_dict()) ws.close()
def test_duplicated_operation_ids(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi", delay: 5) }' ), ).as_dict() ) ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi", delay: 5) }' ), ).as_dict() ) data = ws.receive() assert data["type"] == "websocket.close" assert data["code"] == 4409
def test_server_sent_ping(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload(query="subscription { requestPing }"), ).as_dict() ) response = ws.receive_json() assert response == PingMessage().as_dict() ws.send_json(PongMessage().as_dict()) response = ws.receive_json() assert ( response == NextMessage(id="sub1", payload={"data": {"requestPing": True}}).as_dict() ) response = ws.receive_json() assert response == CompleteMessage(id="sub1").as_dict() ws.close()
def test_subscription_field_errors(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query="subscription { notASubscriptionField }", ), ).as_dict() ) response = ws.receive_json() assert response["type"] == ErrorMessage.type assert response["id"] == "sub1" assert len(response["payload"]) == 1 assert response["payload"][0]["path"] is None assert response["payload"][0]["locations"] == [{"line": 1, "column": 16}] assert ( response["payload"][0]["message"] == "The subscription field 'notASubscriptionField' is not defined." ) ws.close()
async def handle_message(self, message: dict): try: message_type = message.pop("type") if message_type == ConnectionInitMessage.type: await self.handle_connection_init( ConnectionInitMessage(**message)) elif message_type == PingMessage.type: await self.handle_ping(PingMessage(**message)) elif message_type == PongMessage.type: await self.handle_pong(PongMessage(**message)) elif message_type == SubscribeMessage.type: payload = SubscribeMessagePayload(**message.pop("payload")) await self.handle_subscribe( SubscribeMessage(payload=payload, **message)) elif message_type == CompleteMessage.type: await self.handle_complete(CompleteMessage(**message)) else: error_message = f"Unknown message type: {message_type}" await self.handle_invalid_message(error_message) except (KeyError, TypeError): error_message = "Failed to parse message" await self.handle_invalid_message(error_message)
async def test_subscription_exceptions(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { exception(message: "TEST EXC") }', ), ).as_dict()) response = await ws.receive_json() assert response["type"] == ErrorMessage.type assert response["id"] == "sub1" assert len(response["payload"]) == 1 assert response["payload"][0]["path"] is None assert response["payload"][0]["locations"] is None assert response["payload"][0]["message"] == "TEST EXC" await ws.close() assert ws.closed
async def test_subscription_field_errors(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query="subscription { notASubscriptionField }", ), ).as_dict()) response = await ws.receive_json() assert response["type"] == ErrorMessage.type assert response["id"] == "sub1" assert len(response["payload"]) == 1 assert response["payload"][0]["path"] is None assert response["payload"][0]["locations"] == [{ "line": 1, "column": 16 }] assert ( response["payload"][0]["message"] == "The subscription field 'notASubscriptionField' is not defined.") await ws.close() assert ws.closed
async def test_simple_subscription(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi") }'), ).as_dict()) response = await ws.receive_json() assert (response == NextMessage(id="sub1", payload={ "data": { "echo": "Hi" } }).as_dict()) await ws.send_json(CompleteMessage(id="sub1").as_dict()) await ws.close() assert ws.closed
async def test_subscription_syntax_error(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query="subscription { INVALID_SYNTAX "), ).as_dict()) response = await ws.receive_json() assert response["type"] == ErrorMessage.type assert response["id"] == "sub1" assert len(response["payload"]) == 1 assert response["payload"][0]["path"] is None assert response["payload"][0]["locations"] == [{ "line": 1, "column": 31 }] assert (response["payload"][0]["message"] == "Syntax Error: Expected Name, found <EOF>.") await ws.close() assert ws.closed
async def test_connection_init_timeout_cancellation(test_client): app = create_app(connection_init_wait_timeout=timedelta(milliseconds=500)) test_client = TestClient(app) with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() await asyncio.sleep(1) ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query="subscription { debug { isConnectionInitTimeoutTaskDone } }" ), ).as_dict() ) response = ws.receive_json() assert ( response == NextMessage( id="sub1", payload={"data": {"debug": {"isConnectionInitTimeoutTaskDone": True}}}, ).as_dict() ) ws.close()
async def test_duplicated_operation_ids(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi", delay: 5) }'), ).as_dict()) await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi", delay: 5) }'), ).as_dict()) data = await ws.receive(timeout=2) assert ws.closed assert ws.close_code == 4409 assert data.extra == "Subscriber for sub1 already exists"
def test_ws_messages_must_be_text(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_bytes(json.dumps(ConnectionInitMessage().as_dict()).encode()) data = ws.receive() assert data["type"] == "websocket.close" assert data["code"] == 4400
def test_subscription_exceptions(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { exception(message: "TEST EXC") }', ), ).as_dict() ) response = ws.receive_json() assert response["type"] == ErrorMessage.type assert response["id"] == "sub1" assert len(response["payload"]) == 1 assert response["payload"][0]["path"] is None assert response["payload"][0]["locations"] is None assert response["payload"][0]["message"] == "TEST EXC" ws.close()
def test_subscription_cancellation(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi", delay: 99) }' ), ).as_dict() ) ws.send_json( SubscribeMessage( id="sub2", payload=SubscribeMessagePayload( query="subscription { debug { numActiveResultHandlers } }", ), ).as_dict() ) response = ws.receive_json() assert ( response == NextMessage( id="sub2", payload={"data": {"debug": {"numActiveResultHandlers": 2}}} ).as_dict() ) response = ws.receive_json() assert response == CompleteMessage(id="sub2").as_dict() ws.send_json(CompleteMessage(id="sub1").as_dict()) ws.send_json( SubscribeMessage( id="sub3", payload=SubscribeMessagePayload( query="subscription { debug { numActiveResultHandlers } }", ), ).as_dict() ) response = ws.receive_json() assert ( response == NextMessage( id="sub3", payload={"data": {"debug": {"numActiveResultHandlers": 1}}} ).as_dict() ) response = ws.receive_json() assert response == CompleteMessage(id="sub3").as_dict() ws.close()
async def test_too_many_initialisation_requests(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json(ConnectionInitMessage().as_dict()) data = await ws.receive(timeout=2) assert ws.closed assert ws.close_code == 4429 assert data.extra == "Too many initialisation requests"
async def test_ws_messages_must_be_text(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_bytes( json.dumps(ConnectionInitMessage().as_dict()).encode()) data = await ws.receive(timeout=2) assert ws.closed assert ws.close_code == 4400 assert data.extra == "WebSocket message type must be text"
def test_ping_pong(test_client): with test_client.websocket_connect("/", [GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: ws.send_json(ConnectionInitMessage().as_dict()) response = ws.receive_json() assert response == ConnectionAckMessage().as_dict() ws.send_json(PingMessage().as_dict()) response = ws.receive_json() assert response == PongMessage().as_dict() ws.close()
async def test_ping_pong(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json(PingMessage().as_dict()) response = await ws.receive_json() assert response == PongMessage().as_dict() await ws.close() assert ws.closed
async def test_connection_init_timeout_cancellation(aiohttp_client): app = create_app(connection_init_wait_timeout=timedelta(milliseconds=50)) aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await asyncio.sleep(0.1) await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query= "subscription { debug { isConnectionInitTimeoutTaskDone } }" ), ).as_dict()) response = await ws.receive_json() assert (response == NextMessage( id="sub1", payload={ "data": { "debug": { "isConnectionInitTimeoutTaskDone": True } } }, ).as_dict()) await ws.close() assert ws.closed
async def test_server_sent_ping(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query="subscription { requestPing }"), ).as_dict()) response = await ws.receive_json() assert response == PingMessage().as_dict() await ws.send_json(PongMessage().as_dict()) response = await ws.receive_json() assert (response == NextMessage(id="sub1", payload={ "data": { "requestPing": True } }).as_dict()) response = await ws.receive_json() assert response == CompleteMessage(id="sub1").as_dict() await ws.close() assert ws.closed
async def test_subscription_cancellation(aiohttp_client): app = create_app() aiohttp_app_client = await aiohttp_client(app) async with aiohttp_app_client.ws_connect( "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL]) as ws: await ws.send_json(ConnectionInitMessage().as_dict()) response = await ws.receive_json() assert response == ConnectionAckMessage().as_dict() await ws.send_json( SubscribeMessage( id="sub1", payload=SubscribeMessagePayload( query='subscription { echo(message: "Hi", delay: 99) }'), ).as_dict()) await ws.send_json( SubscribeMessage( id="sub2", payload=SubscribeMessagePayload( query="subscription { debug { numActiveResultHandlers } }", ), ).as_dict()) response = await ws.receive_json() assert (response == NextMessage(id="sub2", payload={ "data": { "debug": { "numActiveResultHandlers": 2 } } }).as_dict()) response = await ws.receive_json() assert response == CompleteMessage(id="sub2").as_dict() await ws.send_json(CompleteMessage(id="sub1").as_dict()) await ws.send_json( SubscribeMessage( id="sub3", payload=SubscribeMessagePayload( query="subscription { debug { numActiveResultHandlers } }", ), ).as_dict()) response = await ws.receive_json() assert (response == NextMessage(id="sub3", payload={ "data": { "debug": { "numActiveResultHandlers": 1 } } }).as_dict()) response = await ws.receive_json() assert response == CompleteMessage(id="sub3").as_dict() await ws.close() assert ws.closed