Beispiel #1
0
class RecordsViewTests(SiteMixin, TestCase):
    MOCK_USER_DATA = {
        "username": "******",
        "name": "Test User",
        "email": "*****@*****.**",
    }

    def setUp(self):
        super().setUp()
        dump_random_state()

        self.user = UserFactory(username=self.MOCK_USER_DATA["username"])
        self.orgs = [OrganizationFactory.create(name=name, site=self.site) for name in ["TestOrg1", "TestOrg2"]]
        self.course = CourseFactory.create(site=self.site)
        self.course_runs = CourseRunFactory.create_batch(2, course=self.course)
        self.program = ProgramFactory(
            title="TestProgram1", course_runs=self.course_runs, authoring_organizations=self.orgs, site=self.site
        )
        self.course_certs = [
            CourseCertificateFactory.create(
                course_id=course_run.key,
                site=self.site,
            )
            for course_run in self.course_runs
        ]
        self.program_cert = ProgramCertificateFactory.create(program_uuid=self.program.uuid, site=self.site)
        self.course_credential_content_type = ContentType.objects.get(
            app_label="credentials", model="coursecertificate"
        )
        self.program_credential_content_type = ContentType.objects.get(
            app_label="credentials", model="programcertificate"
        )
        self.course_user_credentials = [
            UserCredentialFactory.create(
                username=self.user.username,
                credential_content_type=self.course_credential_content_type,
                credential=course_cert,
            )
            for course_cert in self.course_certs
        ]
        self.program_user_credential = UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=self.program_cert,
        )

        self.client.login(username=self.user.username, password=USER_PASSWORD)

    def _render_records(self, program_data=None, status_code=200):
        """ Helper method to mock and render a user certificate."""
        if program_data is None:
            program_data = []

        with patch("credentials.apps.records.views.RecordsView._get_programs") as get_programs:
            get_programs.return_value = program_data
            response = self.client.get(reverse("records:index"))
            self.assertEqual(response.status_code, status_code)

        return response

    def assert_matching_template_origin(self, actual, expected_template_name):
        expected = select_template([expected_template_name])
        self.assertEqual(actual.origin, expected.origin)

    def test_no_anonymous_access(self):
        """ Verify that the view rejects non-logged-in users. """
        self.client.logout()
        response = self._render_records(status_code=302)
        self.assertRegex(response.url, "^/login/.*")

    def test_normal_access(self):
        """ Verify that the view works in default case. """
        response = self._render_records()
        response_context_data = response.context_data

        self.assertContains(response, "My Learner Records")

        actual_child_templates = response_context_data["child_templates"]
        self.assert_matching_template_origin(actual_child_templates["footer"], "_footer.html")
        self.assert_matching_template_origin(actual_child_templates["header"], "_header.html")
        self.assert_matching_template_origin(actual_child_templates["masquerade"], "_masquerade.html")

    def test_xss(self):
        """ Verify that the view protects against xss in translations. """
        response = self._render_records(
            [
                {
                    "name": "<xss>",
                    "partner": "XSS",
                    "uuid": "uuid",
                },
            ]
        )

        # Test that the data is parsed from an escaped string
        self.assertContains(
            response,
            "JSON.parse('[{"
            + "\\u0022name\\u0022: \\u0022\\u003Cxss\\u003E\\u0022, "
            + "\\u0022partner\\u0022: \\u0022XSS\\u0022, "
            + "\\u0022uuid\\u0022: \\u0022uuid\\u0022"
            + "}]')",
        )
        self.assertNotContains(response, "<xss>")

    def test_help_url(self):
        """ Verify that the records help url gets loaded into the context """
        response = self._render_records()
        response_context_data = response.context_data
        self.assertIn("records_help_url", response_context_data)
        self.assertNotEqual(response_context_data["records_help_url"], "")

    @ddt.data(
        (Program.ACTIVE, True),
        (Program.RETIRED, True),
        (Program.DELETED, False),
        (Program.UNPUBLISHED, False),
    )
    @ddt.unpack
    def test_completed_render_from_db(self, status, visible):
        """ Verify that a program cert that is completed is returned correctly, with different statuses """
        self.program.status = status
        self.program.save()

        response = self.client.get(reverse("records:index"))
        self.assertEqual(response.status_code, 200)
        program_data = json.loads(response.context_data["programs"])

        expected_program_data = [
            {
                "name": self.program.title,
                "partner": "TestOrg1, TestOrg2",
                "uuid": self.program.uuid.hex,
                "type": slugify(self.program.type),
                "completed": True,
                "empty": False,
            }
        ]
        self.assertEqual(program_data, expected_program_data if visible else [])

    def test_in_progress_from_db(self):
        """ Verify that no program cert, but course certs results in an In Progress program """
        # Delete the program
        self.program_cert.delete()
        response = self.client.get(reverse("records:index"))
        self.assertEqual(response.status_code, 200)
        program_data = json.loads(response.context_data["programs"])
        expected_program_data = [
            {
                "name": self.program.title,
                "partner": "TestOrg1, TestOrg2",
                "uuid": self.program.uuid.hex,
                "type": slugify(self.program.type),
                "completed": False,
                "empty": False,
            }
        ]
        self.assertEqual(program_data, expected_program_data)

    def test_not_visible_from_db(self):
        """ Test that the program's visible_date is considered """
        UserCredentialAttributeFactory(
            user_credential=self.program_user_credential,
            name="visible_date",
            value="9999-01-01T01:01:01Z",
        )
        response = self.client.get(reverse("records:index"))
        self.assertFalse(json.loads(response.context_data["programs"])[0]["completed"])

    def test_multiple_programs(self):
        """ Test that multiple programs can appear, in progress and completed """
        # Create a second program, and delete the first one's certificate
        new_course = CourseFactory.create(site=self.site)
        new_course_run = CourseRunFactory.create(course=new_course)

        new_program = ProgramFactory.create(
            title="ZTestProgram", course_runs=[new_course_run], authoring_organizations=self.orgs, site=self.site
        )
        new_course_cert = CourseCertificateFactory.create(course_id=new_course_run.key, site=self.site)
        new_program_cert = ProgramCertificateFactory.create(program_uuid=new_program.uuid, site=self.site)

        # Make a new user credential
        UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=new_course_cert,
        )
        # Make a new program credential
        UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=new_program_cert,
        )
        self.program_user_credential.delete()

        response = self.client.get(reverse("records:index"))
        self.assertEqual(response.status_code, 200)
        program_data = json.loads(response.context_data["programs"])
        expected_program_data = [
            {
                "name": self.program.title,
                "partner": "TestOrg1, TestOrg2",
                "uuid": self.program.uuid.hex,
                "type": slugify(self.program.type),
                "completed": False,
                "empty": False,
            },
            {
                "name": new_program.title,
                "partner": "TestOrg1, TestOrg2",
                "uuid": new_program.uuid.hex,
                "type": slugify(new_program.type),
                "completed": True,
                "empty": False,
            },
        ]
        self.assertEqual(program_data, expected_program_data)
