def test_viewer_permission(self): """Viewers should be able to see the request if it is embargoed.""" embargoed_foia = FOIARequestFactory(embargo=True) viewer = UserFactory() normie = UserFactory() embargoed_foia.add_viewer(viewer) nose.tools.assert_true(embargoed_foia.has_perm(viewer, "view")) nose.tools.assert_false(embargoed_foia.has_perm(normie, "view"))
def test_demote_editor(self): """Editors should be able to demote editors to viewers.""" embargoed_foia = FOIARequestFactory(embargo=True) editor = UserFactory() embargoed_foia.add_editor(editor) nose.tools.assert_true(embargoed_foia.has_perm(editor, "view")) nose.tools.assert_true(embargoed_foia.has_perm(editor, "change")) embargoed_foia.demote_editor(editor) nose.tools.assert_false(embargoed_foia.has_perm(editor, "change"))
def test_promote_viewer(self): """Editors should be able to promote viewers to editors.""" embargoed_foia = FOIARequestFactory(embargo=True) viewer = UserFactory() embargoed_foia.add_viewer(viewer) nose.tools.assert_true(embargoed_foia.has_perm(viewer, "view")) nose.tools.assert_false(embargoed_foia.has_perm(viewer, "change")) embargoed_foia.promote_viewer(viewer) nose.tools.assert_true(embargoed_foia.has_perm(viewer, "change"))
class TestFOIARequestAppeal(RunCommitHooksMixin, TestCase): """A request should be able to send an appeal to the agency that receives them.""" def setUp(self): self.appeal_agency = AppealAgencyFactory() self.agency = AgencyFactory(status='approved', appeal_agency=self.appeal_agency) self.foia = FOIARequestFactory(agency=self.agency, status='rejected') def test_appeal(self): """Sending an appeal to the agency should require the message for the appeal, which is then turned into a communication to the correct agency. In this case, the correct agency is the same one that received the message.""" ok_(self.foia.has_perm(self.foia.user, 'appeal'), 'The request should be appealable.') ok_(self.agency and self.agency.status == 'approved', 'The agency should be approved.') ok_(self.appeal_agency.get_emails('appeal'), 'The appeal agency should accept email.') # Create the appeal message and submit it appeal_message = 'Lorem ipsum' appeal_comm = self.foia.appeal(appeal_message, self.foia.user) # Check that everything happened like we expected self.foia.refresh_from_db() appeal_comm.refresh_from_db() self.run_commit_hooks() eq_(self.foia.email, self.appeal_agency.get_emails('appeal').first()) eq_(self.foia.status, 'appealing') eq_(appeal_comm.communication, appeal_message) eq_(appeal_comm.from_user, self.foia.user) eq_(appeal_comm.to_user, self.agency.get_user()) ok_(appeal_comm.emails.exists()) def test_mailed_appeal(self): """Sending an appeal to an agency via mail should set the request to 'submitted', create a snail mail task with the 'a' category, and set the appeal communication delivery method to 'mail'.""" # Make the appeal agency unreceptive to emails self.appeal_agency.emails.clear() # Create the appeal message and submit it appeal_message = 'Lorem ipsum' appeal_comm = self.foia.appeal(appeal_message, self.foia.user) # Check that everything happened like we expected self.foia.refresh_from_db() appeal_comm.refresh_from_db() self.run_commit_hooks() eq_( self.foia.status, 'submitted', 'The status of the request should be updated. Actually: %s' % self.foia.status) eq_( appeal_comm.communication, appeal_message, 'The appeal message parameter should be used as the body of the communication.' ) eq_(appeal_comm.from_user, self.foia.user, 'The appeal should be addressed from the request owner.') eq_(appeal_comm.to_user, self.agency.get_user(), 'The appeal should be addressed to the agency.') task = SnailMailTask.objects.get(communication=appeal_comm) ok_(task, 'A snail mail task should be created.') eq_(task.category, 'a')
class TestEmbargo(TestCase): """Embargoing a request hides it from public view.""" def setUp(self): self.user = ProfessionalUserFactory() self.foia = FOIARequestFactory(composer__user=self.user) self.request_factory = RequestFactory() self.url = self.foia.get_absolute_url() def get_response(self, request): """Utility function for calling the embargo view function""" request = mock_middleware(request) return embargo( request, self.foia.jurisdiction.slug, self.foia.jurisdiction.pk, self.foia.slug, self.foia.pk, ) def test_basic_embargo(self): """The embargo should be accepted if the owner can embargo and edit the request.""" ok_(self.foia.has_perm(self.user, 'change'), 'The request should be editable by the user.') ok_(self.foia.has_perm(self.user, 'embargo'), 'The user should be allowed to embargo.') ok_(self.foia.status not in END_STATUS, 'The request should not be closed.') data = {'embargo': 'create'} request = self.request_factory.post(self.url, data) request.user = self.user response = self.get_response(request) self.foia.refresh_from_db() eq_(response.status_code, 302) ok_(self.foia.embargo, 'An embargo should be set on the request.') def test_no_permission_to_edit(self): """Users without permission to edit the request should not be able to change the embargo""" user_without_permission = ProfessionalUserFactory() assert_false(self.foia.has_perm(user_without_permission, 'change')) data = {'embargo': 'create'} request = self.request_factory.post(self.url, data) request.user = user_without_permission response = self.get_response(request) self.foia.refresh_from_db() eq_(response.status_code, 302) ok_(not self.foia.embargo, 'The embargo should not be set on the request.') def test_no_permission_to_embargo(self): """Users without permission to embargo the request should not be allowed to do so.""" user_without_permission = UserFactory() self.foia.composer.user = user_without_permission self.foia.composer.save() ok_(self.foia.has_perm(user_without_permission, 'change')) assert_false(self.foia.has_perm(user_without_permission, 'embargo')) data = {'embargo': 'create'} request = self.request_factory.post(self.url, data) request.user = user_without_permission response = self.get_response(request) self.foia.refresh_from_db() eq_(response.status_code, 302) ok_(not self.foia.embargo, 'The embargo should not be set on the request.') def test_unembargo(self): """ The embargo should be removable by editors of the request. Any user should be allowed to remove an embargo, even if they cannot apply one. """ user_without_permission = UserFactory() self.foia.composer.user = user_without_permission self.foia.composer.save() self.foia.embargo = True self.foia.save() assert_true(self.foia.embargo) assert_true(self.foia.has_perm(user_without_permission, 'change')) assert_false(self.foia.has_perm(user_without_permission, 'embargo')) data = {'embargo': 'delete'} request = self.request_factory.post(self.url, data) request.user = user_without_permission response = self.get_response(request) self.foia.refresh_from_db() eq_(response.status_code, 302) assert_false(self.foia.embargo, 'The embargo should be removed from the request.') def _test_embargo_details(self): """ If the request is in a closed state, it needs a date to be applied. If the user has permission, apply a permanent embargo. """ self.foia.status = 'rejected' self.foia.save() default_expiration_date = datetime.date.today() + datetime.timedelta(1) embargo_form = FOIAEmbargoForm({ 'permanent_embargo': True, 'date_embargo': default_expiration_date }) assert_true(embargo_form.is_valid(), 'Form should validate.') assert_true(self.foia.has_perm(self.user, 'embargo_perm')) data = {'embargo': 'create'} data.update(embargo_form.data) request = self.request_factory.post(self.url, data) request.user = self.user response = self.get_response(request) self.foia.refresh_from_db() eq_(response.status_code, 302) assert_true(self.foia.embargo, 'An embargo should be set on the request.') eq_(self.foia.date_embargo, default_expiration_date, 'An expiration date should be set on the request.') assert_true(self.foia.permanent_embargo, 'A permanent embargo should be set on the request.') def test_cannot_permanent_embargo(self): """Users who cannot set permanent embargoes shouldn't be able to.""" user_without_permission = ProfessionalUserFactory() self.foia.composer.user = user_without_permission self.foia.composer.save() assert_true(self.foia.has_perm(user_without_permission, 'embargo')) assert_false( self.foia.has_perm(user_without_permission, 'embargo_perm')) assert_true(self.foia.has_perm(user_without_permission, 'change')) embargo_form = FOIAEmbargoForm({'permanent_embargo': True}) assert_true(embargo_form.is_valid(), 'Form should validate.') data = {'embargo': 'create'} data.update(embargo_form.data) request = self.request_factory.post(self.url, data) request.user = self.foia.composer.user response = self.get_response(request) self.foia.refresh_from_db() eq_(response.status_code, 302) assert_true(self.foia.embargo, 'An embargo should be set on the request.') assert_false(self.foia.permanent_embargo, 'A permanent embargo should not be set on the request.') def test_update_embargo(self): """The embargo should be able to be updated.""" self.foia.embargo = True self.foia.embargo_permanent = True self.foia.date_embargo = datetime.date.today() + datetime.timedelta(5) self.foia.status = 'rejected' self.foia.save() self.foia.refresh_from_db() assert_true(self.foia.embargo) expiration = datetime.date.today() + datetime.timedelta(15) embargo_form = FOIAEmbargoForm({'date_embargo': expiration}) data = {'embargo': 'update'} data.update(embargo_form.data) request = self.request_factory.post(self.url, data) request.user = self.user response = self.get_response(request) eq_(response.status_code, 302) self.foia.refresh_from_db() assert_true(self.foia.embargo, 'The embargo should stay applied to the request.') assert_false(self.foia.permanent_embargo, 'The permanent embargo should be repealed.') eq_(self.foia.date_embargo, expiration, 'The embargo expiration date should be updated.') def test_expire_embargo(self): """Any requests with an embargo date before today should be unembargoed""" self.foia.embargo = True self.foia.date_embargo = datetime.date.today() - datetime.timedelta(1) self.foia.status = 'rejected' self.foia.save() embargo_expire() self.foia.refresh_from_db() assert_false(self.foia.embargo, 'The embargo should be repealed.') def test_do_not_expire_permanent(self): """A request with a permanent embargo should stay embargoed.""" self.foia.embargo = True self.foia.permanent_embargo = True self.foia.date_embargo = datetime.date.today() - datetime.timedelta(1) self.foia.status = 'rejected' self.foia.save() embargo_expire() self.foia.refresh_from_db() assert_true(self.foia.embargo, 'The embargo should remain embargoed.') def test_do_not_expire_no_date(self): """A request without an expiration date should not expire.""" self.foia.embargo = True self.foia.save() embargo_expire() self.foia.refresh_from_db() assert_true(self.foia.embargo, 'The embargo should remain embargoed.') def test_expire_after_date(self): """Only after the date_embargo passes should the embargo expire.""" self.foia.embargo = True self.foia.date_embargo = datetime.date.today() self.foia.status = 'rejected' self.foia.save() embargo_expire() self.foia.refresh_from_db() assert_true(self.foia.embargo, 'The embargo should remain embargoed.') def test_set_date_on_status_change(self): """ If the request status is changed to an end status and it is embargoed, set the embargo expiration date to 30 days from today. """ default_expiration_date = datetime.date.today() + datetime.timedelta( 30) self.foia.embargo = True self.foia.save() self.foia.status = 'rejected' self.foia.save() self.foia.refresh_from_db() assert_true(self.foia.embargo and self.foia.status in END_STATUS) eq_(self.foia.date_embargo, default_expiration_date, 'The embargo should be given an expiration date.') def test_set_date_exception(self): """ If the request is changed to an inactive state, it is embargoed, and there is no previously set expiration date, then set the embargo expiration to its default value. """ extended_expiration_date = datetime.date.today() + datetime.timedelta( 15) self.foia.embargo = True self.foia.date_embargo = extended_expiration_date self.foia.status = 'rejected' self.foia.save() self.foia.refresh_from_db() assert_true(self.foia.embargo and self.foia.status in END_STATUS) eq_(self.foia.date_embargo, extended_expiration_date, 'The embargo should not change the extended expiration date.') def test_remove_date(self): """The embargo date should be removed if the request is changed to an active state.""" default_expiration_date = datetime.date.today() + datetime.timedelta( 30) self.foia.embargo = True self.foia.save() self.foia.status = 'rejected' self.foia.save() self.foia.refresh_from_db() assert_true(self.foia.embargo and self.foia.status in END_STATUS) eq_(self.foia.date_embargo, default_expiration_date, 'The embargo should be given an expiration date.') self.foia.status = 'appealing' self.foia.save() self.foia.refresh_from_db() assert_false(self.foia.embargo and self.foia.status in END_STATUS) ok_(not self.foia.date_embargo, 'The embargo date should be removed.')
class TestRequestSharing(TestCase): """Allow people to edit and view another user's request.""" def setUp(self): self.foia = FOIARequestFactory() self.editor = UserFactory() self.creator = self.foia.user def test_add_editor(self): """Editors should be able to add editors to the request.""" new_editor = self.editor self.foia.add_editor(new_editor) nose.tools.assert_true(self.foia.has_editor(new_editor)) def test_remove_editor(self): """Editors should be able to remove editors from the request.""" editor_to_remove = self.editor # first we add the editor, otherwise we would have nothing to remove! self.foia.add_editor(editor_to_remove) nose.tools.assert_true(self.foia.has_editor(editor_to_remove)) # now we remove the editor we just added self.foia.remove_editor(editor_to_remove) nose.tools.assert_false(self.foia.has_editor(editor_to_remove)) def test_editor_permission(self): """Editors should have the same abilities and permissions as creators.""" new_editor = self.editor self.foia.add_editor(new_editor) nose.tools.ok_(self.foia.has_perm(new_editor, "change")) def test_add_viewer(self): """Editors should be able to add viewers to the request.""" new_viewer = UserFactory() self.foia.add_viewer(new_viewer) nose.tools.ok_(self.foia.has_viewer(new_viewer)) def test_remove_viewer(self): """Editors should be able to remove viewers from the request.""" viewer_to_remove = UserFactory() # first we add the viewer, otherwise we would have nothing to remove! self.foia.add_viewer(viewer_to_remove) nose.tools.ok_(self.foia.has_viewer(viewer_to_remove)) # now we remove the viewer we just added self.foia.remove_viewer(viewer_to_remove) nose.tools.assert_false(self.foia.has_viewer(viewer_to_remove)) def test_viewer_permission(self): """Viewers should be able to see the request if it is embargoed.""" embargoed_foia = FOIARequestFactory(embargo=True) viewer = UserFactory() normie = UserFactory() embargoed_foia.add_viewer(viewer) nose.tools.assert_true(embargoed_foia.has_perm(viewer, "view")) nose.tools.assert_false(embargoed_foia.has_perm(normie, "view")) def test_promote_viewer(self): """Editors should be able to promote viewers to editors.""" embargoed_foia = FOIARequestFactory(embargo=True) viewer = UserFactory() embargoed_foia.add_viewer(viewer) nose.tools.assert_true(embargoed_foia.has_perm(viewer, "view")) nose.tools.assert_false(embargoed_foia.has_perm(viewer, "change")) embargoed_foia.promote_viewer(viewer) nose.tools.assert_true(embargoed_foia.has_perm(viewer, "change")) def test_demote_editor(self): """Editors should be able to demote editors to viewers.""" embargoed_foia = FOIARequestFactory(embargo=True) editor = UserFactory() embargoed_foia.add_editor(editor) nose.tools.assert_true(embargoed_foia.has_perm(editor, "view")) nose.tools.assert_true(embargoed_foia.has_perm(editor, "change")) embargoed_foia.demote_editor(editor) nose.tools.assert_false(embargoed_foia.has_perm(editor, "change")) def test_access_key(self): """Editors should be able to generate a secure access key to view an embargoed request.""" embargoed_foia = FOIARequestFactory(embargo=True) access_key = embargoed_foia.generate_access_key() nose.tools.assert_true( access_key == embargoed_foia.access_key, "The key in the URL should match the key saved to the request.", ) embargoed_foia.generate_access_key() nose.tools.assert_false( access_key == embargoed_foia.access_key, "After regenerating the link, the key should no longer match.", ) def test_do_not_grant_creator_access(self): """Creators should not be granted access as editors or viewers""" self.foia.add_editor(self.creator) nose.tools.assert_false(self.foia.has_editor(self.creator)) self.foia.add_viewer(self.creator) nose.tools.assert_false(self.foia.has_viewer(self.creator)) # but the creator should still be able to both view and edit! nose.tools.assert_true(self.foia.has_perm(self.creator, "change")) nose.tools.assert_true(self.foia.has_perm(self.creator, "view"))
class TestRequestDetailView(TestCase): """Request detail views support a wide variety of interactions""" def setUp(self): agency = AgencyFactory(appeal_agency=AppealAgencyFactory()) self.foia = FOIARequestFactory(agency=agency) self.view = Detail.as_view() self.url = self.foia.get_absolute_url() self.kwargs = { 'jurisdiction': self.foia.jurisdiction.slug, 'jidx': self.foia.jurisdiction.id, 'slug': self.foia.slug, 'idx': self.foia.id } UserFactory(username='******') def test_add_tags(self): """Posting a collection of tags to a request should update its tags.""" data = {'action': 'tags', 'tags': 'foo, bar'} http_post_response( self.url, self.view, data, self.foia.user, **self.kwargs ) self.foia.refresh_from_db() ok_('foo' in [tag.name for tag in self.foia.tags.all()]) ok_('bar' in [tag.name for tag in self.foia.tags.all()]) def test_add_projects(self): """Posting a collection of projects to a request should add it to those projects.""" project = ProjectFactory() project.contributors.add(self.foia.user) form = ProjectManagerForm( { 'projects': [project.pk] }, user=self.foia.user, ) ok_(form.is_valid()) data = {'action': 'projects'} data.update(form.data) http_post_response( self.url, self.view, data, self.foia.user, **self.kwargs ) project.refresh_from_db() ok_(self.foia in project.requests.all()) def test_appeal(self): """Appealing a request should send a new communication, record the details of the appeal, and update the status of the request.""" comm_count = self.foia.communications.count() data = {'action': 'appeal', 'text': 'Lorem ipsum'} http_post_response( self.url, self.view, data, self.foia.user, **self.kwargs ) self.foia.refresh_from_db() eq_(self.foia.status, 'appealing') eq_(self.foia.communications.count(), comm_count + 1) eq_( self.foia.communications.last().communication, data['text'], 'The appeal should use the language provided by the user.' ) appeal = Appeal.objects.last() ok_(appeal, 'An Appeal object should be created.') eq_( self.foia.communications.last(), appeal.communication, 'The appeal should reference the communication that was created.' ) def test_appeal_example(self): """If an example appeal is used to base the appeal off of, then the examples should be recorded to the appeal object as well.""" example_appeal = ExampleAppealFactory() data = { 'action': 'appeal', 'text': 'Lorem ipsum', 'base_language': example_appeal.pk } http_post_response( self.url, self.view, data, self.foia.user, **self.kwargs ) self.foia.refresh_from_db() appeal = Appeal.objects.last() ok_(appeal.base_language, 'The appeal should record its base language.') ok_(appeal.base_language.count(), 1) def test_unauthorized_appeal(self): """Appealing a request without permission should not do anything.""" unauth_user = UserFactory() comm_count = self.foia.communications.count() previous_status = self.foia.status data = {'action': 'appeal', 'text': 'Lorem ipsum'} http_post_response( self.url, self.view, data, unauth_user, **self.kwargs ) self.foia.refresh_from_db() eq_( self.foia.status, previous_status, 'The status of the request should not be changed.' ) eq_( self.foia.communications.count(), comm_count, 'No communication should be added to the request.' ) def test_missing_appeal(self): """An appeal that is missing its language should not do anything.""" comm_count = self.foia.communications.count() previous_status = self.foia.status data = {'action': 'appeal', 'text': ''} http_post_response( self.url, self.view, data, self.foia.user, **self.kwargs ) self.foia.refresh_from_db() eq_( self.foia.status, previous_status, 'The status of the request should not be changed.' ) eq_( self.foia.communications.count(), comm_count, 'No communication should be added to the request.' ) def test_unappealable_request(self): """An appeal on a request that cannot be appealed should not do anything.""" self.foia.status = 'submitted' self.foia.save() nose.tools.assert_false(self.foia.has_perm(self.foia.user, 'appeal')) comm_count = self.foia.communications.count() previous_status = self.foia.status data = {'action': 'appeal', 'text': 'Lorem ipsum'} http_post_response( self.url, self.view, data, self.foia.user, **self.kwargs ) self.foia.refresh_from_db() eq_( self.foia.status, previous_status, 'The status of the request should not be changed.' ) eq_( self.foia.communications.count(), comm_count, 'No communication should be added to the request.' ) def test_post_status(self): """A user updating the status of their request should update the status, open a status change task, and close any open response tasks""" nose.tools.assert_not_equal(self.foia.status, 'done') eq_( len( StatusChangeTask.objects.filter( foia=self.foia, user=self.foia.user, resolved=False, ) ), 0 ) communication = FOIACommunicationFactory(foia=self.foia) response_task = ResponseTaskFactory( communication=communication, resolved=False, ) data = {'action': 'status', 'status': 'done'} http_post_response( self.url, self.view, data, self.foia.user, **self.kwargs ) self.foia.refresh_from_db() eq_(self.foia.status, 'done') eq_( len( StatusChangeTask.objects.filter( foia=self.foia, user=self.foia.user, resolved=False, ) ), 1 ) response_task.refresh_from_db() ok_(response_task.resolved)
class TestFOIARequestAppeal(TestCase): """A request should be able to send an appeal to the agency that receives them.""" def setUp(self): self.appeal_agency = AppealAgencyFactory() self.agency = AgencyFactory(status='approved', appeal_agency=self.appeal_agency) self.foia = FOIARequestFactory(agency=self.agency, status='rejected') def run_commit_hooks(self): """ Fake transaction commit to run delayed on_commit functions https://medium.com/gitux/speed-up-django-transaction-hooks-tests-6de4a558ef96 """ for db_name in reversed(self._databases_names()): with mock.patch( 'django.db.backends.base.base.BaseDatabaseWrapper.' 'validate_no_atomic_block', lambda a: False): transaction.get_connection( using=db_name, ).run_and_clear_commit_hooks() def test_appeal(self): """Sending an appeal to the agency should require the message for the appeal, which is then turned into a communication to the correct agency. In this case, the correct agency is the same one that received the message.""" ok_(self.foia.has_perm(self.foia.user, 'appeal'), 'The request should be appealable.') ok_(self.agency and self.agency.status == 'approved', 'The agency should be approved.') ok_(self.appeal_agency.get_emails('appeal'), 'The appeal agency should accept email.') # Create the appeal message and submit it appeal_message = 'Lorem ipsum' appeal_comm = self.foia.appeal(appeal_message, self.foia.user) # Check that everything happened like we expected self.foia.refresh_from_db() appeal_comm.refresh_from_db() self.run_commit_hooks() eq_(self.foia.email, self.appeal_agency.get_emails('appeal').first()) eq_(self.foia.status, 'appealing') eq_(appeal_comm.communication, appeal_message) eq_(appeal_comm.from_user, self.foia.user) eq_(appeal_comm.to_user, self.agency.get_user()) ok_(appeal_comm.emails.exists()) def test_mailed_appeal(self): """Sending an appeal to an agency via mail should set the request to 'submitted', create a snail mail task with the 'a' category, and set the appeal communication delivery method to 'mail'.""" # Make the appeal agency unreceptive to emails self.appeal_agency.emails.clear() # Create the appeal message and submit it appeal_message = 'Lorem ipsum' appeal_comm = self.foia.appeal(appeal_message, self.foia.user) # Check that everything happened like we expected self.foia.refresh_from_db() appeal_comm.refresh_from_db() eq_( self.foia.status, 'submitted', 'The status of the request should be updated. Actually: %s' % self.foia.status) eq_( appeal_comm.communication, appeal_message, 'The appeal message parameter should be used as the body of the communication.' ) eq_(appeal_comm.from_user, self.foia.user, 'The appeal should be addressed from the request owner.') eq_(appeal_comm.to_user, self.agency.get_user(), 'The appeal should be addressed to the agency.') task = SnailMailTask.objects.get(communication=appeal_comm) ok_(task, 'A snail mail task should be created.') eq_(task.category, 'a')
class TestRequestSharing(TestCase): """Allow people to edit and view another user's request.""" def setUp(self): self.foia = FOIARequestFactory() self.editor = UserFactory() self.creator = self.foia.user def test_add_editor(self): """Editors should be able to add editors to the request.""" new_editor = self.editor self.foia.add_editor(new_editor) assert_true(self.foia.has_editor(new_editor)) def test_remove_editor(self): """Editors should be able to remove editors from the request.""" editor_to_remove = self.editor # first we add the editor, otherwise we would have nothing to remove! self.foia.add_editor(editor_to_remove) assert_true(self.foia.has_editor(editor_to_remove)) # now we remove the editor we just added self.foia.remove_editor(editor_to_remove) assert_false(self.foia.has_editor(editor_to_remove)) def test_editor_permission(self): """Editors should have the same abilities and permissions as creators.""" new_editor = self.editor self.foia.add_editor(new_editor) ok_(self.foia.has_perm(new_editor, 'change')) def test_add_viewer(self): """Editors should be able to add viewers to the request.""" new_viewer = UserFactory() self.foia.add_viewer(new_viewer) ok_(self.foia.has_viewer(new_viewer)) def test_remove_viewer(self): """Editors should be able to remove viewers from the request.""" viewer_to_remove = UserFactory() # first we add the viewer, otherwise we would have nothing to remove! self.foia.add_viewer(viewer_to_remove) ok_(self.foia.has_viewer(viewer_to_remove)) # now we remove the viewer we just added self.foia.remove_viewer(viewer_to_remove) assert_false(self.foia.has_viewer(viewer_to_remove)) def test_viewer_permission(self): """Viewers should be able to see the request if it is embargoed.""" embargoed_foia = FOIARequestFactory(embargo=True) viewer = UserFactory() normie = UserFactory() embargoed_foia.add_viewer(viewer) assert_true(embargoed_foia.has_perm(viewer, 'view')) assert_false(embargoed_foia.has_perm(normie, 'view')) def test_promote_viewer(self): """Editors should be able to promote viewers to editors.""" embargoed_foia = FOIARequestFactory(embargo=True) viewer = UserFactory() embargoed_foia.add_viewer(viewer) assert_true(embargoed_foia.has_perm(viewer, 'view')) assert_false(embargoed_foia.has_perm(viewer, 'change')) embargoed_foia.promote_viewer(viewer) assert_true(embargoed_foia.has_perm(viewer, 'change')) def test_demote_editor(self): """Editors should be able to demote editors to viewers.""" embargoed_foia = FOIARequestFactory(embargo=True) editor = UserFactory() embargoed_foia.add_editor(editor) assert_true(embargoed_foia.has_perm(editor, 'view')) assert_true(embargoed_foia.has_perm(editor, 'change')) embargoed_foia.demote_editor(editor) assert_false(embargoed_foia.has_perm(editor, 'change')) def test_access_key(self): """Editors should be able to generate a secure access key to view an embargoed request.""" embargoed_foia = FOIARequestFactory(embargo=True) access_key = embargoed_foia.generate_access_key() assert_true( access_key == embargoed_foia.access_key, 'The key in the URL should match the key saved to the request.') embargoed_foia.generate_access_key() assert_false( access_key == embargoed_foia.access_key, 'After regenerating the link, the key should no longer match.') def test_creator_access(self): """Creators should not be granted access as editors or viewers""" self.foia.add_editor(self.creator) assert_false(self.foia.has_editor(self.creator)) self.foia.add_viewer(self.creator) assert_false(self.foia.has_viewer(self.creator)) # but the creator should still be able to both view and edit! assert_true(self.foia.has_perm(self.creator, 'view')) assert_true(self.foia.has_perm(self.creator, 'change')) def test_org_share(self): """Test sharing with your organization""" org = OrganizationFactory() user = UserFactory() MembershipFactory(user=user, organization=org, active=False) self.foia.embargo = True self.foia.composer.organization = org # fellow org member cannot view it before sharing is turned on assert_false(self.foia.has_perm(user, 'view')) self.creator.profile.org_share = True # now org member can view it assert_true(self.foia.has_perm(user, 'view')) # non-org member still cannot view it assert_false(self.foia.has_perm(self.editor, 'view'))
class TestRequestDetailView(TestCase): """Request detail views support a wide variety of interactions""" def setUp(self): agency = AgencyFactory(appeal_agency=AppealAgencyFactory()) self.foia = FOIARequestFactory(agency=agency) self.view = Detail.as_view() self.url = self.foia.get_absolute_url() self.kwargs = { "jurisdiction": self.foia.jurisdiction.slug, "jidx": self.foia.jurisdiction.id, "slug": self.foia.slug, "idx": self.foia.id, } UserFactory(username="******") def test_add_tags(self): """Posting a collection of tags to a request should update its tags.""" data = {"action": "tags", "tags": ["foo", "bar"]} http_post_response(self.url, self.view, data, self.foia.user, **self.kwargs) self.foia.refresh_from_db() ok_("foo" in [tag.name for tag in self.foia.tags.all()]) ok_("bar" in [tag.name for tag in self.foia.tags.all()]) def test_add_projects(self): """Posting a collection of projects to a request should add it to those projects.""" project = ProjectFactory() project.contributors.add(self.foia.user) form = ProjectManagerForm({"projects": [project.pk]}, user=self.foia.user) ok_(form.is_valid()) data = {"action": "projects"} data.update(form.data) http_post_response(self.url, self.view, data, self.foia.user, **self.kwargs) project.refresh_from_db() ok_(self.foia in project.requests.all()) def test_appeal(self): """Appealing a request should send a new communication, record the details of the appeal, and update the status of the request.""" comm_count = self.foia.communications.count() data = {"action": "appeal", "text": "Lorem ipsum"} http_post_response(self.url, self.view, data, self.foia.user, **self.kwargs) self.foia.refresh_from_db() eq_(self.foia.status, "appealing") eq_(self.foia.communications.count(), comm_count + 1) eq_( self.foia.communications.last().communication, data["text"], "The appeal should use the language provided by the user.", ) appeal = Appeal.objects.last() ok_(appeal, "An Appeal object should be created.") eq_( self.foia.communications.last(), appeal.communication, "The appeal should reference the communication that was created.", ) def test_appeal_example(self): """If an example appeal is used to base the appeal off of, then the examples should be recorded to the appeal object as well.""" example_appeal = ExampleAppealFactory() data = { "action": "appeal", "text": "Lorem ipsum", "base_language": example_appeal.pk, } http_post_response(self.url, self.view, data, self.foia.user, **self.kwargs) self.foia.refresh_from_db() appeal = Appeal.objects.last() ok_(appeal.base_language, "The appeal should record its base language.") ok_(appeal.base_language.count(), 1) def test_unauthorized_appeal(self): """Appealing a request without permission should not do anything.""" unauth_user = UserFactory() comm_count = self.foia.communications.count() previous_status = self.foia.status data = {"action": "appeal", "text": "Lorem ipsum"} http_post_response(self.url, self.view, data, unauth_user, **self.kwargs) self.foia.refresh_from_db() eq_( self.foia.status, previous_status, "The status of the request should not be changed.", ) eq_( self.foia.communications.count(), comm_count, "No communication should be added to the request.", ) def test_missing_appeal(self): """An appeal that is missing its language should not do anything.""" comm_count = self.foia.communications.count() previous_status = self.foia.status data = {"action": "appeal", "text": ""} http_post_response(self.url, self.view, data, self.foia.user, **self.kwargs) self.foia.refresh_from_db() eq_( self.foia.status, previous_status, "The status of the request should not be changed.", ) eq_( self.foia.communications.count(), comm_count, "No communication should be added to the request.", ) def test_unappealable_request(self): """An appeal on a request that cannot be appealed should not do anything.""" self.foia.status = "submitted" self.foia.save() nose.tools.assert_false(self.foia.has_perm(self.foia.user, "appeal")) comm_count = self.foia.communications.count() previous_status = self.foia.status data = {"action": "appeal", "text": "Lorem ipsum"} http_post_response(self.url, self.view, data, self.foia.user, **self.kwargs) self.foia.refresh_from_db() eq_( self.foia.status, previous_status, "The status of the request should not be changed.", ) eq_( self.foia.communications.count(), comm_count, "No communication should be added to the request.", ) def test_post_status(self): """A user updating the status of their request should update the status, open a status change task, and close any open response tasks""" nose.tools.assert_not_equal(self.foia.status, "done") eq_( len( StatusChangeTask.objects.filter(foia=self.foia, user=self.foia.user, resolved=False)), 0, ) communication = FOIACommunicationFactory(foia=self.foia) response_task = ResponseTaskFactory(communication=communication, resolved=False) data = {"action": "status", "status": "done"} http_post_response(self.url, self.view, data, self.foia.user, **self.kwargs) self.foia.refresh_from_db() eq_(self.foia.status, "done") eq_( len( StatusChangeTask.objects.filter(foia=self.foia, user=self.foia.user, resolved=False)), 1, ) response_task.refresh_from_db() ok_(response_task.resolved)