diff --git a/arkindex/documents/tests/test_put_elements.py b/arkindex/documents/tests/test_put_elements.py new file mode 100644 index 0000000000000000000000000000000000000000..07a3e530cbc27459b68b29f06ab181e46e8e6f16 --- /dev/null +++ b/arkindex/documents/tests/test_put_elements.py @@ -0,0 +1,254 @@ +from django.urls import reverse +from rest_framework import status + +from arkindex.documents.models import Corpus, Element +from arkindex.images.models import ImageServer +from arkindex.project.aws import S3FileStatus +from arkindex.project.tests import FixtureAPITestCase +from arkindex.users.models import Role, User + + +class TestPatchElements(FixtureAPITestCase): + + @classmethod + def setUpTestData(cls): + super().setUpTestData() + cls.volume_type = cls.corpus.types.get(slug='volume') + cls.page_type = cls.corpus.types.get(slug='page') + cls.line_type = cls.corpus.types.get(slug='text_line') + cls.vol = cls.corpus.elements.get(name='Volume 1') + cls.element = Element.objects.get(name='Volume 1, page 2r') + cls.line = cls.corpus.elements.get(name='Text line') + cls.image = ImageServer.objects.local.images.create( + path="kingdom/far/away", + status=S3FileStatus.Checked, + width=42, + height=42, + ) + cls.private_corpus = Corpus.objects.create(name='private', public=False) + cls.private_elt = cls.private_corpus.elements.create(type=cls.private_corpus.types.create(slug='type')) + + def test_put_element_unverified(self): + """ + Check putting an element with an unverified email is not possible + """ + user = User.objects.create_user('nope@nope.fr') + self.assertFalse(user.verified_email) + self.client.force_login(user) + + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.vol.id)}), + data={'name': 'Untitled (2)'}, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertDictEqual( + response.json(), + {'detail': 'You do not have permission to perform this action.'} + ) + + def test_put_no_write_access(self): + # Create read_only corpus right + self.private_corpus.memberships.create(user=self.user, level=Role.Guest.value) + self.assertTrue(self.user.verified_email) + self.client.force_login(self.user) + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.private_elt.id)}), + data={'name': 'Untitled (2)'}, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertDictEqual( + response.json(), + {'detail': 'You do not have write access to this element.'} + ) + + def test_put_element_no_read_access(self): + """ + Check putting an element as anonymous user is not possible + """ + ext_user = User.objects.create_user(email='ark@ark.net') + ext_user.verified_email = True + ext_user.save() + self.client.force_login(ext_user) + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.private_elt.id)}), + data={'name': 'Untitled (2)'}, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_put_element(self): + self.client.force_login(self.user) + self.assertEqual(self.vol.name, 'Volume 1') + self.assertEqual(self.vol.type, self.volume_type) + with self.assertNumQueries(11): + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.vol.id)}), + data={'name': 'Untitled (2)', 'type': 'text_line'}, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + self.assertEqual(data['name'], 'Untitled (2)') + self.assertEqual(data['type'], 'text_line') + self.vol.refresh_from_db() + self.assertEqual(self.vol.name, 'Untitled (2)') + self.assertEqual(self.vol.type, self.line_type) + + def test_put_element_name_type_only(self): + """ + Ensure putting an element only updates name and type, nothing else, + and every other property is ignored + """ + self.client.force_login(self.user) + box_type = self.corpus.types.create(slug='xyzzy', display_name='box', folder=True) + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.vol.id)}), + data={ + 'id': 'beef', + 'name': 'New name', + 'type': 'xyzzy', + 'corpus': 'something', + 'thumbnail_url': '/dev/urandom', + 'thumbnail_put_url': '/dev/null', + 'zone': 51, + 'metadata': [], + 'classifications': [], + }, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json()['name'], 'New name') + self.assertEqual(response.json()['type'], 'xyzzy') + self.vol.refresh_from_db() + self.assertEqual(self.vol.name, 'New name') + self.assertEqual(self.vol.type, box_type) + self.assertNotEqual(self.vol.id, 'beef') + self.assertNotEqual(self.vol.corpus_id, 'something') + + def test_put_element_polygon(self): + self.client.force_login(self.user) + self.assertIsNotNone(self.element.image_id) + self.assertIsNotNone(self.element.polygon) + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.element.id)}), + data={ + 'name': 'Untitled (2)', + 'type': 'text_line', + 'polygon': [[10, 20], [10, 30], [30, 30], [30, 20], [10, 20]] + }, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json()['name'], 'Untitled (2)') + self.assertEqual(response.json()['type'], 'text_line') + self.element.refresh_from_db() + self.assertEqual(self.element.name, 'Untitled (2)') + self.assertEqual(self.element.type, self.line_type) + self.assertTupleEqual( + self.element.polygon.coords, + ((10, 20), (10, 30), (30, 30), (30, 20), (10, 20)) + ) + + def test_put_element_polygon_without_image(self): + self.client.force_login(self.user) + self.vol.image = None + self.vol.polygon = None + self.vol.save() + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.vol.id)}), + data={ + 'name': 'Untitled (2)', + 'type': 'text_line', + 'polygon': [[10, 20], [10, 30], [30, 30], [30, 20], [10, 20]] + }, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertDictEqual(response.json(), { + 'image': ['Image is required when defining a polygon on an element without an existing zone'] + }) + + def test_put_element_image_preserve_polygon(self): + self.client.force_login(self.user) + self.assertNotEqual(self.element.image, self.image) + expected_polygon = self.element.polygon.clone() + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.element.id)}), + data={ + 'name': 'Untitled (2)', + 'type': 'text_line', + 'image': str(self.image.id), + }, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.element.refresh_from_db() + self.assertEqual(self.element.image, self.image) + self.assertEqual(self.element.polygon, expected_polygon) + + def test_put_element_new_zone(self): + self.client.force_login(self.user) + self.assertIsNone(self.vol.image_id) + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.vol.id)}), + data={ + 'name': 'Untitled (2)', + 'type': 'text_line', + 'image': str(self.image.id), + 'polygon': [[10, 20], [10, 30], [30, 30], [30, 20], [10, 20]], + }, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.vol.refresh_from_db() + self.assertEqual(self.vol.image, self.image) + self.assertTupleEqual( + self.vol.polygon.coords, + ((10, 20), (10, 30), (30, 30), (30, 20), (10, 20)) + ) + + def test_put_element_invalid_dimensions(self): + self.client.force_login(self.user) + self.assertIsNone(self.vol.image) + bad_image = self.imgsrv.images.create( + path='oh-no', + status=S3FileStatus.Unchecked, + width=0, + height=0, + ) + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.vol.id)}), + data={ + 'name': 'Untitled (2)', + 'type': 'text_line', + 'image': str(bad_image.id), + 'polygon': [[10, 20], [10, 30], [30, 30], [30, 20], [10, 20]], + }, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertDictEqual(response.json(), {'image': ['This image does not have valid dimensions.']}) + self.assertIsNone(self.vol.image) + + def test_put_element_polygon_negative(self): + self.client.force_login(self.user) + response = self.client.put( + reverse('api:element-retrieve', kwargs={'pk': str(self.element.id)}), + data={ + 'name': 'Untitled (2)', + 'type': 'text_line', + 'polygon': [[-10, -20], [10, 30], [30, 30], [30, 20], [10, 20]] + }, + format='json', + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertDictEqual(response.json(), { + 'polygon': { + '0': { + '0': ['Ensure this value is greater than or equal to 0.'], + '1': ['Ensure this value is greater than or equal to 0.'], + } + } + })