Skip to content
test_enrollment.py 77.4 KiB
Newer Older
    def test_edit_participation_view_save_integrity_error(self):
        with mock.patch(
                "course.enrollment.Participation.save"
        ) as mock_participation_save, mock.patch(
            "course.enrollment.EditParticipationForm.save"
        ) as mock_form_save:
            from django.db import IntegrityError
            mock_participation_save.side_effect = IntegrityError("my_error")
            mock_form_save.side_effect = IntegrityError("my_error")

            with self.temporarily_switch_to_user(
                    self.instructor_participation.user):
                resp = self.c.post(
                    self.my_participation_edit_url,
                    self.get_edit_participation_form_data("deny"))

            self.assertEqual(resp.status_code, 200)
            self.assertEqual(
                self.get_participation_count_by_status(p_status.requested),
                1)
            expected_error_msg = (
                "A data integrity issue was detected when saving "
                "this participation. Maybe a participation for "
                "this user already exists? (my_error)")
            self.assertResponseMessagesEqual(resp, [expected_error_msg])
            self.assertEqual(len(mail.outbox), 0)


class EnrollmentPreapprovalTestMixin(LocmemBackendTestsMixin,
                                     EnrollmentTestBaseMixin):

    @classmethod
    def setUpTestData(cls):  # noqa
        super(EnrollmentPreapprovalTestMixin, cls).setUpTestData()
Dong Zhuang's avatar
Dong Zhuang committed
        cls.non_ptcp_active_user1.institutional_id_verified = True
        cls.non_ptcp_active_user1.save()
        cls.non_ptcp_active_user2.institutional_id_verified = False
        cls.non_ptcp_active_user2.save()

    @property
    def preapprove_data_emails(self):
Dong Zhuang's avatar
Dong Zhuang committed
        preapproved_user = [self.non_ptcp_active_user1,
                            self.non_ptcp_active_user2]
        preapproved_data = [u.email for u in preapproved_user]
        preapproved_data.insert(1, "  ")  # empty line
        preapproved_data.insert(0, "  ")  # empty line
        return preapproved_data

    @property
    def preapprove_data_institutional_ids(self):
Dong Zhuang's avatar
Dong Zhuang committed
        preapproved_user = [self.non_ptcp_active_user1,
                            self.non_ptcp_active_user2,
                            self.non_ptcp_unconfirmed_user1]
        preapproved_data = [u.institutional_id for u in preapproved_user]
        preapproved_data.insert(1, "  ")  # empty line
        preapproved_data.insert(0, "  ")  # empty line
        return preapproved_data

    @property
    def preapproval_url(self):
        return reverse("relate-create_preapprovals",
                            args=[self.course.identifier])

    @property
    def default_preapprove_role(self):
        role, _ = (ParticipationRole.objects.get_or_create(
            course=self.course, identifier="student"))
        return [str(role.pk)]

    def post_preapproval(self, preapproval_type, preapproval_data=None,
Dong Zhuang's avatar
Dong Zhuang committed
                         force_loggin_instructor=True):
        if preapproval_data is None:
            if preapproval_type == "email":
                preapproval_data = self.preapprove_data_emails
            elif preapproval_type == "institutional_id":
                preapproval_data = self.preapprove_data_institutional_ids

        assert preapproval_data is not None
        assert isinstance(preapproval_data, list)

        data = {
            "preapproval_type": [preapproval_type],
            "preapproval_data": ["\n".join(preapproval_data)],
            "roles": self.student_role_post_data,
            "submit": [""]
        }
Dong Zhuang's avatar
Dong Zhuang committed
        if not force_loggin_instructor:
            approver = self.get_logged_in_user()
        else:
            approver = self.instructor_participation.user
        with self.temporarily_switch_to_user(approver):
            return self.c.post(self.preapproval_url, data, follow=True)

    def get_preapproval_count(self):
        return ParticipationPreapproval.objects.all().count()


