Skip to content

Commit

Permalink
Add basic test for Export Query/Task
Browse files Browse the repository at this point in the history
  • Loading branch information
thenav56 committed Sep 11, 2023
1 parent 3f5a4fa commit afae96b
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ sitecustomize.py
.coverage
htmlcov/
.pytest_cache/

rest-media-temp/
5 changes: 2 additions & 3 deletions apps/common/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.db import models
from django.db.models.fields.files import FieldFile as DjFieldFile
from django.http import HttpRequest
from django.core.files.storage import FileSystemStorage, storages
from django.core.files.storage import FileSystemStorage, default_storage
from django.core.cache import cache
from django.conf import settings

Expand Down Expand Up @@ -41,8 +41,7 @@ def modified_by(self, info: Info) -> UserType:
def get_cached_file_uri(file: DjFieldFile, request: HttpRequest) -> str | None:
if file.name is None:
return

if isinstance(storages.backends['default'], FileSystemStorage):
if isinstance(default_storage, FileSystemStorage):
return request.build_absolute_uri(file.url)
# Other is only S3 for now
cache_key = CacheKey.URL_CACHED_FILE_FIELD_KEY_FORMAT.format(CacheKey.generate_hash(file.name))
Expand Down
4 changes: 2 additions & 2 deletions apps/export/exporter/xlsform.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ def __init__(self, leaf_group):
class XlsQuestionType:
@staticmethod
def get_select_one(question):
name = f'select_one {question.choice.name}'
name = f'select_one {question.choice_collection.name}'
if question.is_or_other:
return f'{name} or_other'
return name

@staticmethod
def get_select_multiple(question):
name = f'select_multiple {question.choice.name}'
name = f'select_multiple {question.choice_collection.name}'
if question.is_or_other:
return f'{name} or_other'
return name
Expand Down
12 changes: 12 additions & 0 deletions apps/export/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from factory.django import DjangoModelFactory

from .models import QuestionnaireExport


class QuestionnaireExportFactory(DjangoModelFactory):
type = QuestionnaireExport.Type.XLSFORM
status = QuestionnaireExport.Status.PENDING
file = 'random-file-path'

class Meta:
model = QuestionnaireExport
1 change: 1 addition & 0 deletions apps/export/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Status(models.IntegerChoices):

questionnaire_id: int
exported_by_id: int
get_type_display: typing.Callable[..., str]
get_status_display: typing.Callable[..., str]

def __str__(self):
Expand Down
161 changes: 161 additions & 0 deletions apps/export/tests/test_mutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
from unittest.mock import patch

from main.tests import TestCase

from apps.user.factories import UserFactory
from apps.project.factories import ProjectFactory
from apps.project.models import ProjectMembership
from apps.export.models import QuestionnaireExport
from apps.questionnaire.factories import QuestionnaireFactory


class TestExportMutation(TestCase):
class Mutation:
CREATE_EXPORT = '''
mutation MyMutation(
$projectId: ID!,
$data: QuestionnaireExportCreateInput!
) {
private {
id
projectScope(pk: $projectId) {
id
createQuestionnaireExport(data: $data) {
ok
errors
result {
id
type
typeDisplay
statusDisplay
status
startedAt
exportedAt
endedAt
questionnaireId
exportedBy {
id
}
file {
name
url
}
}
}
}
}
}
'''

DELETE_EXPORT = '''
mutation MyMutation(
$projectId: ID!,
$questionnaireId: ID!
) {
private {
id
projectScope(pk: $projectId) {
id
deleteQuestionnaireExport(id: $questionnaireId) {
ok
errors
result {
id
type
typeDisplay
statusDisplay
status
startedAt
exportedAt
endedAt
questionnaireId
exportedBy {
id
displayName
}
file {
name
url
}
}
}
}
}
}
'''

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.user, cls.ro_user, cls.other_user = UserFactory.create_batch(3)

user_resource_params = {'created_by': cls.user, 'modified_by': cls.user}
cls.project = ProjectFactory.create(**user_resource_params)
cls.project.add_member(cls.user)
cls.project.add_member(cls.ro_user, role=ProjectMembership.Role.VIEWER)

cls.q1, _ = QuestionnaireFactory.create_batch(2, project=cls.project, **user_resource_params)

@patch('apps.export.serializers.export_task.delay')
def test_export(self, export_task_mock):
class DummyCeleryTaskResponse():
id = 'random-async-task-id'

variables = {
'projectId': self.gID(self.project.id),
'data': {
'type': self.genum(QuestionnaireExport.Type.XLSFORM),
'questionnaire': self.gID(self.q1.pk),
},
}

export_task_mock.return_value = DummyCeleryTaskResponse()
# Without authentication -----
with self.captureOnCommitCallbacks(execute=True):
self.query_check(self.Mutation.CREATE_EXPORT, variables=variables, assert_errors=True)
export_task_mock.assert_not_called()

# With authentication (Without access to project) -----
self.force_login(self.other_user)
with self.captureOnCommitCallbacks(execute=True):
content = self.query_check(self.Mutation.CREATE_EXPORT, variables=variables)
export_task_mock.assert_not_called()
# assert content['data']['private']['projectScope']['questionnaireExport'] is None
assert content['data']['private']['projectScope'] is None

# With authentication (Without access to export) -----
self.force_login(self.ro_user)
with self.captureOnCommitCallbacks(execute=True):
content = self.query_check(self.Mutation.CREATE_EXPORT, variables=variables)
export_task_mock.assert_not_called()
# assert content['data']['private']['projectScope']['questionnaireExport'] is None
assert content['data']['private']['projectScope']['createQuestionnaireExport']['ok'] is False
assert content['data']['private']['projectScope']['createQuestionnaireExport']['errors'] is not None

# With Access
self.force_login(self.user)
with self.captureOnCommitCallbacks(execute=True):
content = self.query_check(
self.Mutation.CREATE_EXPORT,
variables=variables
)['data']['private']['projectScope']['createQuestionnaireExport']
export_task_mock.assert_called_once()
assert content['ok'] is True
assert content['errors'] is None
export = QuestionnaireExport.objects.get(pk=content['result']['id'])
assert export.get_task_id() == DummyCeleryTaskResponse.id
assert content['result'] == {
'id': self.gID(export.pk),
'type': self.genum(QuestionnaireExport.Type.XLSFORM),
'typeDisplay': export.get_type_display(),
'status': self.genum(QuestionnaireExport.Status.PENDING),
'statusDisplay': export.get_status_display(),
'exportedAt': self.gdatetime(export.exported_at),
'startedAt': self.gdatetime(export.started_at),
'endedAt': self.gdatetime(export.ended_at),
'exportedBy': {
'id': self.gID(export.exported_by_id),
},
'file': None,
'questionnaireId': self.gID(self.q1.pk),
}
Loading

0 comments on commit afae96b

Please sign in to comment.