Browse Source

Update test_document.py to make it easy to understand

pull/1349/head
Hironsan 3 years ago
parent
commit
d5ce4d58d4
1 changed files with 67 additions and 418 deletions
  1. 485
      backend/api/tests/api/test_document.py

485
backend/api/tests/api/test_document.py

@ -1,447 +1,96 @@
from django.conf import settings
from django.contrib.auth.models import User
from model_mommy import mommy
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase
from ...models import DOCUMENT_CLASSIFICATION, Document
from .utils import (TestUtilsMixin, assign_user_to_role, create_default_roles,
remove_all_role_mappings)
from ...models import DOCUMENT_CLASSIFICATION
from .utils import CRUDMixin, make_doc, make_user, prepare_project
class TestDocumentListAPI(APITestCase, TestUtilsMixin):
class TestDocumentListAPI(CRUDMixin):
@classmethod
def setUpTestData(cls):
cls.project_member_name = 'project_member_name'
cls.project_member_pass = 'project_member_pass'
cls.non_project_member_name = 'non_project_member_name'
cls.non_project_member_pass = 'non_project_member_pass'
cls.super_user_name = 'super_user_name'
cls.super_user_pass = 'super_user_pass'
create_default_roles()
project_member = User.objects.create_user(username=cls.project_member_name,
password=cls.project_member_pass)
non_project_member = User.objects.create_user(username=cls.non_project_member_name,
password=cls.non_project_member_pass)
super_user = User.objects.create_superuser(username=cls.super_user_name,
password=cls.super_user_pass,
email='fizz@buzz.com')
def setUp(self):
self.project = prepare_project(task=DOCUMENT_CLASSIFICATION)
self.non_member = make_user()
make_doc(self.project.item)
self.data = {'text': 'example'}
self.url = reverse(viewname='doc_list', args=[self.project.item.id])
cls.main_project = mommy.make(
_model='TextClassificationProject',
users=[project_member, super_user],
project_type=DOCUMENT_CLASSIFICATION
)
doc1 = mommy.make('Document', project=cls.main_project)
doc2 = mommy.make('Document', project=cls.main_project)
mommy.make('Document', project=cls.main_project)
def test_allows_project_member_to_list_docs(self):
for member in self.project.users:
self.assert_fetch(member, status.HTTP_200_OK)
cls.random_order_project = mommy.make(
_model='TextClassificationProject',
project_type=DOCUMENT_CLASSIFICATION,
users=[project_member, super_user],
randomize_document_order=True
)
mommy.make('Document', 100, project=cls.random_order_project)
def test_denies_non_project_member_to_list_docs(self):
self.assert_fetch(self.non_member, status.HTTP_403_FORBIDDEN)
sub_project = mommy.make(
_model='TextClassificationProject',
project_type=DOCUMENT_CLASSIFICATION,
users=[non_project_member]
)
mommy.make('Document', project=sub_project)
cls.url = reverse(viewname='doc_list', args=[cls.main_project.id])
cls.random_order_project_url = reverse(viewname='doc_list', args=[cls.random_order_project.id])
cls.data = {'text': 'example'}
assign_user_to_role(project_member=project_member, project=cls.main_project,
role_name=settings.ROLE_ANNOTATOR)
assign_user_to_role(project_member=project_member, project=cls.random_order_project,
role_name=settings.ROLE_ANNOTATOR)
def test_denies_unauthenticated_user_to_list_docs(self):
self.assert_fetch(expected=status.HTTP_403_FORBIDDEN)
mommy.make('DocumentAnnotation', document=doc1, user=project_member)
mommy.make('DocumentAnnotation', document=doc2, user=project_member)
def test_allows_project_admin_to_create_doc(self):
self.assert_create(self.project.users[0], status.HTTP_201_CREATED)
def _test_list(self, url, username, password, expected_num_results):
self.client.login(username=username, password=password)
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.json().get('results')), expected_num_results)
def test_denies_non_project_admin_to_create_doc(self):
for member in self.project.users[1:]:
self.assert_create(member, status.HTTP_403_FORBIDDEN)
def test_returns_docs_to_project_member(self):
self._test_list(self.url,
username=self.project_member_name,
password=self.project_member_pass,
expected_num_results=3)
def test_denies_unauthenticated_user_to_create_doc(self):
self.assert_create(expected=status.HTTP_403_FORBIDDEN)
def test_returns_docs_to_project_member_filtered_to_active(self):
self._test_list('{}?doc_annotations__isnull=true'.format(self.url),
username=self.project_member_name,
password=self.project_member_pass,
expected_num_results=1)
def test_returns_docs_to_project_member_filtered_to_completed(self):
self._test_list('{}?doc_annotations__isnull=false'.format(self.url),
username=self.project_member_name,
password=self.project_member_pass,
expected_num_results=2)
class TestDocumentDetail(CRUDMixin):
def test_returns_docs_to_project_member_filtered_to_active_with_collaborative_annotation(self):
self._test_list('{}?doc_annotations__isnull=true'.format(self.url),
username=self.super_user_name,
password=self.super_user_pass,
expected_num_results=3)
def setUp(self):
self.project = prepare_project(task=DOCUMENT_CLASSIFICATION)
self.non_member = make_user()
doc = make_doc(self.project.item)
self.data = {'text': 'example'}
self.url = reverse(viewname='doc_detail', args=[self.project.item.id, doc.id])
self._patch_project(self.main_project, 'collaborative_annotation', True)
def test_allows_project_member_to_get_doc(self):
for member in self.project.users:
self.assert_fetch(member, status.HTTP_200_OK)
self._test_list('{}?doc_annotations__isnull=true'.format(self.url),
username=self.super_user_name,
password=self.super_user_pass,
expected_num_results=1)
def test_denies_non_project_member_to_get_doc(self):
self.assert_fetch(self.non_member, status.HTTP_403_FORBIDDEN)
def test_returns_docs_to_project_member_filtered_to_completed_with_collaborative_annotation(self):
self._test_list('{}?doc_annotations__isnull=false'.format(self.url),
username=self.super_user_name,
password=self.super_user_pass,
expected_num_results=0)
def test_denies_unauthenticated_user_to_get_doc(self):
self.assert_fetch(expected=status.HTTP_403_FORBIDDEN)
self._patch_project(self.main_project, 'collaborative_annotation', True)
def test_allows_project_admin_to_update_doc(self):
self.assert_update(self.project.users[0], status.HTTP_200_OK)
self._test_list('{}?doc_annotations__isnull=false'.format(self.url),
username=self.super_user_name,
password=self.super_user_pass,
expected_num_results=2)
def test_denies_non_project_admin_to_update_doc(self):
for member in self.project.users[1:]:
self.assert_update(member, status.HTTP_403_FORBIDDEN)
def test_returns_docs_in_consistent_order_for_all_users(self):
self.client.login(username=self.project_member_name, password=self.project_member_pass)
user1_documents = self.client.get(self.url, format='json').json().get('results')
self.client.logout()
def test_denies_non_project_member_to_update_doc(self):
self.assert_update(self.non_member, status.HTTP_403_FORBIDDEN)
self.client.login(username=self.super_user_name, password=self.super_user_pass)
user2_documents = self.client.get(self.url, format='json').json().get('results')
self.client.logout()
def test_allows_project_admin_to_delete_doc(self):
self.assert_delete(self.project.users[0], status.HTTP_204_NO_CONTENT)
self.assertEqual([doc['id'] for doc in user1_documents], [doc['id'] for doc in user2_documents])
def test_denies_non_project_admin_to_delete_doc(self):
for member in self.project.users[1:]:
self.assert_delete(member, status.HTTP_403_FORBIDDEN)
def test_can_return_docs_in_consistent_random_order(self):
self.client.login(username=self.project_member_name, password=self.project_member_pass)
user1_documents1 = self.client.get(self.random_order_project_url, format='json').json().get('results')
user1_documents2 = self.client.get(self.random_order_project_url, format='json').json().get('results')
self.client.logout()
self.assertEqual(user1_documents1, user1_documents2)
def test_denies_non_project_member_to_delete_doc(self):
self.assert_delete(self.non_member, status.HTTP_403_FORBIDDEN)
self.client.login(username=self.super_user_name, password=self.super_user_pass)
user2_documents1 = self.client.get(self.random_order_project_url, format='json').json().get('results')
user2_documents2 = self.client.get(self.random_order_project_url, format='json').json().get('results')
self.client.logout()
self.assertEqual(user2_documents1, user2_documents2)
self.assertNotEqual(user1_documents1, user2_documents1)
self.assertNotEqual(user1_documents2, user2_documents2)
class TestApproveLabelsAPI(CRUDMixin):
def test_do_not_return_docs_to_non_project_member(self):
self.client.login(username=self.non_project_member_name,
password=self.non_project_member_pass)
response = self.client.get(self.url, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def setUp(self):
self.project = prepare_project(task=DOCUMENT_CLASSIFICATION)
self.non_member = make_user()
doc = make_doc(self.project.item)
self.url = reverse(viewname='approve_labels', args=[self.project.item.id, doc.id])
def test_do_not_return_docs_of_other_projects(self):
self._test_list(self.url,
username=self.project_member_name,
password=self.project_member_pass,
expected_num_results=self.main_project.documents.count())
def test_allow_project_admin_and_approver_to_approve_and_disapprove(self):
for member in self.project.users[:2]:
self.data = {'approved': True}
response = self.assert_create(member, status.HTTP_200_OK)
self.assertEqual(response.data['annotation_approver'], member.username)
self.data = {'approved': False}
response = self.assert_create(member, status.HTTP_200_OK)
self.assertIsNone(response.data['annotation_approver'])
def test_allows_superuser_to_create_doc(self):
self.client.login(username=self.super_user_name,
password=self.super_user_pass)
response = self.client.post(self.url, format='json', data=self.data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_disallows_project_member_to_create_doc(self):
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.post(self.url, format='json', data=self.data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@classmethod
def doCleanups(cls):
remove_all_role_mappings()
class TestDocumentDetailAPI(APITestCase):
@classmethod
def setUpTestData(cls):
cls.project_member_name = 'project_member_name'
cls.project_member_pass = 'project_member_pass'
cls.non_project_member_name = 'non_project_member_name'
cls.non_project_member_pass = 'non_project_member_pass'
cls.super_user_name = 'super_user_name'
cls.super_user_pass = 'super_user_pass'
create_default_roles()
project_member = User.objects.create_user(username=cls.project_member_name,
password=cls.project_member_pass)
non_project_member = User.objects.create_user(username=cls.non_project_member_name,
password=cls.non_project_member_pass)
# Todo: change super_user to project_admin.
super_user = User.objects.create_superuser(username=cls.super_user_name,
password=cls.super_user_pass,
email='fizz@buzz.com')
project = mommy.make(
_model='TextClassificationProject',
project_type=DOCUMENT_CLASSIFICATION,
users=[project_member, super_user]
)
cls.doc = mommy.make('Document', project=project)
cls.url = reverse(viewname='doc_detail', args=[project.id, cls.doc.id])
cls.data = {'text': 'example'}
assign_user_to_role(project_member=project_member, project=project,
role_name=settings.ROLE_ANNOTATOR)
def test_returns_doc_to_project_member(self):
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json')
self.assertEqual(response.data['id'], self.doc.id)
def test_do_not_return_doc_to_non_project_member(self):
self.client.login(username=self.non_project_member_name,
password=self.non_project_member_pass)
response = self.client.get(self.url, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_allows_superuser_to_update_doc(self):
self.client.login(username=self.super_user_name,
password=self.super_user_pass)
response = self.client.patch(self.url, format='json', data=self.data)
self.assertEqual(response.data['text'], self.data['text'])
def test_disallows_project_member_to_update_doc(self):
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.patch(self.url, format='json', data=self.data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_allows_superuser_to_delete_doc(self):
self.client.login(username=self.super_user_name,
password=self.super_user_pass)
response = self.client.delete(self.url, format='json')
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
def test_disallows_project_member_to_delete_doc(self):
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.delete(self.url, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@classmethod
def doCleanups(cls):
remove_all_role_mappings()
class TestApproveLabelsAPI(APITestCase):
@classmethod
def setUpTestData(cls):
cls.annotator_name = 'annotator_name'
cls.annotator_pass = 'annotator_pass'
cls.approver_name = 'approver_name_name'
cls.approver_pass = 'approver_pass'
cls.project_admin_name = 'project_admin_name'
cls.project_admin_pass = 'project_admin_pass'
create_default_roles()
annotator = User.objects.create_user(username=cls.annotator_name,
password=cls.annotator_pass)
approver = User.objects.create_user(username=cls.approver_name,
password=cls.approver_pass)
project_admin = User.objects.create_user(username=cls.project_admin_name,
password=cls.project_admin_pass)
project = mommy.make(
_model='TextClassificationProject',
project_type=DOCUMENT_CLASSIFICATION,
users=[annotator, approver, project_admin]
)
cls.doc = mommy.make('Document', project=project)
cls.url = reverse(viewname='approve_labels', args=[project.id, cls.doc.id])
assign_user_to_role(project_member=annotator, project=project,
role_name=settings.ROLE_ANNOTATOR)
assign_user_to_role(project_member=approver, project=project,
role_name=settings.ROLE_ANNOTATION_APPROVER)
assign_user_to_role(project_member=project_admin, project=project,
role_name=settings.ROLE_PROJECT_ADMIN)
def test_allow_project_admin_to_approve_and_disapprove_labels(self):
self.client.login(username=self.project_admin_name, password=self.project_admin_pass)
response = self.client.post(self.url, format='json', data={'approved': True})
self.assertEqual(response.data['annotation_approver'], self.project_admin_name)
response = self.client.post(self.url, format='json', data={'approved': False})
self.assertIsNone(response.data['annotation_approver'])
def test_allow_approver_to_approve_and_disapprove_labels(self):
self.client.login(username=self.approver_name, password=self.approver_pass)
response = self.client.post(self.url, format='json', data={'approved': True})
self.assertEqual(response.data['annotation_approver'], self.approver_name)
response = self.client.post(self.url, format='json', data={'approved': False})
self.assertIsNone(response.data['annotation_approver'])
def test_disallows_non_annotation_approver_to_approve_and_disapprove_labels(self):
self.client.login(username=self.annotator_name, password=self.annotator_pass)
response = self.client.post(self.url, format='json', data={'approved': True})
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@classmethod
def doCleanups(cls):
remove_all_role_mappings()
class TestSearch(APITestCase):
@classmethod
def setUpTestData(cls):
cls.project_member_name = 'project_member_name'
cls.project_member_pass = 'project_member_pass'
cls.non_project_member_name = 'non_project_member_name'
cls.non_project_member_pass = 'non_project_member_pass'
create_default_roles()
project_member = User.objects.create_user(username=cls.project_member_name,
password=cls.project_member_pass)
non_project_member = User.objects.create_user(username=cls.non_project_member_name,
password=cls.non_project_member_pass)
cls.main_project = mommy.make(
_model='TextClassificationProject',
project_type=DOCUMENT_CLASSIFICATION,
users=[project_member]
)
cls.search_term = 'example'
doc1 = mommy.make('Document', text=cls.search_term, project=cls.main_project)
doc2 = mommy.make('Document', text='Lorem', project=cls.main_project)
label1 = mommy.make('Label', project=cls.main_project)
label2 = mommy.make('Label', project=cls.main_project)
mommy.make('SequenceAnnotation', document=doc1, user=project_member, label=label1)
mommy.make('SequenceAnnotation', document=doc2, user=project_member, label=label2)
sub_project = mommy.make(
_model='TextClassificationProject',
project_type=DOCUMENT_CLASSIFICATION,
users=[non_project_member]
)
mommy.make('Document', text=cls.search_term, project=sub_project)
cls.url = reverse(viewname='doc_list', args=[cls.main_project.id])
cls.data = {'q': cls.search_term}
assign_user_to_role(project_member=project_member, project=cls.main_project,
role_name=settings.ROLE_ANNOTATOR)
def test_can_filter_doc_by_term(self):
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=self.data)
count = Document.objects.filter(text__contains=self.search_term,
project=self.main_project).count()
self.assertEqual(response.data['count'], count)
def test_can_order_doc_by_created_at_ascending(self):
params = {'ordering': 'created_at'}
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=params)
docs = Document.objects.filter(project=self.main_project).order_by('created_at').values()
for d1, d2 in zip(response.data['results'], docs):
self.assertEqual(d1['id'], d2['id'])
def test_can_order_doc_by_created_at_descending(self):
params = {'ordering': '-created_at'}
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=params)
docs = Document.objects.filter(project=self.main_project).order_by('-created_at').values()
for d1, d2 in zip(response.data['results'], docs):
self.assertEqual(d1['id'], d2['id'])
def test_can_order_doc_by_annotation_updated_at_ascending(self):
params = {'ordering': 'seq_annotations__updated_at'}
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=params)
docs = Document.objects.filter(project=self.main_project).order_by('seq_annotations__updated_at').values()
for d1, d2 in zip(response.data['results'], docs):
self.assertEqual(d1['id'], d2['id'])
def test_can_order_doc_by_annotation_updated_at_descending(self):
params = {'ordering': '-seq_annotations__updated_at'}
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=params)
docs = Document.objects.filter(project=self.main_project).order_by('-seq_annotations__updated_at').values()
for d1, d2 in zip(response.data['results'], docs):
self.assertEqual(d1['id'], d2['id'])
@classmethod
def doCleanups(cls):
remove_all_role_mappings()
class TestFilter(APITestCase):
@classmethod
def setUpTestData(cls):
cls.project_member_name = 'project_member_name'
cls.project_member_pass = 'project_member_pass'
create_default_roles()
project_member = User.objects.create_user(username=cls.project_member_name,
password=cls.project_member_pass)
cls.main_project = mommy.make(
_model='SequenceLabelingProject',
users=[project_member],
project_type='SequenceLabeling'
)
cls.label1 = mommy.make('Label', project=cls.main_project)
cls.label2 = mommy.make('Label', project=cls.main_project)
doc1 = mommy.make('Document', project=cls.main_project)
doc2 = mommy.make('Document', project=cls.main_project)
mommy.make('Document', project=cls.main_project)
mommy.make('SequenceAnnotation', document=doc1, user=project_member, label=cls.label1)
mommy.make('SequenceAnnotation', document=doc2, user=project_member, label=cls.label2)
cls.url = reverse(viewname='doc_list', args=[cls.main_project.id])
cls.params = {'seq_annotations__label__id': cls.label1.id}
assign_user_to_role(project_member=project_member, project=cls.main_project,
role_name=settings.ROLE_ANNOTATOR)
def test_can_filter_by_label(self):
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=self.params)
docs = Document.objects.filter(project=self.main_project,
seq_annotations__label__id=self.label1.id).values()
for d1, d2 in zip(response.data['results'], docs):
self.assertEqual(d1['id'], d2['id'])
def test_can_filter_doc_with_annotation(self):
params = {'seq_annotations__isnull': False}
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=params)
docs = Document.objects.filter(project=self.main_project, seq_annotations__isnull=False).values()
self.assertEqual(response.data['count'], docs.count())
for d1, d2 in zip(response.data['results'], docs):
self.assertEqual(d1['id'], d2['id'])
def test_can_filter_doc_without_anotation(self):
params = {'seq_annotations__isnull': True}
self.client.login(username=self.project_member_name,
password=self.project_member_pass)
response = self.client.get(self.url, format='json', data=params)
docs = Document.objects.filter(project=self.main_project, seq_annotations__isnull=True).values()
self.assertEqual(response.data['count'], docs.count())
for d1, d2 in zip(response.data['results'], docs):
self.assertEqual(d1['id'], d2['id'])
@classmethod
def doCleanups(cls):
remove_all_role_mappings()
def test_denies_annotator_to_approve_and_disapprove(self):
self.assert_create(self.project.users[2], status.HTTP_403_FORBIDDEN)
Loading…
Cancel
Save