From c503b746aeee5b6f844d10090a5084006bb5931e Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Fri, 20 Sep 2024 12:04:14 -0400 Subject: [PATCH] project.test_views: test handling of invalid reference order. Past bugs in the project editing forms have allowed project references to be created with 'order=None', or with two references having the same 'order'. Either situation should now prevent the project from being submitted; check that this is the case. ReferenceFormSet should allow these situations to be fixed manually: if the confirm_reference_order checkbox is checked, then the 'order' of all references should be updated; and if that box is not checked, the 'order' of existing references should be untouched. Check that this works as expected. --- physionet-django/project/test_views.py | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/physionet-django/project/test_views.py b/physionet-django/project/test_views.py index 64b74d90a3..8bac0e8ee5 100644 --- a/physionet-django/project/test_views.py +++ b/physionet-django/project/test_views.py @@ -1,5 +1,6 @@ import base64 +import html.parser import os from http import HTTPStatus import json @@ -41,6 +42,26 @@ def _basic_auth(username, password, encoding='UTF-8'): return 'Basic ' + token +def _parse_html_form_fields(content): + """ + Parse HTML and return all form fields as a dictionary. + + Note that this currently only handles input elements, not other + form elements such as select or textarea. It also makes no + distinction between multiple forms. + """ + fields = {} + + class Parser(html.parser.HTMLParser): + def handle_starttag(self, tag, attrs): + attrs = dict(attrs) + if tag == 'input' and 'name' in attrs: + fields[attrs['name']] = attrs.get('value', '') + + Parser().feed(content) + return fields + + class TestAccessPresubmission(TestMixin): """ Test that certain views or content in their various states can only @@ -558,6 +579,55 @@ def test_content(self): project.refresh_from_db() self.assertFalse(project.is_submittable()) + def test_reference_order(self): + """ + Test handling of references with invalid order. + """ + self.client.login(username=self.AUTHOR, password=self.PASSWORD) + + project = ActiveProject.objects.get(title=self.PROJECT_TITLE) + self.assertEqual(project.references.count(), 0) + self.assertTrue(project.is_submittable()) + + # References with distinct order values are okay. + ref1 = project.references.create(description="asdf", order=1) + ref2 = project.references.create(description="ghjk", order=2) + self.assertTrue(project.is_submittable()) + + # Same order value for two references is an error. + ref1.order = 2 + ref1.save() + self.assertFalse(project.is_submittable()) + + # Order value of None is an error. + ref2.order = None + ref2.save() + self.assertFalse(project.is_submittable()) + + content_url = reverse('project_content', args=(project.slug,)) + response = self.client.get(content_url) + data = _parse_html_form_fields(response.content.decode()) + + # Try submitting form without confirm_reference_order. + # Existing order values should be unchanged. + data.pop('confirm_reference_order', None) + response = self.client.post(content_url, data=data) + ref1.refresh_from_db() + self.assertEqual(ref1.order, 2) + ref2.refresh_from_db() + self.assertEqual(ref2.order, None) + self.assertFalse(project.is_submittable()) + + # Try submitting form with confirm_reference_order. + # Order values should be unique. + data['confirm_reference_order'] = '1' + response = self.client.post(content_url, data=data) + ref1.refresh_from_db() + self.assertEqual(ref1.order, 1) + ref2.refresh_from_db() + self.assertEqual(ref2.order, 2) + self.assertTrue(project.is_submittable()) + class TestProjectTransfer(TestCase): """