✅ Add basic create order test

🎨 Add MutationTest helper to help write mutation tests with strawberry
➕ add pytest-django
⬆ Bump some python deps
This commit is contained in:
nikolark authored and Nikolai R Kristiansen committed 2022-01-16 22:30:47 +01:00
1 parent 549b0cfdd6
commit 9d3afadbff
6 files changed
+325 -193

No files matched your search

+131
View File
@@ -0,0 +1,131 @@
from dataclasses import dataclass
from django.contrib.auth.models import User
from graphql.pyutils import snake_to_camel
import strawberry
from strawberry.arguments import is_unset
from strawberry.types import ExecutionResult
from main.api.schema import schema
from typing import Any, Iterable, Type
from django.test.client import RequestFactory
def lowercase_first_letter(text: str):
return text[0].lower() + text[1:]
def is_valid_field_values(values: Iterable) -> bool:
return isinstance(values, list) and all(isinstance(value, str) or isinstance(value, dict) for value in values)
@dataclass
class MutationTest:
"""Helper class to test graphql mutations"""
mutation: Type[strawberry.mutation]
fields: Iterable[str]
user: User = None
_schema: strawberry.Schema = schema # FIXME: take as arg
def __str__(self) -> str:
return f"MutationTest ({self.name})"
@property
def name(self) -> str:
return snake_to_camel(self.mutation.name)
@property
def arguments(self) -> Iterable[dict[str, Any]]:
return [
{
"name": lowercase_first_letter(snake_to_camel(arg.python_name)),
"type": self._schema.schema_converter.from_argument(arg).type,
"is_required": is_unset(arg),
}
for arg in self.mutation.arguments
]
@property
def required_arguments(self) -> Iterable[dict[str, Any]]:
return [arg for arg in self.arguments if arg["is_required"]]
@property
def optional_arguments(self) -> Iterable[dict[str, Any]]:
return [arg for arg in self.arguments if not arg["is_required"]]
@property
def mutation_args(self) -> tuple[Iterable[str], Iterable[str]]:
_definitions = []
_inputs = []
for arg in self.arguments:
name = arg["name"]
_definitions.append(f"${name}: {arg['type']}")
_inputs.append(f"{name}: ${name}")
return _definitions, _inputs
@classmethod
def expand_field(cls, field):
if isinstance(field, str):
return field
if isinstance(field, dict):
field_string = ""
for key, values in field.items():
if not is_valid_field_values(values):
raise ValueError("Wrong format in MutationTest retrieval fields")
values_string = ", ".join(cls.expand_field(value) for value in values)
field_string = f"""
{field_string}
{key} {{
{values_string}
}}
"""
return field_string
raise ValueError("MutationTest: Fields can only be strings or dicts")
@property
def serialize(self) -> str:
_definitions, _inputs = self.mutation_args
definitions = ", ".join(_definitions)
inputs = ", ".join(_inputs)
operation_name = lowercase_first_letter(self.name)
if not isinstance(self.fields, str):
expanded_fields = [self.expand_field(field) for field in self.fields]
return_fields = " ".join(expanded_fields)
else:
return_fields = self.fields
return f"""mutation {self.name}({definitions}) {{
{operation_name}({inputs}) {{
{return_fields}
}}
}}"""
@property
def user_request(self):
request_factory = RequestFactory()
request = request_factory.get("/api/v2/graphql/")
if self.user:
request.user = self.user
return request
# Perform the mutation
# only keyword args please
def mutate(self, **kwargs) -> ExecutionResult:
variables = {lowercase_first_letter(snake_to_camel(name)): val for name, val in kwargs.items()}
required_names = set(arg["name"] for arg in self.required_arguments)
missing_args = required_names - set(variables.keys())
if missing_args:
raise ValueError(f"Missing required arguments: {missing_args}")
# Execute mutation
return self._schema.execute_sync(
query=self.serialize, variable_values=variables, context_value=self.user_request
)
-16
View File
@@ -1,16 +0,0 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
+50
View File
@@ -0,0 +1,50 @@
import pytest
from django.test import Client
from strawberry.django.test import GraphQLTestClient
from main.api.mutations import create_order
from main.models import Product, Order
from main.testing import MutationTest
@pytest.fixture(name="gql_client")
def gql_client_fixture(client: Client):
yield GraphQLTestClient(client)
@pytest.fixture(name='product')
def product_fix(db):
return Product.objects.create(name='borg', sale_price_ext=35, sale_price_int=30)
@pytest.fixture(name='create_order_mutation')
def create_order_mutation_fixture(product):
fields = """
... on CreateOrderSuccess {
order {
id
orderSum
orderlines {
id
product {
id
}
}
}
}"""
# FIXME: maybe drop this helper and just use GraphQLTestClient
return MutationTest(create_order, fields)
def test_create_order(product, create_order_mutation):
assert Order.objects.count() == 0
res = create_order_mutation.mutate(customerId="external", isExternal=True, orderLines=[{"productId": product.id, "amount": 1}])
assert res
assert not res.errors
assert res.data
order_lines = res.data.get('createOrder', {}).get('order', {}).get('orderlines', [])
assert order_lines
assert order_lines[0]['product']['id'] == str(product.id)
assert Order.objects.count() == 1
o = Order.objects.first()
assert o.orderlines.first().product.id == product.id