Skip to content

Commit cc8ed3f

Browse files
committed
fix tests
1 parent ce784da commit cc8ed3f

File tree

2 files changed

+146
-10
lines changed

2 files changed

+146
-10
lines changed

gql/transport/http_multipart_transport.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ async def subscribe(
186186
async for result in self._parse_multipart_response(response, content_type):
187187
yield result
188188

189-
except TransportServerError:
189+
except (TransportServerError, TransportProtocolError):
190190
raise
191191
except Exception as e:
192192
raise TransportConnectionFailed(str(e)) from e
@@ -253,6 +253,9 @@ async def _parse_multipart_response(
253253
result = self._parse_multipart_part(part_data)
254254
if result:
255255
yield result
256+
except TransportServerError:
257+
# Re-raise transport-level errors
258+
raise
256259
except Exception as e:
257260
log.warning("Error parsing multipart part: %s", e)
258261

tests/test_http_multipart_transport.py

Lines changed: 142 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
import json
33
from typing import Mapping
44

5+
import aiohttp
56
import pytest
67

78
from gql import Client, gql
9+
from gql.graphql_request import GraphQLRequest
810
from gql.transport.exceptions import (
911
TransportAlreadyConnected,
1012
TransportClosed,
13+
TransportConnectionFailed,
1114
TransportProtocolError,
1215
TransportServerError,
1316
)
@@ -243,6 +246,7 @@ async def test_http_multipart_graphql_errors(aiohttp_server):
243246
"""Test handling of GraphQL-level errors in response."""
244247
from aiohttp import web
245248

249+
from gql.transport.exceptions import TransportQueryError
246250
from gql.transport.http_multipart_transport import HTTPMultipartTransport
247251

248252
async def handler(request):
@@ -275,15 +279,15 @@ async def handler(request):
275279
async with Client(transport=transport) as session:
276280
query = gql(subscription_str)
277281

278-
results = []
279-
async for result in session.subscribe(query):
280-
results.append(result)
282+
# Client raises TransportQueryError when there are errors in the result
283+
with pytest.raises(TransportQueryError) as exc_info:
284+
async for result in session.subscribe(query):
285+
pass
281286

282-
assert len(results) == 1
283-
assert results[0]["book"]["title"] == "Book 1"
284-
assert results[0].errors is not None
285-
assert len(results[0].errors) == 1
286-
assert "deprecated" in results[0].errors[0]["message"]
287+
# Verify error details
288+
assert "deprecated" in str(exc_info.value).lower()
289+
assert exc_info.value.data is not None
290+
assert exc_info.value.data["book"]["title"] == "Book 1"
287291

288292

289293
@pytest.mark.asyncio
@@ -369,9 +373,10 @@ async def test_http_multipart_transport_not_connected():
369373
transport = HTTPMultipartTransport(url="http://example.com/graphql")
370374

371375
query = gql(subscription_str)
376+
request = GraphQLRequest(query)
372377

373378
with pytest.raises(TransportClosed):
374-
async for result in transport.subscribe(query._ast):
379+
async for result in transport.subscribe(request):
375380
pass
376381

377382

@@ -492,3 +497,131 @@ async def handler(request):
492497
results.append(result)
493498

494499
assert len(results) == 1
500+
501+
502+
@pytest.mark.asyncio
503+
async def test_http_multipart_execute_empty_response(aiohttp_server):
504+
"""Test execute method with empty response (no results)."""
505+
from aiohttp import web
506+
507+
from gql.transport.http_multipart_transport import HTTPMultipartTransport
508+
509+
async def handler(request):
510+
# Return empty multipart response (no data parts)
511+
body = "--graphql--\r\n"
512+
return web.Response(
513+
text=body,
514+
content_type='multipart/mixed; boundary="graphql"',
515+
)
516+
517+
app = web.Application()
518+
app.router.add_route("POST", "/", handler)
519+
server = await aiohttp_server(app)
520+
521+
url = server.make_url("/")
522+
transport = HTTPMultipartTransport(url=url, timeout=10)
523+
524+
async with Client(transport=transport) as session:
525+
query = gql(subscription_str)
526+
527+
with pytest.raises(TransportProtocolError) as exc_info:
528+
await session.execute(query)
529+
530+
assert "No result received" in str(exc_info.value)
531+
532+
533+
@pytest.mark.asyncio
534+
async def test_http_multipart_response_without_payload_wrapper(aiohttp_server):
535+
"""Test parsing response without payload wrapper (direct format)."""
536+
from aiohttp import web
537+
538+
from gql.transport.http_multipart_transport import HTTPMultipartTransport
539+
540+
async def handler(request):
541+
# Send data in direct format (no payload wrapper)
542+
response = {"data": {"book": book1}}
543+
part = (
544+
f"--graphql\r\n"
545+
f"Content-Type: application/json\r\n"
546+
f"\r\n"
547+
f"{json.dumps(response)}\r\n"
548+
f"--graphql--\r\n"
549+
)
550+
return web.Response(
551+
text=part,
552+
content_type='multipart/mixed; boundary="graphql"',
553+
)
554+
555+
app = web.Application()
556+
app.router.add_route("POST", "/", handler)
557+
server = await aiohttp_server(app)
558+
559+
url = server.make_url("/")
560+
transport = HTTPMultipartTransport(url=url, timeout=10)
561+
562+
async with Client(transport=transport) as session:
563+
query = gql(subscription_str)
564+
565+
results = []
566+
async for result in session.subscribe(query):
567+
results.append(result)
568+
569+
assert len(results) == 1
570+
assert results[0]["book"]["title"] == "Book 1"
571+
572+
573+
@pytest.mark.asyncio
574+
async def test_http_multipart_newline_separator(aiohttp_server):
575+
"""Test parsing multipart response with LF separator instead of CRLF."""
576+
from aiohttp import web
577+
578+
from gql.transport.http_multipart_transport import HTTPMultipartTransport
579+
580+
async def handler(request):
581+
# Use LF instead of CRLF
582+
payload = {"data": {"book": book1}}
583+
wrapped = {"payload": payload}
584+
part = (
585+
f"--graphql\n"
586+
f"Content-Type: application/json\n"
587+
f"\n"
588+
f"{json.dumps(wrapped)}\n"
589+
f"--graphql--\n"
590+
)
591+
return web.Response(
592+
text=part,
593+
content_type='multipart/mixed; boundary="graphql"',
594+
)
595+
596+
app = web.Application()
597+
app.router.add_route("POST", "/", handler)
598+
server = await aiohttp_server(app)
599+
600+
url = server.make_url("/")
601+
transport = HTTPMultipartTransport(url=url, timeout=10)
602+
603+
async with Client(transport=transport) as session:
604+
query = gql(subscription_str)
605+
606+
results = []
607+
async for result in session.subscribe(query):
608+
results.append(result)
609+
610+
assert len(results) == 1
611+
assert results[0]["book"]["title"] == "Book 1"
612+
613+
614+
@pytest.mark.asyncio
615+
async def test_http_multipart_connection_error():
616+
"""Test handling of connection errors (non-transport exceptions)."""
617+
from gql.transport.http_multipart_transport import HTTPMultipartTransport
618+
619+
# Use an invalid URL that will fail to connect
620+
transport = HTTPMultipartTransport(url="http://invalid.local:99999/graphql", timeout=1)
621+
622+
async with Client(transport=transport) as session:
623+
query = gql(subscription_str)
624+
625+
with pytest.raises(TransportConnectionFailed):
626+
async for result in session.subscribe(query):
627+
pass

0 commit comments

Comments
 (0)