def test_supports_generic_in_unions_multiple_vars(): A = typing.TypeVar("A") B = typing.TypeVar("B") @strawberry.type class Edge(typing.Generic[A, B]): info: A node: B @strawberry.type class Fallback: node: str @strawberry.type class Query: @strawberry.field def example(self) -> typing.Union[Fallback, Edge[int, str]]: return Edge(node="string", info=1) schema = strawberry.Schema(query=Query) query = """{ example { __typename ... on IntStrEdge { node info } } }""" result = schema.execute_sync(query) assert not result.errors assert result.data == { "example": { "__typename": "IntStrEdge", "node": "string", "info": 1 } }
def test_supports_lists_within_unions(): T = typing.TypeVar("T") @strawberry.type class User: name: str @strawberry.type class Edge(typing.Generic[T]): nodes: typing.List[T] @strawberry.type class Query: @strawberry.field def user(self, info) -> typing.Union[User, Edge[User]]: return Edge(nodes=[User("P")]) schema = strawberry.Schema(query=Query) query = """{ user { __typename ... on UserEdge { nodes { name } } } }""" result = schema.execute_sync(query) assert not result.errors assert result.data == { "user": { "__typename": "UserEdge", "nodes": [{ "name": "P" }] } }
def test_field_description(): @strawberry.type class Query: a: str = strawberry.field(description="Example") @strawberry.field def b(self, info, id: int) -> str: return "I'm a resolver" @strawberry.field(description="Example C") def c(self, info, id: int) -> str: return "I'm a resolver" schema = strawberry.Schema(query=Query) query = """{ __type(name: "Query") { fields { name description } } }""" result = schema.execute_sync(query) assert not result.errors assert result.data["__type"]["fields"] == [ { "name": "a", "description": "Example" }, { "name": "b", "description": None }, { "name": "c", "description": "Example C" }, ]
def test_raises_error_when_unable_to_find_type(): global T, User, Edge T = typing.TypeVar("T") @strawberry.type class User: name: str @strawberry.type class Edge(typing.Generic[T]): nodes: typing.List[T] @strawberry.type class Query: @strawberry.field def user(self) -> typing.Union[User, Edge[User]]: return Edge(nodes=["bad example"]) # type: ignore schema = strawberry.Schema(query=Query) query = """{ user { __typename ... on UserEdge { nodes { name } } } }""" result = schema.execute_sync(query) assert result.errors[0].message == ( "Unable to find type for <class 'tests.schema.test_generics." "test_raises_error_when_unable_to_find_type.<locals>.Edge'> " "and (<class 'str'>,)" ) del T, User, Edge
async def test_runs_directives(): @strawberry.type class Person: name: str = "Jess" @strawberry.type class Query: @strawberry.field def person(self, info) -> Person: return Person() @strawberry.directive(locations=[DirectiveLocation.FIELD], description="Make string uppercase") def uppercase(value: str): return value.upper() @strawberry.directive(locations=[DirectiveLocation.FIELD]) def replace(value: str, old: str, new: str): return value.replace(old, new) schema = strawberry.Schema(query=Query, directives=[uppercase, replace]) query = """query People($identified: Boolean!){ person { name @uppercase } jess: person { name @replace(old: "Jess", new: "Jessica") } johnDoe: person { name @replace(old: "Jess", new: "John") @include(if: $identified) } }""" result = await execute(schema, query, variable_values={"identified": False}) assert not result.errors assert result.data["person"]["name"] == "JESS" assert result.data["jess"]["name"] == "Jessica" assert result.data["johnDoe"].get("name") is None
def test_generated_names(): T = typing.TypeVar("T") @strawberry.type class EdgeWithCursor(typing.Generic[T]): cursor: strawberry.ID node: T @strawberry.type class SpecialPerson: name: str @strawberry.type class Query: @strawberry.field def person_edge(self) -> EdgeWithCursor[SpecialPerson]: return EdgeWithCursor( cursor=strawberry.ID("1"), node=SpecialPerson(name="Example") ) schema = strawberry.Schema(query=Query) query = """{ personEdge { __typename cursor node { name } } }""" result = schema.execute_sync(query) assert not result.errors assert result.data == { "personEdge": { "__typename": "SpecialPersonEdgeWithCursor", "cursor": "1", "node": {"name": "Example"}, } }
def create_app(**kwargs): @strawberry.input class FolderInput: files: typing.List[Upload] @strawberry.type class Query: hello: str = "strawberry" @strawberry.type class Mutation: @strawberry.mutation def read_text(self, text_file: Upload) -> str: return text_file.read().decode() @strawberry.mutation def read_files(self, files: typing.List[Upload]) -> typing.List[str]: contents = [] for file in files: contents.append(file.read().decode()) return contents @strawberry.mutation def read_folder(self, folder: FolderInput) -> typing.List[str]: contents = [] for file in folder.files: contents.append(file.read().decode()) return contents schema = strawberry.Schema(query=Query, mutation=Mutation) class GraphQLView(BaseGraphQLView): def get_root_value(self): return Query() app = Sanic(f"test_{int(random()*1000)}") app.add_route( GraphQLView.as_view(schema=schema, graphiql=kwargs.get("graphiql", True)), "/graphql", ) return app
def test_generic_with_enum_as_param_of_type_inside_unions(): T = typing.TypeVar("T") @strawberry.type class Pet: name: str @strawberry.type class ErrorNode(typing.Generic[T]): code: T @strawberry.enum class Codes(Enum): a = "a" b = "b" @strawberry.type class Query: @strawberry.field def result(self) -> typing.Union[Pet, ErrorNode[Codes]]: return ErrorNode(code=Codes.a) schema = strawberry.Schema(query=Query) query = """{ result { __typename ... on CodesErrorNode { code } } }""" result = schema.execute_sync(query) assert not result.errors assert result.data == { "result": { "__typename": "CodesErrorNode", "code": "a" } }
def test_supports_generic(): T = typing.TypeVar("T") @strawberry.type class Edge(typing.Generic[T]): cursor: strawberry.ID node: T @strawberry.type class Person: name: str @strawberry.type class Query: @strawberry.field def person_edge(self, info, **kwargs) -> Edge[Person]: return Edge(cursor=strawberry.ID("1"), node=Person(name="Example")) schema = strawberry.Schema(query=Query) query = """{ personEdge { __typename cursor node { name } } }""" result = graphql_sync(schema, query) assert not result.errors assert result.data == { "personEdge": { "__typename": "PersonEdge", "cursor": "1", "node": { "name": "Example" }, } }
async def test_subscription_with_arguments(): @strawberry.type class Query: x: str = "Hello" @strawberry.type class Subscription: @strawberry.subscription async def example(self, name: str) -> typing.AsyncGenerator[str, None]: yield f"Hi {name}" schema = strawberry.Schema(query=Query, subscription=Subscription) query = 'subscription { example(name: "Nina") }' sub = await schema.subscribe(query) result = await sub.__anext__() assert not result.errors assert result.data["example"] == "Hi Nina"
def test_private_field_access_in_resolver(): @strawberry.type class Query: name: str age: strawberry.Private[int] @strawberry.field def age_in_months(self) -> int: return self.age * 12 schema = strawberry.Schema(query=Query) result = schema.execute_sync( "query { ageInMonths }", root_value=Query(name="Dave", age=7) ) assert not result.errors assert result.data == { "ageInMonths": 84, }
def test_override_unknown_scalars(): Duration = strawberry.scalar( timedelta, name="Duration", serialize=timedelta.total_seconds, parse_value=lambda s: timedelta(seconds=s), ) @strawberry.type class Query: @strawberry.field def duration(self, value: timedelta) -> timedelta: return value schema = strawberry.Schema(Query, scalar_overrides={timedelta: Duration}) result = schema.execute_sync("{ duration(value: 10) }") assert not result.errors assert result.data == {"duration": 10}
def test_deserialization(): @strawberry.type class Query: deserialized = None @strawberry.field def deserialize(self, arg: datetime.date) -> bool: Query.deserialized = arg return True schema = strawberry.Schema(Query) query = """query Deserialize($value: Date!) { deserialize(arg: $value) }""" result = schema.execute_sync(query, variable_values={"value": "2019-10-25"}) assert not result.errors assert Query.deserialized == datetime.date(2019, 10, 25)
def test_errors_when_using_a_generic_without_passing_a_type(): T = typing.TypeVar("T") @strawberry.type class Edge(typing.Generic[T]): cursor: strawberry.ID node: T @strawberry.type class Query: @strawberry.field def int_edge(self, info, **kwargs) -> Edge: return Edge(cursor=strawberry.ID("1"), node=1) with pytest.raises(TypeError) as error: strawberry.Schema(query=Query) assert str(error) == ( f'Query fields cannot be resolved. The type "{Edge}" ' 'of the field "int_edge" is generic, but no type has been passed')
async def test_subscription(): @strawberry.type class Query: x: str = "Hello" @strawberry.type class Subscription: @strawberry.subscription async def example(self, info) -> typing.AsyncGenerator[str, None]: yield "Hi" schema = strawberry.Schema(query=Query, subscription=Subscription) query = "subscription { example }" sub = await subscribe(schema, parse(query)) result = await sub.__anext__() assert not result.errors assert result.data["example"] == "Hi"
def test_raises_graphql_error_when_permission_is_denied(): class IsAuthenticated(BasePermission): message = "User is not authenticated" def has_permission(self, source: typing.Any, info: Info, **kwargs) -> bool: return False @strawberry.type class Query: @strawberry.field(permission_classes=[IsAuthenticated]) def user(self) -> str: return "patrick" schema = strawberry.Schema(query=Query) query = "{ user }" result = schema.execute_sync(query) assert result.errors[0].message == "User is not authenticated"
async def test_asking_for_wrong_field(): @strawberry.type class Query: example: Optional[str] = None schema = strawberry.Schema(query=Query, extensions=[DisableValidation()]) query = """ query { sample } """ result = await schema.execute( query, root_value=Query(), ) assert result.errors is None assert result.data == {}
def test_argument_descriptions(): @strawberry.type class Query: @strawberry.field def hello( # type: ignore name: Annotated[ str, strawberry.argument(description="Your name") # noqa: F722 ] = "Patrick") -> str: return f"Hi {name}" schema = strawberry.Schema(query=Query) assert str(schema) == dedent('''\ type Query { hello( """Your name""" name: String! = "Patrick" ): String! }''')
def test_using_generics_raises_when_missing_annotation(): @strawberry.type class Edge(Generic[T]): node: T @strawberry.type class User: name: str error_message = ( f'Query fields cannot be resolved. The type "{repr(Edge)}" ' "is generic, but no type has been passed" ) @strawberry.type class Query: user: Edge with pytest.raises(TypeError, match=error_message): strawberry.Schema(Query)
def test_init_var(): @strawberry.type class Category: name: str id: InitVar[str] @strawberry.type class Query: @strawberry.field def category(self, info) -> Category: return Category(name="example", id="123") schema = strawberry.Schema(query=Query) query = "{ category { name } }" result = schema.execute_sync(query) assert not result.errors assert result.data["category"]["name"] == "example"
def test_bounded_instance_method_resolvers(): class CoolClass: def method(self): _ = self return "something" instance = CoolClass() @strawberry.type class Query: blah: str = strawberry.field(resolver=instance.method) schema = strawberry.Schema(query=Query) query = "{ blah }" result = schema.execute_sync(query) assert not result.errors assert result.data == {"blah": "something"}
async def test_invalid_query_with_validation_disabled(): @strawberry.type class Query: example: Optional[str] = None schema = strawberry.Schema(query=Query) query = """ query { example """ result = await schema.execute(query, root_value=Query()) assert str( result.errors[0]) == ("Syntax Error: Expected Name, found <EOF>.\n\n" "GraphQL request:4:5\n" "3 | example\n" "4 | \n" " | ^")
async def test_errors_when_running_async_in_sync_mode(): @strawberry.type class Query: @strawberry.field async def name(self) -> str: return "Patrick" schema = strawberry.Schema(query=Query) query = """ query { name } """ with pytest.raises(RuntimeError) as e: schema.execute_sync(query) assert e.value.args[ 0] == "GraphQL execution failed to complete synchronously."
def test_duplicate_scalars(): MyCustomScalar = strawberry.scalar( str, name="MyCustomScalar", ) MyCustomScalar2 = strawberry.scalar( int, name="MyCustomScalar", ) @strawberry.type class Query: scalar_1: MyCustomScalar scalar_2: MyCustomScalar2 with pytest.raises( TypeError, match="Scalar `MyCustomScalar` has already been registered"): strawberry.Schema(Query)
def test_supports_generic_in_unions(): T = typing.TypeVar("T") @strawberry.type class Edge(typing.Generic[T]): cursor: strawberry.ID node: T @strawberry.type class Fallback: node: str @strawberry.type class Query: @strawberry.field def example(self) -> typing.Union[Fallback, Edge[int]]: return Edge(cursor=strawberry.ID("1"), node=1) schema = strawberry.Schema(query=Query) query = """{ example { __typename ... on IntEdge { cursor node } } }""" result = schema.execute_sync(query) assert not result.errors assert result.data == { "example": { "__typename": "IntEdge", "cursor": "1", "node": 1 } }
def test_named_union(): global Result @strawberry.type class A: a: int @strawberry.type class B: b: int Result = strawberry.union("Result", (A, B)) @strawberry.type class Query: ab: Result = A(a=5) schema = strawberry.Schema(query=Query) query = """{ __type(name: "Result") { kind description } ab { __typename, ... on A { a } } }""" result = schema.execute_sync(query, root_value=Query()) assert not result.errors assert result.data["ab"] == {"__typename": "A", "a": 5} assert result.data["__type"] == {"kind": "UNION", "description": None} del Result
def test_union_as_mutation_return(): global A, B @strawberry.type class A: x: int @strawberry.type class B: y: int @strawberry.type class Mutation: @strawberry.mutation def hello(self) -> Union[A, B]: return B(y=5) schema = strawberry.Schema(query=A, mutation=Mutation) query = """ mutation { hello { __typename ... on A { x } ... on B { y } } } """ result = schema.execute_sync(query) assert not result.errors assert result.data["hello"] == {"__typename": "B", "y": 5} del A, B
def test_field_deprecated_reason(): @strawberry.type class Query: a: str = strawberry.field(deprecation_reason="Deprecated A") @strawberry.field def b(self, id: int) -> str: return "I'm a resolver" @strawberry.field(deprecation_reason="Deprecated B") def c(self, id: int) -> str: return "I'm a resolver" schema = strawberry.Schema(query=Query) query = """{ __type(name: "Query") { fields(includeDeprecated: true) { name deprecationReason } } }""" result = schema.execute_sync(query) assert not result.errors assert result.data["__type"]["fields"] == [ { "name": "a", "deprecationReason": "Deprecated A" }, { "name": "b", "deprecationReason": None }, { "name": "c", "deprecationReason": "Deprecated B" }, ]
def test_optional_input_field_unset(): @strawberry.input class TestInput: name: Optional[str] = UNSET age: Optional[int] = UNSET @strawberry.type class Query: @strawberry.field def hello(self, input: TestInput) -> str: if is_unset(input.name): return "Hi there" return f"Hi {input.name}" schema = strawberry.Schema(query=Query) assert ( str(schema) == dedent( """ type Query { hello(input: TestInput!): String! } input TestInput { name: String age: Int } """ ).strip() ) result = schema.execute_sync( """ query { hello(input: {}) } """ ) assert not result.errors assert result.data == {"hello": "Hi there"}
def test_execution_context_operation_name_and_type(): operation_name = None operation_type = None class MyExtension(Extension): def on_request_end(self): nonlocal operation_name nonlocal operation_type execution_context = self.execution_context operation_name = execution_context.operation_name operation_type = execution_context.operation_type schema = strawberry.Schema(Query, extensions=[MyExtension]) result = schema.execute_sync("{ ping }") assert not result.errors assert operation_name is None assert operation_type == "QUERY" # Try again with an operation_name result = schema.execute_sync("query MyOperation { ping }") assert not result.errors assert operation_name == "MyOperation" assert operation_type == "QUERY" # Try again with an operation_name override result = schema.execute_sync( """ query MyOperation { ping } query MyOperation2 { ping } """, operation_name="MyOperation2", ) assert not result.errors assert operation_name == "MyOperation2" assert operation_type == "QUERY"