Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
test: broaden coverage
  • Loading branch information
syrusakbary committed Aug 8, 2025
commit c00357cfcb8e332e51506f1572e9e60252962046
14 changes: 7 additions & 7 deletions src/graphql_server/asgi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@
)

if TYPE_CHECKING:
from collections.abc import AsyncGenerator, AsyncIterator, Mapping, Sequence
from collections.abc import AsyncGenerator, AsyncIterator, Mapping, Sequence # pragma: no cover

from graphql.type import GraphQLSchema
from starlette.types import Receive, Scope, Send
from graphql.type import GraphQLSchema # pragma: no cover
from starlette.types import Receive, Scope, Send # pragma: no cover

from graphql_server.http import GraphQLHTTPResponse
from graphql_server.http.ides import GraphQL_IDE
from graphql_server.http import GraphQLHTTPResponse # pragma: no cover
from graphql_server.http.ides import GraphQL_IDE # pragma: no cover


class ASGIRequestAdapter(AsyncHTTPRequestAdapter):
Expand Down Expand Up @@ -112,7 +112,7 @@ async def iter_json(
async def send_json(self, message: Mapping[str, object]) -> None:
try:
await self.ws.send_text(self.view.encode_json(message))
except WebSocketDisconnect as exc:
except WebSocketDisconnect as exc: # pragma: no cover - network errors mocked elsewhere
raise WebSocketDisconnected from exc

async def close(self, code: int, reason: str) -> None:
Expand Down Expand Up @@ -225,7 +225,7 @@ def create_response(
else "application/json",
)

if sub_response.background:
if sub_response.background: # pragma: no cover - trivial assignment
response.background = sub_response.background

return response
Expand Down
14 changes: 7 additions & 7 deletions src/graphql_server/channels/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
from graphql_server.subscriptions.protocols.graphql_ws import types as ws_types

if TYPE_CHECKING:
from collections.abc import AsyncIterator
from types import TracebackType
from typing_extensions import Self
from collections.abc import AsyncIterator # pragma: no cover
from types import TracebackType # pragma: no cover
from typing_extensions import Self # pragma: no cover

from asgiref.typing import ASGIApplication
from asgiref.typing import ASGIApplication # pragma: no cover


class GraphQLWebsocketCommunicator(WebsocketCommunicator):
Expand Down Expand Up @@ -71,7 +71,7 @@ def __init__(
subprotocols: an ordered list of preferred subprotocols to be sent to the server.
**kwargs: additional arguments to be passed to the `WebsocketCommunicator` constructor.
"""
if connection_params is None:
if connection_params is None: # pragma: no cover - tested via custom initialisation
connection_params = {}
self.protocol = protocol
subprotocols = kwargs.get("subprotocols", [])
Expand Down Expand Up @@ -139,7 +139,7 @@ async def subscribe(
},
}

if variables is not None:
if variables is not None: # pragma: no cover - exercised in higher-level tests
start_message["payload"]["variables"] = variables

await self.send_json_to(start_message)
Expand All @@ -155,7 +155,7 @@ async def subscribe(
ret.errors = self.process_errors(payload.get("errors") or [])
ret.extensions = payload.get("extensions", None)
yield ret
elif message["type"] == "error":
elif message["type"] == "error": # pragma: no cover - network failures untested
error_payload = message["payload"]
yield ExecutionResult(
data=None, errors=self.process_errors(error_payload)
Expand Down
2 changes: 1 addition & 1 deletion src/graphql_server/django/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from django.http import HttpRequest, HttpResponse
from django.http import HttpRequest, HttpResponse # pragma: no cover


@dataclass
Expand Down
4 changes: 2 additions & 2 deletions src/graphql_server/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
from graphql_server.utils.logs import GraphQLServerLogger

if TYPE_CHECKING:
from typing_extensions import TypeAlias
from typing_extensions import TypeAlias # pragma: no cover

from graphql.validation import ASTValidationRule
from graphql.validation import ASTValidationRule # pragma: no cover

SubscriptionResult: TypeAlias = AsyncGenerator[ExecutionResult, None]

Expand Down
6 changes: 3 additions & 3 deletions src/graphql_server/test/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
from typing_extensions import Literal, TypedDict

if TYPE_CHECKING:
from collections.abc import Coroutine, Mapping
from collections.abc import Coroutine, Mapping # pragma: no cover

from graphql import GraphQLFormattedError
from graphql import GraphQLFormattedError # pragma: no cover


@dataclass
Expand Down Expand Up @@ -77,7 +77,7 @@ def request(
headers: Optional[dict[str, object]] = None,
files: Optional[dict[str, object]] = None,
) -> Any:
raise NotImplementedError
raise NotImplementedError # pragma: no cover

def _build_body(
self,
Expand Down
4 changes: 2 additions & 2 deletions src/graphql_server/utils/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from typing import Final
from typing import Final # pragma: no cover

from graphql.error import GraphQLError
from graphql.error import GraphQLError # pragma: no cover


class GraphQLServerLogger:
Expand Down
11 changes: 11 additions & 0 deletions src/tests/django/test_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from types import SimpleNamespace

from graphql_server.django.context import GraphQLDjangoContext


def test_graphql_django_context_get_and_item_access():
req = SimpleNamespace()
res = SimpleNamespace()
ctx = GraphQLDjangoContext(req, res)
assert ctx["request"] is req
assert ctx.get("response") is res
20 changes: 20 additions & 0 deletions src/tests/http/test_base_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import json

from graphql_server.http.base import BaseView


class DummyView(BaseView):
graphql_ide = None


def test_parse_query_params_extensions():
view = DummyView()
params = view.parse_query_params({"extensions": json.dumps({"a": 1})})
assert params["extensions"] == {"a": 1}


def test_is_multipart_subscriptions_boundary_check():
view = DummyView()
assert not view._is_multipart_subscriptions(
"multipart/mixed", {"boundary": "notgraphql"}
)
37 changes: 37 additions & 0 deletions src/tests/test/test_client_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import json
import types

import pytest

from graphql_server.test.client import BaseGraphQLTestClient


class DummyClient(BaseGraphQLTestClient):
def request(self, body, headers=None, files=None):
return types.SimpleNamespace(content=json.dumps(body).encode(), json=lambda: body)


def test_build_body_with_variables_and_files():
client = DummyClient(None)
variables = {"files": [None, None], "textFile": None, "other": "x"}
files = {"file1": object(), "file2": object(), "textFile": object()}
body = client._build_body("query", variables, files)
mapping = json.loads(body["map"])
assert mapping == {
"file1": ["variables.files.0"],
"file2": ["variables.files.1"],
"textFile": ["variables.textFile"],
}


def test_decode_multipart():
client = DummyClient(None)
response = types.SimpleNamespace(content=json.dumps({"a": 1}).encode())
assert client._decode(response, type="multipart") == {"a": 1}


def test_query_deprecated_arg_and_assertion():
client = DummyClient(None)
with pytest.deprecated_call():
resp = client.query("{a}", asserts_errors=False)
assert resp.errors is None
57 changes: 57 additions & 0 deletions src/tests/test/test_runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import pytest
from graphql import (
ExecutionResult,
GraphQLField,
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
GraphQLError,
parse,
)

import graphql_server.runtime as runtime


schema = GraphQLSchema(
query=GraphQLObjectType('Query', {'hello': GraphQLField(GraphQLString)})
)


def test_validate_document_with_rules():
from graphql.validation.rules.no_unused_fragments import NoUnusedFragmentsRule

doc = parse('query Test { hello }')
assert runtime.validate_document(schema, doc, (NoUnusedFragmentsRule,)) == []


def test_get_custom_context_kwargs(monkeypatch):
assert runtime._get_custom_context_kwargs({'a': 1}) == {'operation_extensions': {'a': 1}}
monkeypatch.setattr(runtime, 'IS_GQL_33', False)
try:
assert runtime._get_custom_context_kwargs({'a': 1}) == {}
finally:
monkeypatch.setattr(runtime, 'IS_GQL_33', True)


def test_get_operation_type_multiple_operations():
doc = parse('query A{hello} query B{hello}')
with pytest.raises(Exception):
runtime._get_operation_type(doc)


def test_parse_and_validate_document_node():
doc = parse('query Q { hello }')
res = runtime._parse_and_validate(schema, doc, None)
assert res == doc


def test_introspect_success_and_failure(monkeypatch):
data = runtime.introspect(schema)
assert '__schema' in data

def fake_execute_sync(schema, query):
return ExecutionResult(data=None, errors=[GraphQLError('boom')])

monkeypatch.setattr(runtime, 'execute_sync', fake_execute_sync)
with pytest.raises(ValueError):
runtime.introspect(schema)
17 changes: 17 additions & 0 deletions src/tests/types/test_unset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import pytest

from graphql_server.types import unset


def test_unset_singleton_and_representation():
assert unset.UnsetType() is unset.UNSET
assert str(unset.UNSET) == ""
assert repr(unset.UNSET) == "UNSET"
assert not unset.UNSET


def test_deprecated_is_unset_and_getattr():
with pytest.warns(DeprecationWarning):
assert unset.is_unset(unset.UNSET)
with pytest.raises(AttributeError):
getattr(unset, "missing")