Skip to content

Commit

Permalink
graphql: Add tests for education and files modules
Browse files Browse the repository at this point in the history
  • Loading branch information
Kurocon committed Oct 28, 2024
1 parent d90e2aa commit 3494332
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 11 deletions.
21 changes: 12 additions & 9 deletions amelie/education/graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@
from django.utils.translation import gettext_lazy as _
from graphene_django.forms.mutation import DjangoFormMutation

from amelie import settings
from amelie.education.forms import EducationalBouquetForm
from amelie.graphql.pagination.connection_field import DjangoPaginationConnectionField

from amelie.education.models import Category, Page
from amelie.iamailer import MailTask, Recipient

from amelie.activities.graphql import ActivityLabelType
from amelie.calendar.graphql import EventType, EVENT_TYPE_BASE_FIELDS
Expand Down Expand Up @@ -65,7 +60,6 @@ class EducationEventFilterSet(FilterSet):
class Meta:
model = EducationEvent
fields = {
'id': ("exact",),
'summary_nl': ("icontains", "iexact"),
'summary_en': ("icontains", "iexact"),
'begin': ("gt", "lt", "exact"),
Expand Down Expand Up @@ -98,6 +92,7 @@ def resolve_activity_type(self: EducationEvent, info):
def resolve_absolute_url(self: EducationEvent, info):
return self.get_absolute_url()


class EducationQuery(graphene.ObjectType):
educationpage_category = graphene.Field(EducationPageCategoryType, id=graphene.ID())
educationpage_categories = DjangoPaginationConnectionField(EducationPageCategoryType)
Expand All @@ -106,7 +101,7 @@ class EducationQuery(graphene.ObjectType):
educationpages = DjangoPaginationConnectionField(EducationPageType)

education_event = graphene.Field(EducationEventType, id=graphene.ID())
education_events = DjangoPaginationConnectionField(EducationEventType)
education_events = DjangoPaginationConnectionField(EducationEventType, id=graphene.ID())

def resolve_educationpage_category(root, info, id=None):
"""Find education page category by ID"""
Expand All @@ -122,12 +117,20 @@ def resolve_educationpage(root, info, id=None, slug=None):
return Page.objects.get(slug=slug)
return None

def resolve_education_event(self, id=None):
def resolve_education_event(self, info, id=None):
"""Find education event by ID"""
qs = EducationEvent.objects.filter_public(info.context)
if id is not None:
return EducationEvent.objects.get(pk=id)
return qs.get(pk=id)
return None

def resolve_education_events(self, info, id=None, *args, **kwargs):
"""Find education event by ID"""
qs = EducationEvent.objects.filter_public(info.context)
if id is not None:
return qs.filter(pk=id)
return qs


class EducationalBouquetMutation(DjangoFormMutation):

Expand Down
4 changes: 2 additions & 2 deletions amelie/graphql/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import re
from typing import Optional, Dict, Tuple
from typing import Optional, Dict, Tuple, Union

from django.test import Client
from graphene_django.utils.testing import GraphQLTestMixin
Expand Down Expand Up @@ -30,7 +30,7 @@ def setUp(self):
self.load_basic_data()

def _test_private_model(self, query_name: str, public_field_spec: str = "id",
variables: Optional[Dict[str, Tuple[str, str]]] = None,
variables: Optional[Dict[str, Tuple[Union[str, int], str]]] = None,
error_regex: Optional[re.Pattern] = None):
"""
Test if a model instance that should be private is actually not accessible via GraphQL.
Expand Down
162 changes: 162 additions & 0 deletions amelie/graphql/tests/test_education.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import json
import datetime
import random
from typing import Dict

from django.core.files.uploadedfile import SimpleUploadedFile
from django.utils import timezone

from amelie.education.models import EducationEvent
from amelie.files.models import Attachment
from amelie.members.models import Committee
from amelie.graphql.tests import BaseGraphQLPrivateFieldTests


def generate_education_events():
"""
Generate Education Events for testing.
It will generate 4 events:
- A public event that is visible
- A public event that is not visible
- A private event that is visible
- A private event that is not visible
"""

now = timezone.now()
committee = Committee.objects.all()[0]

i = 0
for public in [True, False]:
i += 1
start = now + datetime.timedelta(days=i, seconds=random.uniform(0, 5*3600))
end = start + datetime.timedelta(seconds=random.uniform(3600, 10*3600))

event = EducationEvent(
begin=start, end=end, summary_nl='Test Event %i' % i,
summary_en='Test event %i' % i,
organizer=committee, public=public,
education_organizer="Education organizer"
)
event.save()


class EducationGraphQLPrivateFieldTests(BaseGraphQLPrivateFieldTests):
"""
Tests for private fields of models of the Education app
"""

def setUp(self):
super(EducationGraphQLPrivateFieldTests, self).setUp()

# Generate education events
generate_education_events()

# Retrieve those events
self.public_event = EducationEvent.objects.filter(public=True).order_by('-id').first()
self.private_event = EducationEvent.objects.filter(public=False).order_by('-id').first()

# Add a private and public attachment to the public visible event
self.public_attachment = Attachment(
public=True, file=SimpleUploadedFile("public.txt", b"File Contents")
)
self.public_attachment.save(create_thumbnails=False)
self.private_attachment = Attachment(
public=False, file=SimpleUploadedFile("private.txt", b"Secret Contents")
)
self.private_attachment.save(create_thumbnails=False)

self.public_event.attachments.add(self.public_attachment)
self.public_event.attachments.add(self.private_attachment)


EDUCATION_EVENT_PRIVATE_FIELDS: Dict[str, str] = {
"callback_url": "callbackUrl",
"callback_secret_key": "callbackSecretKey",
"update_count": "updateCount",

# organizer private subfields
"organizer.abbreviation": "organizer { abbreviation }",
"organizer.private_email": "organizer { privateEmail }",
"organizer.superuser": "organizer { superuser }",
"organizer.gitlab": "organizer { gitlab }",
"organizer.ledger_account_number": "organizer { ledgerAccountNumber }",

# organizer.function_set private subfields
"organizer.function_set.note": "organizer { functionSet { note }}",
}

def test_education_event_private_model(self):
# Test if private events cannot be retrieved
self._test_private_model(
query_name="educationEvent",
variables={"id": (self.private_event.id, "ID")}
)

def test_education_events_private_model(self):
# Test if private events cannot be retrieved via list view
self._test_private_model_list(
query_name="educationEvents",
public_field_spec="results { id }",
variables={"id": (self.private_event.id, "ID")}
)

def test_education_event_private_fields(self):
# Test if private fields on public events cannot be retrieved
for field_name, field_spec in self.EDUCATION_EVENT_PRIVATE_FIELDS.items():
self._test_public_model_and_private_field(
query_name="educationEvent", field_name=field_name, field_spec=field_spec,
variables={"id": (self.public_event.id, "ID")},
)

def test_education_events_private_fields(self):
# Test if private fields on public events cannot be retrieved via list view
for field_name, field_spec in self.EDUCATION_EVENT_PRIVATE_FIELDS.items():
# Wrap the field spec in "results { <SPEC> }" for list view
field_spec = f"results {{ {field_spec} }}"
self._test_public_model_and_private_field(
query_name="educationEvents", field_name=field_name, field_spec=field_spec,
variables={"id": (self.public_event.id, "ID")}
)

def test_education_event_private_attachment(self):
# Test if private event attachments are hidden in get view
query = "query ($id: ID) { educationEvent(id: $id) { attachments { public }}}"
response = self.query(query, variables={"id": self.public_event.id})
content = json.loads(response.content)

# The request should succeed
self.assertResponseNoErrors(
response,
f"Query for 'educationEvent', public field 'attachments' returned an error!"
)

# Check that all attachments are public, and that the correct amount of attachments are received (1)
self.assertTrue(all(a['public'] == True for a in content['data']['educationEvent']['attachments']),
f"Query for 'educationEvent', public field 'attachments' returned a private attachment!")
num_attachments = len(content['data']['educationEvent']['attachments'])
self.assertEqual(
num_attachments, 1,
f"Query for 'educationEvent', public field 'attachments' did not return 1 expected attachment (returned {num_attachments})!"
)

def test_education_events_private_attachment(self):
# Test if private event attachments are hidden in list view
query = "query ($id: ID) { educationEvents(id: $id) { results { attachments { public }}}}"
response = self.query(query, variables={"id": self.public_event.id})
content = json.loads(response.content)

# The request should succeed
self.assertResponseNoErrors(
response,
f"Query for 'educationEvents', public field 'attachments' returned an error!"
)

# Check that all attachments are public, and that the correct amount of attachments are received (1)
self.assertTrue(all(a['public'] == True for a in content['data']['educationEvents']['results'][0]['attachments']),
f"Query for 'educationEvents', public field 'attachments' returned a private attachment!")
num_attachments = len(content['data']['educationEvents']['results'][0]['attachments'])
self.assertEqual(
num_attachments, 1,
f"Query for 'educationEvents', public field 'attachments' did not return 1 expected attachment (returned {num_attachments})!"
)
28 changes: 28 additions & 0 deletions amelie/graphql/tests/test_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from django.core.files.uploadedfile import SimpleUploadedFile

from amelie.files.models import Attachment
from amelie.graphql.tests import BaseGraphQLPrivateFieldTests


class FilesGraphQLPrivateFieldTests(BaseGraphQLPrivateFieldTests):
"""
Tests for private fields of models of the Files app
"""

def setUp(self):
super(FilesGraphQLPrivateFieldTests, self).setUp()

# Create a private attachment
self.private_attachment = Attachment(
public=False, file=SimpleUploadedFile("private.txt", b"Secret Contents")
)
self.private_attachment.save(create_thumbnails=False)


def test_attachment_private_model(self):
# Test if private attachment cannot be retrieved
self._test_private_model(
query_name="attachment",
public_field_spec="public",
variables={"id": (self.private_attachment.id, "ID")}
)

0 comments on commit 3494332

Please sign in to comment.