Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: mock pypandoc.convert_text when testing export views #1117

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,20 @@ def files(settings, tmp_path):
def json_data():
json_file = Path(settings.BASE_DIR) / 'import' / 'catalogs.json'
return {'elements': json.loads(json_file.read_text())}


@pytest.fixture
def mocked_convert_text(mocker):
"""Mock the pypandoc.convert_text function.

`mocked_convert_text` can be used in tests of the export views.
Use it to assert pypandoc would have been called with:
mocked_convert_text.assert_called(), mocked_convert_text.assert_called_once() or
mocked_convert_text.assert_called_once_with().

See:
- <https://pytest-mock.readthedocs.io/en/latest/usage.html>
- <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock>
"""
from rdmo.core.utils import pypandoc # noqa: F401
return mocker.patch("pypandoc.convert_text")
18 changes: 2 additions & 16 deletions rdmo/conditions/tests/test_viewset_condition.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import xml.etree.ElementTree as et

import pytest

from django.urls import reverse
Expand Down Expand Up @@ -64,19 +62,13 @@ def test_index(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_export(db, client, username, password, export_format):
def test_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)

url = reverse(urlnames['export']) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['list'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['condition']


def test_export_search(db, client):
client.login(username='editor', password='editor')
Expand Down Expand Up @@ -285,16 +277,10 @@ def test_delete(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_detail_export(db, client, username, password, export_format):
def test_detail_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)
instance = Condition.objects.first()

url = reverse(urlnames['detail_export'], args=[instance.pk]) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['detail'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['condition']
18 changes: 2 additions & 16 deletions rdmo/conditions/tests/test_viewset_condition_multisite.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import xml.etree.ElementTree as et

import pytest

from django.urls import reverse
Expand Down Expand Up @@ -32,19 +30,13 @@ def test_index(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_export(db, client, username, password, export_format):
def test_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)

url = reverse(urlnames['export']) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['list'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['condition']


@pytest.mark.parametrize('username,password', users)
def test_detail(db, client, username, password):
Expand Down Expand Up @@ -245,16 +237,10 @@ def test_delete(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_detail_export(db, client, username, password, export_format):
def test_detail_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)
instance = Condition.objects.first()

url = reverse(urlnames['detail_export'], args=[instance.pk]) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['detail'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['condition']
54 changes: 53 additions & 1 deletion rdmo/core/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from http import HTTPStatus
from typing import Optional

import pytest

from rdmo.core.utils import human2bytes, join_url, sanitize_url
from rdmo.core.utils import human2bytes, join_url, render_to_format, sanitize_url
from rdmo.projects.models import Project
from rdmo.projects.utils import get_value_path
from rdmo.views.utils import ProjectWrapper

urls = (
('', ''),
Expand All @@ -23,6 +27,8 @@
("0", 0),
)

export_formats = ("invalid", "rtf", "odt", "docx", "html", "markdown", "tex", "pdf")


@pytest.mark.parametrize('url,sanitized_url', urls)
def test_sanitize_url(url, sanitized_url):
Expand All @@ -36,3 +42,49 @@ def test_join_url():
@pytest.mark.parametrize('human,bytes', human2bytes_test_values)
def test_human2bytes(human: Optional[str], bytes: float):
assert human2bytes(human) == bytes


@pytest.fixture(scope="module")
def context(django_db_blocker):
"""A fixture of a view context, to be used when testing 'render_to_format'.

Note: The fixture queries the database only once for the parametrized tests.
"""
with django_db_blocker.unblock():
project = Project.objects.get(id=1)
project.catalog.prefetch_elements()
return {
"format": None, # will be defined in the test function
"project": project,
"project_wrapper": ProjectWrapper(project),
"title": project.title,
"resource_path": get_value_path(project)
}


@pytest.mark.django_db()
@pytest.mark.parametrize("export_format", export_formats)
def test_render_to_format(rf, context, export_format):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

render_to_format, export_format xml and project don't play along. xml was not tested previously. This kind of data can not be exportes as xml?

Is the test still alright?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xml is not using render_to_format so it does not need to be tested here. It should also stay in the regular tests and not be mocked.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine otherwise, thanks a lot!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♂️

Okay, thanks for the hint. Only pypandoc.convert_text is mocked.

I removed multiple checks of the generated XML. Should these checks stay then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the xml serializers/renderers stuff is pretty unique and also fast, please keep it in

"""A test of the 'render_to_format' view with multiple export formats."""
context["format"] = export_format
title = context["project"].title

request = rf.get("/projects/project_answers_export")
response = render_to_format(
request,
export_format,
title,
"projects/project_answers_export.html",
context,
)

# an invalid export format should return an error message
if export_format == "invalid":
assert response.status_code == HTTPStatus.BAD_REQUEST
assert "This format is not supported." in str(response.content)
return
# check for the requested export format in the headers
assert response.status_code == HTTPStatus.OK
assert export_format in response.headers["Content-Type"]
expected = f'filename="{title}.{export_format}"'
assert expected in response.headers["Content-Disposition"]
18 changes: 2 additions & 16 deletions rdmo/domain/tests/test_viewset_attribute.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import xml.etree.ElementTree as et

import pytest

from django.urls import reverse
Expand Down Expand Up @@ -54,19 +52,13 @@ def test_list(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_export(db, client, username, password, export_format):
def test_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)

url = reverse(urlnames['export']) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['list'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['attribute']


def test_export_search(db, client):
client.login(username='editor', password='editor')
Expand Down Expand Up @@ -241,16 +233,10 @@ def test_delete(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_detail_export(db, client, username, password, export_format):
def test_detail_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)
instance = Attribute.objects.first()

url = reverse(urlnames['detail_export'], args=[instance.pk]) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['detail'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['attribute']
18 changes: 2 additions & 16 deletions rdmo/options/tests/test_viewset_options.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import xml.etree.ElementTree as et

import pytest

from django.db.models import Max
Expand Down Expand Up @@ -65,19 +63,13 @@ def test_index(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_export(db, client, username, password, export_format):
def test_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)

url = reverse(urlnames['export']) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['list'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['option']


def test_export_search(db, client):
client.login(username='editor', password='editor')
Expand Down Expand Up @@ -182,16 +174,10 @@ def test_delete(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_detail_export(db, client, username, password, export_format):
def test_detail_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)
instance = Option.objects.first()

url = reverse(urlnames['detail_export'], args=[instance.pk]) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['detail'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['option']
18 changes: 2 additions & 16 deletions rdmo/options/tests/test_viewset_optionsets.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import xml.etree.ElementTree as et

import pytest

from django.urls import reverse
Expand Down Expand Up @@ -65,19 +63,13 @@ def test_index(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_export(db, client, username, password, export_format):
def test_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)

url = reverse(urlnames['export']) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['list'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['optionset', 'option']


def test_export_search(db, client):
client.login(username='editor', password='editor')
Expand Down Expand Up @@ -289,16 +281,10 @@ def test_delete(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_detail_export(db, client, username, password, export_format):
def test_detail_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)
instance = OptionSet.objects.first()

url = reverse(urlnames['detail_export'], args=[instance.pk]) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['detail'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['optionset', 'option']
4 changes: 2 additions & 2 deletions rdmo/projects/tests/test_view_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def test_project_answers(db, client, username, password, project_id):
@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('project_id', projects)
@pytest.mark.parametrize('export_format', export_formats)
def test_project_answers_export(db, client, username, password, project_id, export_format):
def test_project_answers_export(db, client, username, password, project_id, export_format, mocked_convert_text):
client.login(username=username, password=password)

url = reverse('project_answers_export', args=[project_id, export_format])
Expand Down Expand Up @@ -730,7 +730,7 @@ def test_project_view(db, client, username, password, project_id):
@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('project_id', projects)
@pytest.mark.parametrize('export_format', export_formats)
def test_project_view_export(db, client, username, password, project_id, export_format, files):
def test_project_view_export(db, client, username, password, project_id, export_format, files, mocked_convert_text):
client.login(username=username, password=password)
project_views = Project.objects.get(pk=project_id).views.all()

Expand Down
16 changes: 2 additions & 14 deletions rdmo/questions/tests/test_viewset_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,13 @@ def test_index(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_export(db, client, username, password, export_format):
def test_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)

url = reverse(urlnames['export']) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['list'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['catalog', 'section', 'page', 'questionset', 'question']


def test_export_search(db, client):
client.login(username='editor', password='editor')
Expand Down Expand Up @@ -238,20 +232,14 @@ def test_delete(db, client, username, password):

@pytest.mark.parametrize('username,password', users)
@pytest.mark.parametrize('export_format', export_formats)
def test_detail_export(db, client, username, password, export_format):
def test_detail_export(db, client, username, password, export_format, mocked_convert_text):
client.login(username=username, password=password)
instance = Catalog.objects.first()

url = reverse(urlnames['detail_export'], args=[instance.pk]) + export_format + '/'
response = client.get(url)
assert response.status_code == status_map['detail'][username], response.content

if response.status_code == 200 and export_format == 'xml':
root = et.fromstring(response.content)
assert root.tag == 'rdmo'
for child in root:
assert child.tag in ['catalog', 'section', 'page', 'questionset', 'question']


def test_detail_export_full(db, client):
client.login(username='editor', password='editor')
Expand Down
Loading