class CreatePreapprovalsTest(EnrollmentTestMixin,
                             SingleCourseTestMixin, TestCase):
    # test enrollment.create_preapprovals
    @classmethod
    def setUpTestData(cls):  # noqa
        super(CreatePreapprovalsTest, cls).setUpTestData()
        cls.course.enrollment_approval_required = True
        cls.course.preapproval_require_verified_inst_id = True
        cls.course.save()

    def setUp(self):
        super(CreatePreapprovalsTest, self).setUp()
        fake_send_enrollment_decision = mock.patch(
            "course.enrollment.send_enrollment_decision")
        self.mock_send_enrollment_decision = (
            fake_send_enrollment_decision.start())
        self.addCleanup(fake_send_enrollment_decision.stop)

    def post_preapproval(
            self, preapproval_type, data, force_loggin_instructor=True):

        data = {
            "preapproval_type": [preapproval_type],
            "preapproval_data": data,
            "roles": self.student_role_post_data,
            "submit": [""]
        }
        if not force_loggin_instructor:
            approver = self.get_logged_in_user()
        else:
            approver = self.instructor_participation.user
        with self.temporarily_switch_to_user(approver):
            return self.c.post(self.preapproval_url, data, follow=True)

    def test_no_permission(self):
        with self.temporarily_switch_to_user(self.student_participation.user):
            resp = self.c.get(self.preapproval_url)
            self.assertEqual(resp.status_code, 403)
            resp = self.c.post(self.preapproval_url, data={})
            self.assertEqual(resp.status_code, 403)

    def test_login_required(self):
        with self.temporarily_switch_to_user(None):
            resp = self.c.get(self.preapproval_url)
            self.assertEqual(resp.status_code, 302)
            resp = self.c.post(self.preapproval_url, data={})
            self.assertEqual(resp.status_code, 302)
    def test_get(self):
Dong Zhuang's avatar
Dong Zhuang committed
        with self.temporarily_switch_to_user(self.instructor_participation.user):
            resp = self.c.get(self.preapproval_url)
            self.assertEqual(resp.status_code, 200)
Dong Zhuang's avatar
Dong Zhuang committed

    def test_post_form_not_valid(self):
        resp = self.post_preapproval("email", {})
        self.assertEqual(resp.status_code, 200)

        resp = self.post_preapproval("some_type", {})
        self.assertEqual(resp.status_code, 200)

        resp = self.post_preapproval("institutional_id", {})
        self.assertEqual(resp.status_code, 200)
    def test_create_preapproval_email(self):
        approval_data = "abc@foo.com\n  \n cde@foo.com\n  \n"
        resp = self.post_preapproval(
            "email",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 2)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 2,
                 'n_exist': 0,
                 'n_requested_approved': 0

        # repost same data
        resp = self.post_preapproval(
            "email",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 2)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 0,
                 'n_exist': 2,
                 'n_requested_approved': 0
             }])

    def test_create_preapproval_email_handle_pendinng(self):
        user = factories.UserFactory()
        factories.ParticipationFactory(
            course=self.course, user=user, status=p_status.requested
        approval_data = "%s\n  \n cde@foo.com\n  \n" % user.email.upper()
        resp = self.post_preapproval(
            "email",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 2)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 2,
                 'n_exist': 0,
                 'n_requested_approved': 1
             }])
        self.assertEqual(
            self.mock_send_enrollment_decision.call_count, 1)
        self.mock_send_enrollment_decision.reset_mock()

        # repost same data
        resp = self.post_preapproval(
            "email",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 2)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 0,
                 'n_exist': 2,
                 'n_requested_approved': 0
             }])
        self.assertEqual(
            self.mock_send_enrollment_decision.call_count, 0)
    def test_create_preapproval_inst_id(self):
        approval_data = "abc\n  \ncde \n  \n"
        resp = self.post_preapproval(
Dong Zhuang's avatar
Dong Zhuang committed
            "institutional_id",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 2)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 2,
                 'n_exist': 0,
                 'n_requested_approved': 0

        # repost same data
        resp = self.post_preapproval(
Dong Zhuang's avatar
Dong Zhuang committed
            "institutional_id",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 2)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 0,
                 'n_exist': 2,
                 'n_requested_approved': 0
    def test_create_preapproval_inst_id_handle_pending_require_verified(self):
        self.course.preapproval_require_verified_inst_id = False
        self.course.save()
        user1 = factories.UserFactory(institutional_id_verified=True)
        user2 = factories.UserFactory(institutional_id_verified=False)
        factories.ParticipationFactory(
            course=self.course, user=user1, status=p_status.requested)
        factories.ParticipationFactory(
            course=self.course, user=user2, status=p_status.requested)
        approval_data = "%s\n  \ncde \n  %s\n" % (
            user1.institutional_id.upper(), user2.institutional_id)

        resp = self.post_preapproval(
            "institutional_id",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
Dong Zhuang's avatar
Dong Zhuang committed

        self.assertEqual(
            self.get_preapproval_count(), 3)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 3,
                 'n_exist': 0,
                 'n_requested_approved': 2
             }])

        self.assertEqual(
            self.mock_send_enrollment_decision.call_count, 2)
        self.mock_send_enrollment_decision.reset_mock()