Beispiel #2
0
class ProgramListingViewTests(SiteMixin, TestCase):
    MOCK_USER_DATA = {
        "username": "******",
        "name": "Test User",
        "email": "*****@*****.**",
    }

    def setUp(self):
        super().setUp()
        dump_random_state()

        self.user = UserFactory(username=self.MOCK_USER_DATA["username"], is_staff=True)
        self.orgs = [OrganizationFactory.create(name=name, site=self.site) for name in ["TestOrg1", "TestOrg2"]]
        self.course = CourseFactory.create(site=self.site)
        self.course_runs = CourseRunFactory.create_batch(2, course=self.course)
        self.program = ProgramFactory(
            title="TestProgram1", course_runs=self.course_runs, authoring_organizations=self.orgs, site=self.site
        )
        self.course_certs = [
            CourseCertificateFactory.create(
                course_id=course_run.key,
                site=self.site,
            )
            for course_run in self.course_runs
        ]
        self.program_cert = ProgramCertificateFactory.create(program_uuid=self.program.uuid, site=self.site)
        self.course_credential_content_type = ContentType.objects.get(
            app_label="credentials", model="coursecertificate"
        )
        self.program_credential_content_type = ContentType.objects.get(
            app_label="credentials", model="programcertificate"
        )
        self.course_user_credentials = [
            UserCredentialFactory.create(
                username=self.user.username,
                credential_content_type=self.course_credential_content_type,
                credential=course_cert,
            )
            for course_cert in self.course_certs
        ]
        self.program_user_credentials = UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=self.program_cert,
        )

        self.client.login(username=self.user.username, password=USER_PASSWORD)

    def _render_listing(self, expected_program_data=None, status_code=200):
        """ Helper method to mock and render a user certificate."""
        response = self.client.get(reverse("program_listing"))
        self.assertEqual(response.status_code, status_code)

        if expected_program_data is not None:
            program_data = json.loads(response.context_data["programs"])
            self.assertListEqual(program_data, expected_program_data)

        return response

    def _default_program_data(self, overrides=None):
        # if nothing is adjusted, this is the expected listing
        data = [
            {
                "name": self.program.title,
                "partner": "TestOrg1, TestOrg2",
                "uuid": self.program.uuid.hex,
                "type": slugify(self.program.type),
                "completed": True,
                "empty": False,
            },
        ]

        if overrides is not None:
            data[0].update(overrides)

        return data

    def _verify_normal_access(self):
        response = self._render_listing()
        response_context_data = response.context_data

        self.assertContains(response, "Program Listing View")

        actual_child_templates = response_context_data["child_templates"]
        self.assert_matching_template_origin(actual_child_templates["footer"], "_footer.html")
        self.assert_matching_template_origin(actual_child_templates["header"], "_header.html")
        self.assertNotIn("masquerade", actual_child_templates)  # no masquerading on this view

    def assert_matching_template_origin(self, actual, expected_template_name):
        expected = select_template([expected_template_name])
        self.assertEqual(actual.origin, expected.origin)

    def test_no_anonymous_access(self):
        """ Verify that the view rejects non-logged-in users. """
        self.client.logout()
        response = self._render_listing(status_code=302)
        self.assertRegex(response.url, "^/login/.*")

    def test_non_superuser_access(self):
        """ Verify that the view rejects non-superuser users. """
        self.user.is_superuser = False
        self.user.is_staff = False
        self.user.save()
        self._render_listing(status_code=404)

    def test_only_staff_access(self):
        """ Verify that the view rejects non-staff users. """
        self.user.is_staff = False
        self.user.save()
        self._render_listing(status_code=404)

    def test_normal_access_superuser(self):
        """ Verify that the view works with only superuser, no staff. """
        self.user.is_superuser = True
        self.user.is_staff = False
        self._verify_normal_access()

    def test_normal_access_as_staff(self):
        """ Verify that the view works in default case. Staff is set in the setup method."""
        self._verify_normal_access()

    @ddt.data(
        (Program.ACTIVE, True),
        (Program.RETIRED, False),  # this is different from RecordsView
        (Program.DELETED, False),
        (Program.UNPUBLISHED, False),
    )
    @ddt.unpack
    def test_completed_render_from_db(self, status, visible):
        """ Verify that a program cert that is completed is returned correctly, with different statuses """
        self.program.status = status
        self.program.save()

        data = self._default_program_data() if visible else []
        self._render_listing(expected_program_data=data)

    def test_in_progress_from_db(self):
        """ Verify that no program cert, but course certs results in an In Progress program """
        # Delete the program cert
        self.program_cert.delete()

        data = self._default_program_data(overrides={"completed": False})
        self._render_listing(expected_program_data=data)

    def test_empty_programs(self):
        """ Test that a program with no certs shows as empty """
        # Delete all certs
        for cert in self.course_certs:
            cert.delete()
        self.program_cert.delete()

        data = self._default_program_data(overrides={"completed": False, "empty": True})
        self._render_listing(expected_program_data=data)
