Beispiel #1
0
 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"))
Beispiel #2
0
 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"))
Beispiel #3
0
 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"))
Beispiel #4
0
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')
Beispiel #5
0
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.')
Beispiel #6
0
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"))
Beispiel #7
0
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')
Beispiel #9
0
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'))
Beispiel #10
0
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)