Dong Zhuang's avatar
Dong Zhuang committed

        # repost same data
        resp = self.post_preapproval(
Dong Zhuang's avatar
Dong Zhuang committed
            "institutional_id",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 3)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 0,
                 'n_exist': 3,
                 'n_requested_approved': 0
             }])
        self.assertEqual(
            self.mock_send_enrollment_decision.call_count, 0)

    def test_create_preapproval_inst_id_handle_pending(self):
        user1 = factories.UserFactory(institutional_id_verified=True)
        user2 = factories.UserFactory(institutional_id_verified=False)
        factories.ParticipationFactory(
            course=self.course, user=user1, status=p_status.requested)
        factories.ParticipationFactory(
            course=self.course, user=user2, status=p_status.requested)
        approval_data = "%s\n  \ncde \n  %s\n" % (
            user1.institutional_id, user2.institutional_id)

        resp = self.post_preapproval(
            "institutional_id",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 3)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 3,
                 'n_exist': 0,
                 'n_requested_approved': 1
             }])
        self.assertEqual(
            self.mock_send_enrollment_decision.call_count, 1)
        self.mock_send_enrollment_decision.reset_mock()
Dong Zhuang's avatar
Dong Zhuang committed

        # repost same data
        resp = self.post_preapproval(
Dong Zhuang's avatar
Dong Zhuang committed
            "institutional_id",
            approval_data)
        self.assertRedirects(
            resp, self.course_page_url, fetch_redirect_response=False)
        self.assertEqual(
            self.get_preapproval_count(), 3)
        self.assertMockAddedMessagesCalledWith(
            [MESSAGE_BATCH_PREAPPROVED_RESULT_PATTERN
             % {
                 'n_created': 0,
                 'n_exist': 3,
                 'n_requested_approved': 0
             }])
        self.assertEqual(
            self.mock_send_enrollment_decision.call_count, 0)
        self.mock_send_enrollment_decision.reset_mock()

        # update_course
        self.course.preapproval_require_verified_inst_id = False
        self.course.save()
        # user2 is expected to be approved as active participation upon
        # course update.
        self.assertEqual(
            Participation.objects.get(user=user2).status, p_status.active)
        self.assertEqual(
            self.mock_send_enrollment_decision.call_count, 1)
        self.mock_send_enrollment_decision.reset_mock()


class EditParticipationFormTest(SingleCourseTestMixin, TestCase):
    # test enrollment.EditParticipationForm
    # (currently for cases not covered by other tests)
    # todo: full test

    def setUp(self):
        super(EditParticipationFormTest, self).setUp()
        rf = RequestFactory()
        self.request = rf.get(self.get_course_page_url())

    def get_form_submit_inputs(self, form):
        from crispy_forms.layout import Submit
        inputs = [
            (input.name, input.value) for input in form.helper.inputs
            if isinstance(input, Submit)
        ]
        names = list(dict(inputs).keys())
        values = list(dict(inputs).values())
        return names, values

    def get_pctx_by_participation(self, participation):
        self.request.user = participation.user

        from course.utils import CoursePageContext
        return CoursePageContext(self.request, self.course.identifier)

    def test_role_button_disabled(self):
        pctx = self.get_pctx_by_participation(self.ta_participation)
        form = enrollment.EditParticipationForm(
            add_new=False, pctx=pctx, instance=self.student_participation)
        self.assertTrue(form.fields["roles"].disabled)

    def test_role_button_not_disabled(self):
        pctx = self.get_pctx_by_participation(self.instructor_participation)
        form = enrollment.EditParticipationForm(
            add_new=False, pctx=pctx, instance=self.student_participation)
        self.assertFalse(form.fields["roles"].disabled)

    def test_drop_button_not_added_for_dropped_participation(self):
        dropped = factories.ParticipationFactory(
            course=self.course, status=p_status.dropped)

        pctx = self.get_pctx_by_participation(self.instructor_participation)
        form = enrollment.EditParticipationForm(
            add_new=False, pctx=pctx, instance=dropped)

        names, _ = self.get_form_submit_inputs(form)
        self.assertNotIn("drop", names)