Beispiel #3
0
class ProgramCertificateIssuedEmailTests(SiteMixin, TestCase):
    """
    Tests for the automated email sent to learners after completing an edX Program.

    Testing that the right configuration is used is done in `test_models.py` so this code
    will only verify that an email matching the configuration is sent.
    """

    USERNAME = "******"
    FAKE_PROGRAM_UUID = "f6551af4-aa5a-4089-801b-53485d0d1726"

    def setUp(self):
        super().setUp()
        self.user = UserFactory(username=self.USERNAME)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.program = None
        self.program_cert = None

        mail.outbox = []

        # Setup program and default email config
        self._setup_program_and_program_cert("Example Program")
        self.default_config = ProgramCompletionEmailConfiguration.objects.create(
            identifier="default",
            html_template="<h1>Default Template</h1>",
            plaintext_template="Default Template",
            enabled=True,
        )

    def _setup_program_and_program_cert(self, program_type):
        self.program = ProgramFactory(site=self.site)
        self.program.type = program_type
        self.program.type_slug = slugify(program_type)
        self.program.save()
        self.program_cert = ProgramCertificateFactory(
            site=self.site, program_uuid=self.program.uuid)

    def _build_expected_plaintext_email_body(self):
        return [
            "Congratulations on completing the {} {} Program!".format(
                self.program.title,
                self.program.type,
            ),
            "Sincerely,",
            "The {} Team".format(self.site.siteconfiguration.platform_name),
            textwrap.dedent(self.default_config.plaintext_template),
        ]

    def _build_expected_html_email_body(self):
        return [
            "Congratulations on completing the {} {} Program!".format(
                self.program.title, self.program.type),
            "Sincerely,<br/>The {} Team".format(
                self.site.siteconfiguration.platform_name),
            self.default_config.html_template,
        ]

    def _assert_email_contents(self):
        """
        Utility function that verifies the contents of the generated email
        """
        expected_plaintext_email_contents = self._build_expected_plaintext_email_body(
        )
        expected_html_email_contents = self._build_expected_html_email_body()

        self.assertEqual(1, len(mail.outbox))

        email = mail.outbox[0]
        plaintext_body = email.body
        html_body = email.alternatives[0][0]

        self.assertEqual(email.to[0], self.user.email)
        self._assert_subject(email.subject)
        self._assert_email_body_contents(plaintext_body,
                                         expected_plaintext_email_contents)
        self._assert_email_body_contents(html_body,
                                         expected_html_email_contents)

    def _assert_subject(self, email_subject):
        """
        Utility method that verifies the subject text of the automated emails being sent to
        learners.
        """
        expected_subject = "Congratulations for finishing your {} {} Program!".format(
            self.program.title, self.program.type)
        self.assertEqual(email_subject, expected_subject)

    def _assert_email_body_contents(self, email_body, fragments):
        """
        Utility method that verifies the content in the generated email is as expected.
        """
        for fragment in fragments:
            self.assertIn(fragment, email_body)

    def test_base_template(self):
        send_program_certificate_created_message(self.user.username,
                                                 self.program_cert)
        self._assert_email_contents()

    def test_no_config(self):
        """With the config deleted, it shouldn't send an email"""
        self.default_config.delete()
        send_program_certificate_created_message(self.user.username,
                                                 self.program_cert)
        self.assertEqual(0, len(mail.outbox))

    def test_disabled_config(self):
        """With the config disabled, it shouldn't send an email"""
        self.default_config.enabled = False
        self.default_config.save()
        send_program_certificate_created_message(self.user.username,
                                                 self.program_cert)
        self.assertEqual(0, len(mail.outbox))

    def test_retired_program(self):
        self.program.status = ProgramStatus.RETIRED.value
        self.program.save()
        send_program_certificate_created_message(self.user.username,
                                                 self.program_cert)
        self.assertEqual(0, len(mail.outbox))

    def test_send_email_exception_occurs(self):
        self._setup_program_and_program_cert("Radical Program")

        expected_messages = [
            "Sending Program completion email to learner with id [{}] in Program [{}]"
            .format(self.user.id, self.program.uuid),
            "Unable to send email to learner with id: [{}] for Program [{}]. Error occurred while attempting to "
            "format or send message: Error!".format(self.user.id,
                                                    self.program.uuid),
        ]

        with LogCapture() as log:
            with mock.patch("edx_ace.ace.send",
                            side_effect=Exception("Error!")):
                send_program_certificate_created_message(
                    self.user.username, self.program_cert)

        for index, message in enumerate(expected_messages):
            assert message in log.records[index].getMessage()
Beispiel #4
0
class ProgramCertificateIssuedEmailTests(SiteMixin, TestCase):
    """
    Tests for the automated email sent to learners after completing an edX Program.
    """
    USERNAME = "******"
    FAKE_PROGRAM_UUID = 'f6551af4-aa5a-4089-801b-53485d0d1726'

    def setUp(self):
        super().setUp()
        self.user = UserFactory(username=self.USERNAME)
        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.program = None
        self.program_cert = None

        mail.outbox = []

    def _get_custom_completion_email_template_settings(self):
        return {
            'f6551af4-aa5a-4089-801b-53485d0d1726': {
                'plaintext':
                '''
                I am email one
                I have the best content
                ''',
                'html':
                '''
                <p>I am email one</p>
                <p>I have the best content</p>
                ''',
            },
            'excellent-program': {
                'plaintext':
                '''
                I am email two
                I have better content
                ''',
                'html':
                '''
                <p>I am email two</p>
                <p>I have better content</p>
                ''',
            },
            'tubular-program': {
                'plaintext':
                '''
                I am email three
                I have great content too
                ''',
                'html':
                '''
                <p>I am email three</p>
                <p>I have great content too</p>
                ''',
            }
        }

    def _setup_program_and_program_cert(self, program_type, uuid=None):
        self.program = ProgramFactory(site=self.site)
        self.program.type = program_type
        self.program.type_slug = slugify(program_type)

        if uuid:
            self.program.uuid = uuid

        self.program.save()

        self.program_cert = ProgramCertificateFactory(
            site=self.site, program_uuid=self.program.uuid)

    def _build_expected_plaintext_email_body(self):
        custom_completion_email_template_settings = self._get_custom_completion_email_template_settings(
        )

        email_fragments = [
            'Congratulations on completing the {} {} Program!'.format(
                self.program.title,
                self.program.type,
            ),
            'Sincerely,',
            'The {} Team'.format(self.site.siteconfiguration.platform_name),
        ]

        if custom_completion_email_template_settings.get(self.program.uuid):
            email_fragments.append(
                textwrap.dedent(
                    custom_completion_email_template_settings.get(
                        self.program.uuid).get('plaintext')))
        elif custom_completion_email_template_settings.get(
                self.program.type_slug):
            email_fragments.append(
                textwrap.dedent(
                    custom_completion_email_template_settings.get(
                        self.program.type_slug).get('plaintext')))

        return email_fragments

    def _build_expected_html_email_body(self):
        custom_completion_email_template_settings = self._get_custom_completion_email_template_settings(
        )

        email_fragments = [
            "Congratulations on completing the {} {} Program!".format(
                self.program.title, self.program.type),
            'Sincerely,<br/>The {} Team'.format(
                self.site.siteconfiguration.platform_name),
        ]

        if custom_completion_email_template_settings.get(self.program.uuid):
            email_fragments.append(
                custom_completion_email_template_settings.get(
                    self.program.uuid).get('html'))
        elif custom_completion_email_template_settings.get(
                self.program.type_slug):
            email_fragments.append(
                custom_completion_email_template_settings.get(
                    self.program.type_slug).get('html'))

        return email_fragments

    def _assert_email_contents(self):
        """
        Utility function that verifies the contents of the generated email
        """
        expected_plaintext_email_contents = self._build_expected_plaintext_email_body(
        )
        expected_html_email_contents = self._build_expected_html_email_body()

        self.assertEqual(1, len(mail.outbox))

        email = mail.outbox[0]
        plaintext_body = email.body
        html_body = email.alternatives[0][0]

        self.assertEqual(email.to[0], self.user.email)
        self._assert_subject(email.subject)
        self._assert_email_body_contents(plaintext_body,
                                         expected_plaintext_email_contents)
        self._assert_email_body_contents(html_body,
                                         expected_html_email_contents)

    def _assert_subject(self, email_subject):
        """
        Utility method that verifies the subject text of the automated emails being sent to
        learners.
        """
        expected_subject = 'Congratulations for finishing your {} {} Program!'.format(
            self.program.title, self.program.type)
        self.assertEqual(email_subject, expected_subject)

    def _assert_email_body_contents(self, email_body, fragments):
        """
        Utility method that verifies the content in the generated email is as expected.
        """
        for fragment in fragments:
            self.assertIn(fragment, email_body)

    def test_base_template(self):
        self._setup_program_and_program_cert('Radical Program')

        send_program_certificate_created_message(self.user.username,
                                                 self.program_cert)

        self._assert_email_contents()

    def test_custom_email_template_program_uuid(self):
        """
        Test for the contents of a custom email template.
        """
        self._setup_program_and_program_cert('Excellent Program',
                                             self.FAKE_PROGRAM_UUID)

        with self.settings(CUSTOM_COMPLETION_EMAIL_TEMPLATE_EXTRA=self.
                           _get_custom_completion_email_template_settings()):
            send_program_certificate_created_message(self.user.username,
                                                     self.program_cert)

        self._assert_email_contents()

    def test_custom_email_template_program_type(self):
        self._setup_program_and_program_cert('Excellent Program')

        with self.settings(CUSTOM_COMPLETION_EMAIL_TEMPLATE_EXTRA=self.
                           _get_custom_completion_email_template_settings()):
            send_program_certificate_created_message(self.user.username,
                                                     self.program_cert)

        self._assert_email_contents()

    def test_custom_email_template_alternative_program_type(self):
        self._setup_program_and_program_cert('Tubular Program')

        with self.settings(CUSTOM_COMPLETION_EMAIL_TEMPLATE_EXTRA=self.
                           _get_custom_completion_email_template_settings()):
            send_program_certificate_created_message(self.user.username,
                                                     self.program_cert)

        self._assert_email_contents()

    def test_send_email_exception_occurs(self):
        self._setup_program_and_program_cert("Radical Program")

        expected_messages = [
            'Sending Program completion email to learner with id [{}] in Program [{}]'
            .format(self.user.id, self.program.uuid),
            'Unable to send email to learner with id: [{}] for Program [{}]. Error occurred while attempting to '
            'format or send message: Error!'.format(self.user.id,
                                                    self.program.uuid)
        ]

        with LogCapture() as log:
            with mock.patch('edx_ace.ace.send',
                            side_effect=Exception("Error!")):
                send_program_certificate_created_message(
                    self.user.username, self.program_cert)

        for index, message in enumerate(expected_messages):
            assert message in log.records[index].getMessage()