class EnrollmentEmailConnectionsTestMixin(LocmemBackendTestsMixin):
    # Ensure request/decision mail will be sent with/without EmailConnection
    # settings. https://github.com/inducer/relate/pull/366
    courses_attributes_extra_list = [{"enrollment_approval_required": True}]

    email_connections = {
        "enroll": {
            'host': 'smtp.gmail.com',
            'username': 'blah@blah.com',
            'password': 'password',
            'port': 587,
            'use_tls': True,
        },
    }

    email_connections_none = {}
    enrollment_email_from = "enroll@example.com"
    robot_email_from = "robot@example.com"


# Todo: refactor email connections in all views
class EnrollmentRequestEmailConnectionsTest(
            EnrollmentEmailConnectionsTestMixin, EnrollmentTestBaseMixin, TestCase):

    def test_email_with_email_connections1(self):
        # with EMAIL_CONNECTIONS and ENROLLMENT_EMAIL_FROM configured
        with self.settings(
                EMAIL_CONNECTIONS=self.email_connections,
                ROBOT_EMAIL_FROM=self.robot_email_from,
                ENROLLMENT_EMAIL_FROM=self.enrollment_email_from):

            expected_from_email = settings.ENROLLMENT_EMAIL_FROM
Dong Zhuang's avatar
Dong Zhuang committed
            with self.temporarily_switch_to_user(self.non_ptcp_active_user1):
Dong Zhuang's avatar
Dong Zhuang committed
                self.c.post(self.enroll_request_url, follow=True)

            msg = mail.outbox[0]
            self.assertEqual(msg.from_email, expected_from_email)

    def test_email_with_email_connections3(self):
        # with neither EMAIL_CONNECTIONS nor ENROLLMENT_EMAIL_FROM configured
        with self.settings(
                EMAIL_CONNECTIONS=self.email_connections,
                ROBOT_EMAIL_FROM=self.robot_email_from):
            if hasattr(settings, ENROLLMENT_EMAIL_FROM):
                del settings.ENROLLMENT_EMAIL_FROM

            expected_from_email = settings.ROBOT_EMAIL_FROM

Dong Zhuang's avatar
Dong Zhuang committed
            with self.temporarily_switch_to_user(self.non_ptcp_active_user1):
Dong Zhuang's avatar
Dong Zhuang committed
                self.c.post(self.enroll_request_url, follow=True)

            msg = mail.outbox[0]
            self.assertEqual(msg.from_email, expected_from_email)


class EnrollmentDecisionEmailConnectionsTest(
        EnrollmentEmailConnectionsTestMixin, EnrollmentDecisionTestMixin, TestCase):

    # {{{ with EMAIL_CONNECTIONS and ENROLLMENT_EMAIL_FROM configured
    def test_email_with_email_connections1(self):
        with self.settings(
                RELATE_EMAIL_SMTP_ALLOW_NONAUTHORIZED_SENDER=False,
                EMAIL_CONNECTIONS=self.email_connections,
                ROBOT_EMAIL_FROM=self.robot_email_from,
                ENROLLMENT_EMAIL_FROM=self.enrollment_email_from):

            expected_from_email = settings.ENROLLMENT_EMAIL_FROM

Dong Zhuang's avatar
Dong Zhuang committed
            with self.temporarily_switch_to_user(
                    self.instructor_participation.user):
                self.c.post(
                    self.my_participation_edit_url,
                    self.get_edit_participation_form_data("approve"))

            msg = mail.outbox[0]
            self.assertEqual(msg.from_email, expected_from_email)

    def test_email_with_email_connections2(self):
        with self.settings(
                RELATE_EMAIL_SMTP_ALLOW_NONAUTHORIZED_SENDER=True,
                EMAIL_CONNECTIONS=self.email_connections,
                ROBOT_EMAIL_FROM=self.robot_email_from,
                ENROLLMENT_EMAIL_FROM=self.enrollment_email_from):

            expected_from_email = self.course.from_email

Dong Zhuang's avatar
Dong Zhuang committed
            with self.temporarily_switch_to_user(
                    self.instructor_participation.user):
                self.c.post(
                    self.my_participation_edit_url,
                    self.get_edit_participation_form_data("approve"))

            msg = mail.outbox[0]
            self.assertEqual(msg.from_email, expected_from_email)
    # }}}

    # {{{ with neither EMAIL_CONNECTIONS nor ENROLLMENT_EMAIL_FROM configured
    def test_email_with_email_connections3(self):
        with self.settings(
                RELATE_EMAIL_SMTP_ALLOW_NONAUTHORIZED_SENDER=False,
                ROBOT_EMAIL_FROM=self.robot_email_from):
            if hasattr(settings, EMAIL_CONNECTIONS):
                del settings.EMAIL_CONNECTIONS
            if hasattr(settings, ENROLLMENT_EMAIL_FROM):
                del settings.ENROLLMENT_EMAIL_FROM

            expected_from_email = settings.ROBOT_EMAIL_FROM

Dong Zhuang's avatar
Dong Zhuang committed
            with self.temporarily_switch_to_user(
                    self.instructor_participation.user):
                self.c.post(
                    self.my_participation_edit_url,
                    self.get_edit_participation_form_data("approve"))
Dong Zhuang's avatar
Dong Zhuang committed

            msg = mail.outbox[0]
            self.assertEqual(msg.from_email, expected_from_email)

    def test_email_with_email_connections4(self):
        with self.settings(
                RELATE_EMAIL_SMTP_ALLOW_NONAUTHORIZED_SENDER=True,
                ROBOT_EMAIL_FROM=self.robot_email_from):
            if hasattr(settings, EMAIL_CONNECTIONS):
                del settings.EMAIL_CONNECTIONS
            if hasattr(settings, ENROLLMENT_EMAIL_FROM):
                del settings.ENROLLMENT_EMAIL_FROM

            expected_from_email = self.course.from_email

Dong Zhuang's avatar
Dong Zhuang committed
            with self.temporarily_switch_to_user(
                    self.instructor_participation.user):
                self.c.post(self.my_participation_edit_url,
                            self.get_edit_participation_form_data("approve"))
            msg = mail.outbox[0]
            self.assertEqual(msg.from_email, expected_from_email)
    # }}}


Dong Zhuang's avatar
Dong Zhuang committed
class ParticipationQueryFormTest(unittest.TestCase):
    #test enrollment.ParticipationQueryForm
Dong Zhuang's avatar
Dong Zhuang committed
    def setUp(self):
        self.course = factories.CourseFactory()

    def test_form_valid(self):
        data = {
            "queries": "id:1234",
            "op": "apply_tag",
            "tag": "hello"}

        form = enrollment.ParticipationQueryForm(data=data)
        self.assertTrue(form.is_valid())

    def test_form_valid_no_tag(self):
        data = {
            "queries": "id:1234",
            "op": "drop"}

        form = enrollment.ParticipationQueryForm(data=data)
        self.assertTrue(form.is_valid())

    def test_form_tag_invalid(self):
        data = {
            "queries": "id:1234",
            "op": "apply_tag",
            "tag": "~hello~"}

        form = enrollment.ParticipationQueryForm(data=data)
        self.assertIn("Name contains invalid characters.", form.errors["tag"])

1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948

class QueryParticipationsTestMixin(SingleCoursePageTestMixin):
    def setUp(self):
        super(QueryParticipationsTestMixin, self).setUp()
        fake_add_message = mock.patch('course.enrollment.messages.add_message')
        self.mock_add_message = fake_add_message.start()
        self.addCleanup(fake_add_message.stop)

    @property
    def query_participation_url(self):
        return self.get_course_view_url(
            "relate-query_participations", self.course.identifier)

    @classmethod
    def setup_participation_data(cls):
        p_list = [factories.ParticipationFactory(
            course=cls.course,
            status=p_status.requested,
            tags=list(factories.ParticipationTagFactory.create_batch(
                size=2, course=cls.course)))]

        p_list.extend(factories.ParticipationFactory.create_batch(
            size=3, course=cls.course,
            tags=list(factories.ParticipationTagFactory.create_batch(
                size=2, course=cls.course))))

        return p_list

    def post_query_participation(self, queries, op=None, tag=None, apply=False,
                                 force_loggin_instructor=True):
        form_data = {"queries": queries}
        form_data["op"] = op or "apply_tag"
        form_data["tag"] = tag or ""
        if apply:
            form_data["apply"] = ""
        else:
            form_data["submit"] = ""

        if not force_loggin_instructor:
            u = self.get_logged_in_user()
        else:
            u = self.instructor_participation.user
        with self.temporarily_switch_to_user(u):
            return self.c.post(self.query_participation_url, data=form_data)