Beispiel #5
0
class RecordsViewTests(SiteMixin, TestCase):
    MOCK_USER_DATA = {'username': '******', 'name': 'Test User', 'email': '*****@*****.**', }

    def setUp(self):
        super().setUp()
        dump_random_state()

        self.user = UserFactory(username=self.MOCK_USER_DATA['username'])
        self.orgs = [OrganizationFactory.create(name=name, site=self.site) for name in ['TestOrg1', 'TestOrg2']]
        self.course = CourseFactory.create(site=self.site)
        self.course_runs = CourseRunFactory.create_batch(2, course=self.course)
        self.program = ProgramFactory(title="TestProgram1",
                                      course_runs=self.course_runs,
                                      authoring_organizations=self.orgs,
                                      site=self.site)
        self.course_certs = [CourseCertificateFactory.create(
            course_id=course_run.key, site=self.site,
        ) for course_run in self.course_runs]
        self.program_cert = ProgramCertificateFactory.create(program_uuid=self.program.uuid, site=self.site)
        self.course_credential_content_type = ContentType.objects.get(
            app_label='credentials',
            model='coursecertificate'
        )
        self.program_credential_content_type = ContentType.objects.get(
            app_label='credentials',
            model='programcertificate'
        )
        self.course_user_credentials = [UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.course_credential_content_type,
            credential=course_cert
        ) for course_cert in self.course_certs]
        self.program_user_credential = UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=self.program_cert
        )

        self.client.login(username=self.user.username, password=USER_PASSWORD)

    def _render_records(self, program_data=None, status_code=200):
        """ Helper method to mock and render a user certificate."""
        if program_data is None:
            program_data = []

        with patch('credentials.apps.records.views.RecordsView._get_programs') as get_programs:
            get_programs.return_value = program_data
            response = self.client.get(reverse('records:index'))
            self.assertEqual(response.status_code, status_code)

        return response

    def assert_matching_template_origin(self, actual, expected_template_name):
        expected = select_template([expected_template_name])
        self.assertEqual(actual.origin, expected.origin)

    def test_no_anonymous_access(self):
        """ Verify that the view rejects non-logged-in users. """
        self.client.logout()
        response = self._render_records(status_code=302)
        self.assertRegex(response.url, '^/login/.*')  # pylint: disable=deprecated-method

    def test_normal_access(self):
        """ Verify that the view works in default case. """
        response = self._render_records()
        response_context_data = response.context_data

        self.assertContains(response, 'My Learner Records')

        actual_child_templates = response_context_data['child_templates']
        self.assert_matching_template_origin(actual_child_templates['footer'], '_footer.html')
        self.assert_matching_template_origin(actual_child_templates['header'], '_header.html')
        self.assert_matching_template_origin(actual_child_templates['masquerade'], '_masquerade.html')

    def test_xss(self):
        """ Verify that the view protects against xss in translations. """
        response = self._render_records([
            {
                "name": "<xss>",
                'partner': 'XSS',
                'uuid': 'uuid',
            },
        ])

        # Test that the data is parsed from an escaped string
        self.assertContains(response, 'JSON.parse(\'[{' +
                                      '\\u0022name\\u0022: \\u0022\\u003Cxss\\u003E\\u0022, ' +
                                      '\\u0022partner\\u0022: \\u0022XSS\\u0022, ' +
                                      '\\u0022uuid\\u0022: \\u0022uuid\\u0022' +
                                      '}]\')')
        self.assertNotContains(response, '<xss>')

    def test_help_url(self):
        """ Verify that the records help url gets loaded into the context """
        response = self._render_records()
        response_context_data = response.context_data
        self.assertIn('records_help_url', response_context_data)
        self.assertNotEqual(response_context_data['records_help_url'], '')

    @ddt.data(
        (Program.ACTIVE, True),
        (Program.RETIRED, True),
        (Program.DELETED, False),
        (Program.UNPUBLISHED, False),
    )
    @ddt.unpack
    def test_completed_render_from_db(self, status, visible):
        """ Verify that a program cert that is completed is returned correctly, with different statuses """
        self.program.status = status
        self.program.save()

        response = self.client.get(reverse('records:index'))
        self.assertEqual(response.status_code, 200)
        program_data = json.loads(response.context_data['programs'])

        expected_program_data = [
            {
                'name': self.program.title,
                'partner': 'TestOrg1, TestOrg2',
                'uuid': self.program.uuid.hex,
                'type': slugify(self.program.type),
                'completed': True,
                'empty': False,
            }
        ]
        self.assertEqual(program_data, expected_program_data if visible else [])

    def test_in_progress_from_db(self):
        """ Verify that no program cert, but course certs reuslts in an In Progress program """
        # Delete the program
        self.program_cert.delete()
        response = self.client.get(reverse('records:index'))
        self.assertEqual(response.status_code, 200)
        program_data = json.loads(response.context_data['programs'])
        expected_program_data = [
            {
                'name': self.program.title,
                'partner': 'TestOrg1, TestOrg2',
                'uuid': self.program.uuid.hex,
                'type': slugify(self.program.type),
                'completed': False,
                'empty': False,
            }
        ]
        self.assertEqual(program_data, expected_program_data)

    def test_not_visible_from_db(self):
        """ Test that the program's visible_date is considered """
        UserCredentialAttributeFactory(
            user_credential=self.program_user_credential,
            name='visible_date',
            value='9999-01-01T01:01:01Z',
        )
        response = self.client.get(reverse('records:index'))
        self.assertFalse(json.loads(response.context_data['programs'])[0]['completed'])

    def test_multiple_programs(self):
        """ Test that multiple programs can appear, in progress and completed """
        # Create a second program, and delete the first one's certificate
        new_course = CourseFactory.create(site=self.site)
        new_course_run = CourseRunFactory.create(course=new_course)

        new_program = ProgramFactory.create(title='ZTestProgram',
                                            course_runs=[new_course_run],
                                            authoring_organizations=self.orgs,
                                            site=self.site)
        new_course_cert = CourseCertificateFactory.create(course_id=new_course_run.key, site=self.site)
        new_program_cert = ProgramCertificateFactory.create(program_uuid=new_program.uuid, site=self.site)

        # Make a new user credential
        UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=new_course_cert
        )
        # Make a new program credential
        UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=new_program_cert
        )
        self.program_user_credential.delete()

        response = self.client.get(reverse('records:index'))
        self.assertEqual(response.status_code, 200)
        program_data = json.loads(response.context_data['programs'])
        expected_program_data = [
            {
                'name': self.program.title,
                'partner': 'TestOrg1, TestOrg2',
                'uuid': self.program.uuid.hex,
                'type': slugify(self.program.type),
                'completed': False,
                'empty': False,
            },
            {
                'name': new_program.title,
                'partner': 'TestOrg1, TestOrg2',
                'uuid': new_program.uuid.hex,
                'type': slugify(new_program.type),
                'completed': True,
                'empty': False,
            }
        ]
        self.assertEqual(program_data, expected_program_data)