class QueryParticipationsParseQueryTest(QueryParticipationsTestMixin, TestCase):
    # test enrollment.query_participations for parse query
    @classmethod
    def setUpTestData(cls):  # noqa
        super(QueryParticipationsParseQueryTest, cls).setUpTestData()

        # Participations are created here should not be modified
        cls.participations = cls.setup_participation_data()

    def test_no_query_permission(self):
        with self.temporarily_switch_to_user(self.participations[0].user):
            resp = self.c.get(self.query_participation_url)
            self.assertEqual(resp.status_code, 403)
            resp = self.c.post(self.query_participation_url, data={})
            self.assertEqual(resp.status_code, 403)

    def test_with_view_participant_masked_profile_permission(self):
        with self.temporarily_switch_to_user(self.ta_participation.user):
            resp = self.c.get(self.query_participation_url)
            self.assertEqual(resp.status_code, 200)
            resp = self.c.post(self.query_participation_url, data={})
            self.assertEqual(resp.status_code, 200)

        from course.constants import participation_permission as pperm
        from course.models import ParticipationPermission
        pp = ParticipationPermission(
            participation=self.ta_participation,
            permission=pperm.view_participant_masked_profile)
        pp.save()

        with self.temporarily_switch_to_user(self.ta_participation.user):
            resp = self.c.get(self.query_participation_url)
            self.assertEqual(resp.status_code, 403)
            resp = self.c.post(self.query_participation_url, data={})
            self.assertEqual(resp.status_code, 403)

    def test_user_id_equal(self):
        queries = "id:%d" % self.participations[0].user.id

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_user_email_equal(self):
        queries = "email:%s" % self.participations[0].user.email

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_user_email_contains(self):
        queries = "email-contains:test_factory_"

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        result = resp.context["result"]
        self.assertSetEqual(set(result), set(self.participations))

    def test_username_equal(self):
        queries = "username:%s" % self.participations[0].user.username

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_username_contains(self):
        queries = "username-contains:testuser_"

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        result = resp.context["result"]
        self.assertSetEqual(set(result), set(self.participations))

    def test_inst_id_equal(self):
        queries = "institutional-id:%s" % (
            self.participations[0].user.institutional_id)

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_inst_id_contains(self):
        queries = "institutional-id-contains:institutional_id"

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        result = resp.context["result"]
        self.assertSetEqual(set(result), set(self.participations))

    def test_tagged(self):
        queries = "tagged:%s" % (
            self.participations[0].tags.all()[0].name)

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_tagged2(self):
        queries = "tagged:%s" % (
            self.participations[1].tags.all()[0].name)

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", self.participations[1:])

    def test_role(self):
        queries = "role:%s" % (
            self.participations[1].roles.all()[0].identifier)
        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        result = resp.context["result"]

        self.assertEqual(len(result), 5)

    def test_role_ta(self):
        queries = "role:teaching_assistant"
        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.ta_participation])

    def test_status(self):
        queries = "status:requested"

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_has_started_flow(self):
        with self.temporarily_switch_to_user(self.participations[1].user):
            self.start_flow(self.flow_id)
        with self.temporarily_switch_to_user(self.participations[2].user):
            self.start_flow(self.flow_id)

        queries = "has-started:%s" % self.flow_id

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[1], self.participations[2]])

    def test_has_submitted_flow(self):
        with self.temporarily_switch_to_user(self.participations[1].user):
            self.start_flow(self.flow_id)
        with self.temporarily_switch_to_user(self.participations[2].user):
            self.start_flow(self.flow_id)
            self.end_flow()

        queries = "has-submitted:%s" % self.flow_id

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)
        self.assertResponseContextEqual(
            resp, "result", [self.participations[2]])

    def test_and(self):
        queries = "role:student and status:requested"
        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)

        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_not(self):
        queries = "role:student not status:active"
        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)

        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_or(self):
        queries = "id:%d or status:requested" % self.participations[0].user.id
        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)

        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_parentheses(self):
        queries = "role:student and (email-contains:.com not status:active)"
        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)

        self.assertResponseContextEqual(
            resp, "result", [self.participations[0]])

    def test_multiple_line(self):
        queries = (
            "id:%s\n  \n  id:%s" % (
                self.participations[0].user.id, self.participations[2].user.id))

        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)

        self.assertResponseContextEqual(
            resp, "result", [self.participations[0], self.participations[2]])

    def test_input_not_valid(self):
        queries = "unknown:"
        resp = self.post_query_participation(queries)
        self.assertEqual(resp.status_code, 200)

        self.assertFormErrorLoose(resp, None)
        self.assertResponseContextEqual(
            resp, "result", None)

        self.assertEqual(self.mock_add_message.call_count, 1)
        self.assertIn(
            "Error in line 1: InvalidTokenError: at index 0: ...unknown:...",
            self.mock_add_message.call_args[0])

    # {{{ apply

    def test_apply_tag(self):
        p1 = factories.ParticipationFactory(
            course=self.course,
            user=factories.UserFactory(username="temp_user"))
        p2 = factories.ParticipationFactory(
            course=self.course,
            user=factories.UserFactory(username="temp_user2"))

        queries = "username:%s or username:%s" % (
            p1.user.username, p2.user.username)
        resp = self.post_query_participation(
            queries, apply=True, op="apply_tag", tag="temp_tag")
        self.assertEqual(resp.status_code, 200)

        p1.refresh_from_db()
        p2.refresh_from_db()
        self.assertEqual(p1.tags.all()[0].name, "temp_tag")
        self.assertEqual(p2.tags.all()[0].name, "temp_tag")
        self.assertEqual(self.mock_add_message.call_count, 1)
        self.assertIn(
            "Operation successful on 2 participations.",
            self.mock_add_message.call_args[0])

    def test_remove_tag(self):
        to_remove_tag = "to_remove"
        p1 = factories.ParticipationFactory(
            course=self.course,
            user=factories.UserFactory(username="temp_user"),
            tags=[to_remove_tag, "abcd"]
        )
        p2 = factories.ParticipationFactory(
            course=self.course,
            user=factories.UserFactory(username="temp_user2"),
            tags=[to_remove_tag, "cdef"])

        queries = "username:%s or username:%s" % (
            p1.user.username, p2.user.username)
        resp = self.post_query_participation(
            queries, apply=True, op="remove_tag", tag=to_remove_tag)
        self.assertEqual(resp.status_code, 200)

        p1.refresh_from_db()
        p2.refresh_from_db()
        self.assertEqual(p1.tags.all()[0].name, "abcd")
        self.assertEqual(p2.tags.all()[0].name, "cdef")

        self.assertEqual(self.mock_add_message.call_count, 1)
        self.assertIn(
            "Operation successful on 2 participations.",
            self.mock_add_message.call_args[0])

    def test_drop(self):
        to_remove_tag = "to_remove"
        p1 = factories.ParticipationFactory(
            course=self.course,
            user=factories.UserFactory(username="temp_user"),
            tags=[to_remove_tag, "abcd"])
        p2 = factories.ParticipationFactory(
            course=self.course,
            user=factories.UserFactory(username="temp_user2"),
            tags=[to_remove_tag, "cdef"])

        queries = "tagged:%s" % to_remove_tag
        resp = self.post_query_participation(
            queries, apply=True, op="drop")
        self.assertEqual(resp.status_code, 200)

        self.assertEqual(self.mock_add_message.call_count, 1)
        self.assertIn(
            "Operation successful on 2 participations.",
            self.mock_add_message.call_args[0])

        p1.refresh_from_db()
        p2.refresh_from_db()
        self.assertEqual(p1.status, p_status.dropped)
        self.assertEqual(p2.status, p_status.dropped)

    # }}}


# vim: foldmethod=marker