Beispiel #6
0
class ProgramListingViewTests(SiteMixin, TestCase):
    MOCK_USER_DATA = {'username': '******', 'name': 'Test User', 'email': '*****@*****.**', }

    def setUp(self):
        super().setUp()
        dump_random_state()

        self.user = UserFactory(username=self.MOCK_USER_DATA['username'], is_superuser=True)
        self.orgs = [OrganizationFactory.create(name=name, site=self.site) for name in ['TestOrg1', 'TestOrg2']]
        self.course = CourseFactory.create(site=self.site)
        self.course_runs = CourseRunFactory.create_batch(2, course=self.course)
        self.program = ProgramFactory(title="TestProgram1",
                                      course_runs=self.course_runs,
                                      authoring_organizations=self.orgs,
                                      site=self.site)
        self.course_certs = [CourseCertificateFactory.create(
            course_id=course_run.key, site=self.site,
        ) for course_run in self.course_runs]
        self.program_cert = ProgramCertificateFactory.create(program_uuid=self.program.uuid, site=self.site)
        self.course_credential_content_type = ContentType.objects.get(
            app_label='credentials',
            model='coursecertificate'
        )
        self.program_credential_content_type = ContentType.objects.get(
            app_label='credentials',
            model='programcertificate'
        )
        self.course_user_credentials = [UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.course_credential_content_type,
            credential=course_cert
        ) for course_cert in self.course_certs]
        self.program_user_credentials = UserCredentialFactory.create(
            username=self.user.username,
            credential_content_type=self.program_credential_content_type,
            credential=self.program_cert
        )

        self.client.login(username=self.user.username, password=USER_PASSWORD)

    def _render_listing(self, expected_program_data=None, status_code=200):
        """ Helper method to mock and render a user certificate."""
        response = self.client.get(reverse('program_listing'))
        self.assertEqual(response.status_code, status_code)

        if expected_program_data is not None:
            program_data = json.loads(response.context_data['programs'])
            self.assertListEqual(program_data, expected_program_data)

        return response

    def _default_program_data(self, overrides=None):
        # if nothing is adjusted, this is the expected listing
        data = [
            {
                'name': self.program.title,
                'partner': 'TestOrg1, TestOrg2',
                'uuid': self.program.uuid.hex,
                'type': slugify(self.program.type),
                'completed': True,
                'empty': False,
            },
        ]

        if overrides is not None:
            data[0].update(overrides)

        return data

    def assert_matching_template_origin(self, actual, expected_template_name):
        expected = select_template([expected_template_name])
        self.assertEqual(actual.origin, expected.origin)

    def test_no_anonymous_access(self):
        """ Verify that the view rejects non-logged-in users. """
        self.client.logout()
        response = self._render_listing(status_code=302)
        self.assertRegexpMatches(response.url, '^/login/.*')  # pylint: disable=deprecated-method

    def test_only_superuser_access(self):
        """ Verify that the view rejects non-superusers. """
        self.user.is_superuser = False
        self.user.save()
        self._render_listing(status_code=404)

    def test_normal_access(self):
        """ Verify that the view works in default case. """
        response = self._render_listing()
        response_context_data = response.context_data

        self.assertContains(response, 'Program Listing View')

        actual_child_templates = response_context_data['child_templates']
        self.assert_matching_template_origin(actual_child_templates['footer'], '_footer.html')
        self.assert_matching_template_origin(actual_child_templates['header'], '_header.html')
        self.assertFalse('masquerade' in actual_child_templates)  # no masquerading on this view

    @ddt.data(
        (Program.ACTIVE, True),
        (Program.RETIRED, False),  # this is different from RecordsView
        (Program.DELETED, False),
        (Program.UNPUBLISHED, False),
    )
    @ddt.unpack
    def test_completed_render_from_db(self, status, visible):
        """ Verify that a program cert that is completed is returned correctly, with different statuses """
        self.program.status = status
        self.program.save()

        data = self._default_program_data() if visible else []
        self._render_listing(expected_program_data=data)

    def test_in_progress_from_db(self):
        """ Verify that no program cert, but course certs results in an In Progress program """
        # Delete the program cert
        self.program_cert.delete()

        data = self._default_program_data(overrides={'completed': False})
        self._render_listing(expected_program_data=data)

    def test_empty_programs(self):
        """ Test that a program with no certs shows as empty """
        # Delete all certs
        for cert in self.course_certs:
            cert.delete()
        self.program_cert.delete()

        data = self._default_program_data(overrides={'completed': False, 'empty': True})
        self._render_listing(expected_program_data=data)