Example #1
0
 def test_models_course_run_state_coming(self):
     """
     A course run that is future and not yet open for enrollment should return a state
     with a CTA to see details with the start date.
     """
     course_run = CourseRunFactory(
         enrollment_start=self.now + timedelta(hours=1),
         enrollment_end=self.now + timedelta(hours=2),
         start=self.now + timedelta(hours=3),
         end=self.now + timedelta(hours=4),
     )
     self.assertEqual(
         dict(course_run.state),
         {
             "priority": 2,
             "text": "starting on",
             "call_to_action": None,
             "datetime": self.now + timedelta(hours=3),
         },
     )
Example #2
0
    def test_enrollment_create_failure(self, lms_mock):
        """
        What we do when the enrollment fails.
        """
        user = UserFactory()
        course_run = CourseRunFactory(
            start=arrow.utcnow().shift(days=-5).datetime,
            end=arrow.utcnow().shift(days=+90).datetime,
            enrollment_start=arrow.utcnow().shift(days=-35).datetime,
            enrollment_end=arrow.utcnow().shift(days=+10).datetime,
        )

        self.client.force_login(user)
        lms_mock.set_enrollment.return_value = False
        response = self.client.post("/api/v1.0/enrollments/",
                                    data={"course_run": course_run.id})

        self.assertEqual(response.status_code, 400)
        lms_mock.set_enrollment.assert_called_once_with(
            user, course_run.resource_link)
Example #3
0
    def test_admin_course_run_change_view_get_superuser_public(self):
        """Public course runs should not render a change view."""
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        # Create a public course run
        course = CourseFactory()
        CourseRunFactory(direct_course=course)
        course.extended_object.publish("en")

        self.assertEqual(CourseRun.objects.count(), 2)
        public_course_run = CourseRun.objects.get(
            draft_course_run__isnull=False)

        # Get the admin change view
        url = reverse("admin:courses_courserun_change",
                      args=[public_course_run.id])
        response = self.client.get(url, follow=True)

        self.assertEqual(response.status_code, 403)
Example #4
0
 def test_models_course_run_state_ongoing_closed(self):
     """
     A course run that is on-going but closed for enrollment should return a state with
     "on-going" as text and no CTA.
     """
     course_run = CourseRunFactory(
         enrollment_start=self.now - timedelta(hours=3),
         start=self.now - timedelta(hours=2),
         enrollment_end=self.now - timedelta(hours=1),
         end=self.now + timedelta(hours=1),
     )
     self.assertEqual(
         dict(course_run.state),
         {
             "priority": 4,
             "text": "on-going",
             "call_to_action": None,
             "datetime": None,
         },
     )
Example #5
0
    def test_models_course_copy_relations_publish_recursive_loop(self):
        """
        In a previous version of the the CourseRun method "copy_translations" in which we
        used instances instead of update queries, this test was generating an infinite
        recursive loop.
        """
        course = CourseFactory(page_title="my course title")
        TitleFactory(
            page=course.extended_object, language="fr", title="mon titre de cours"
        )
        course_run = CourseRunFactory(direct_course=course, title="my run")
        course_run_translation_fr = CourseRunTranslation.objects.create(
            master=course_run, language_code="fr", title="ma session"
        )
        self.assertTrue(course.extended_object.publish("fr"))

        course_run_translation_fr.title = "ma session modifiée"
        course_run_translation_fr.save()

        self.assertTrue(course.extended_object.publish("fr"))
Example #6
0
 def test_models_course_run_state_future_open(self):
     """
     A course run that is future and open for enrollment should return a state with a CTA
     to enroll and the start date.
     """
     course_run = CourseRunFactory(
         enrollment_start=self.now - timedelta(hours=1),
         enrollment_end=self.now + timedelta(hours=1),
         start=self.now + timedelta(hours=2),
         end=self.now + timedelta(hours=3),
     )
     self.assertEqual(
         dict(course_run.state),
         {
             "priority": 1,
             "text": "starting on",
             "call_to_action": "enroll now",
             "datetime": self.now + timedelta(hours=2),
         },
     )
    def prepare_to_test_state(self, state):
        """
        Not a test.
        Create objects and mock to help testing the impact of the state on template rendering.
        """
        course = CourseFactory(page_title="my course", should_publish=True)
        CourseRunFactory(
            page_parent=course.extended_object,
            page_title="my course run",
            should_publish=True,
        )

        url = course.extended_object.get_absolute_url()
        with mock.patch.object(
            CourseRun, "state", new_callable=mock.PropertyMock, return_value=state
        ):
            response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        return response
Example #8
0
    def test_enrollment_create(self, lms_mock):
        """
        Course is open for enrollment and user can join it. Enrollment is successful.
        """
        user = UserFactory()
        course_run = CourseRunFactory(
            start=arrow.utcnow().shift(days=-5).datetime,
            end=arrow.utcnow().shift(days=+90).datetime,
            enrollment_start=arrow.utcnow().shift(days=-35).datetime,
            enrollment_end=arrow.utcnow().shift(days=+10).datetime,
        )

        self.client.force_login(user)
        lms_mock.set_enrollment.return_value = True
        response = self.client.post("/api/v1.0/enrollments/",
                                    data={"course_run": course_run.id})

        self.assertEqual(response.status_code, 201)
        lms_mock.set_enrollment.assert_called_once_with(
            user, course_run.resource_link)
Example #9
0
    def test_templates_course_run_detail_breadcrumb_below_course(self):
        """
        Validate the format of the breadcrumb on a course run directly placed below the course.
        """
        home_page = PageFactory(title__title="home",
                                title__language="en",
                                should_publish=True)
        search_page = PageFactory(
            title__title="courses",
            title__language="en",
            parent=home_page,
            should_publish=True,
        )
        course = CourseFactory(
            page_title="course name",
            page_parent=search_page,
            page_in_navigation=True,
            should_publish=True,
        )
        course_run = CourseRunFactory(
            page_title="session 42",
            page_parent=course.extended_object,
            should_publish=True,
        )
        response = self.client.get(
            course_run.extended_object.get_absolute_url())

        self.assertEqual(response.status_code, 200)
        self.assertContains(
            response,
            ('<ul class="breadcrumbs__list">'
             '  <li class="breadcrumbs__item">You are here:</li>'
             '  <li class="breadcrumbs__item"><a href="/en/home/">home</a></li>'
             '  <li class="breadcrumbs__item"><a href="/en/home/courses/">courses</a></li>'
             '  <li class="breadcrumbs__item">'
             '    <a href="/en/home/courses/course-name/">course name</a>'
             "    </li>"
             '  <li class="breadcrumbs__item"><span class="active">session 42</span></li>'
             "</ul>"),
            html=True,
        )
Example #10
0
    def test_models_course_run_mark_dirty_delete_course_run(self):
        """
        Deleting a scheduled course run should mark the related course page dirty.
        """
        course_run = CourseRunFactory()
        self.assertTrue(course_run.direct_course.extended_object.publish("en"))
        title_obj = course_run.direct_course.extended_object.title_set.first()
        course_run.refresh_from_db()

        self.assertEqual(title_obj.publisher_state, PUBLISHER_STATE_DEFAULT)

        course_run.delete()
        title_obj.refresh_from_db()

        self.assertEqual(title_obj.publisher_state, PUBLISHER_STATE_DIRTY)
Example #11
0
    def test_models_course_run_mark_dirty_delete_course_run_to_be_scheduled(
            self):
        """
        Deleting a course run yet to be scheduled should not mark the related course page dirty.
        """
        field = random.choice(["start", "enrollment_start"])
        course_run = CourseRunFactory(**{field: None})
        self.assertTrue(course_run.direct_course.extended_object.publish("en"))
        title_obj = course_run.direct_course.extended_object.title_set.first()
        course_run.refresh_from_db()

        course_run.delete()
        title_obj.refresh_from_db()

        self.assertEqual(title_obj.publisher_state, PUBLISHER_STATE_DEFAULT)
Example #12
0
    def test_indexers_courses_get_es_documents_no_end_no_enrollment_end(self):
        """
        Course runs with no end date and no date of end of enrollment should be open for ever.
        """
        course = CourseFactory(should_publish=True)
        CourseRunFactory(
            page_parent=course.extended_object,
            end=None,
            enrollment_end=None,
            should_publish=True,
        )

        indexed_courses = list(
            CoursesIndexer.get_es_documents(index="some_index", action="some_action")
        )
        self.assertEqual(len(indexed_courses), 1)
        self.assertEqual(len(indexed_courses[0]["course_runs"]), 1)
        self.assertEqual(indexed_courses[0]["course_runs"][0]["end"].year, 9999)
        self.assertEqual(
            indexed_courses[0]["course_runs"][0]["enrollment_end"].year, 9999
        )
Example #13
0
    def test_admin_course_run_change_view_get_staff_missing_object_permission(
            self):
        """
        Staff users missing permissions should not be allowed to view a course run's change view.
        """
        user = UserFactory(is_staff=True)
        self.client.login(username=user.username, password="******")

        course_run = CourseRunFactory()

        # Create the required model permissions but not the page permission
        self.add_permission(user, "change_courserun")
        self.add_permission(user, "change_page")

        # Get the admin change view
        url = reverse("admin:courses_courserun_change", args=[course_run.id])
        response = self.client.get(url, follow=True)

        self.assertEqual(response.status_code, 200)
        self.assertNotContains(response, "id_title")
        self.assertContains(response, "Perhaps it was deleted?")
 def test_course_enrollment_widget_props_tag(self):
     """
     CourseEnrollment required id, resource_link and state.priority course run's properties.
     course_enrollment_widget_props should return these properties wrapped into
     a courseRun object as a stringified json.
     """
     course_run = CourseRunFactory(
         resource_link=
         "http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/course/"
     )
     context = {"run": course_run}
     self.assertEqual(
         course_enrollment_widget_props(context),
         json.dumps({
             "courseRun": {
                 "id": course_run.id,
                 "resource_link": course_run.resource_link,
                 "priority": course_run.direct_course.state["priority"],
             }
         }),
     )
Example #15
0
    def test_models_course_run_mark_dirty_any_field(self):
        """
        Updating the value of any editable field on the course run should mark the related
        course page dirty (waiting to be published).
        """

        fields = map(
            lambda f: f.name,
            filter(
                lambda f: f.editable and not f.auto_created and not f.name ==
                "direct_course",
                CourseRun._meta.fields,
            ),
        )
        stub = CourseRunFactory(
            sync_mode="manual",
            catalog_visibility=CourseRunCatalogVisibility.COURSE_ONLY,
        )  # New random values to update our course run

        for field in fields:
            course_run = CourseRunFactory()
            self.assertTrue(
                course_run.direct_course.extended_object.publish("en"))
            title_obj = course_run.direct_course.extended_object.title_set.first(
            )

            setattr(course_run, field, getattr(stub, field))
            course_run.save()

            self.assertEqual(
                title_obj.publisher_state,
                PUBLISHER_STATE_DEFAULT,
                msg=f"Before refreshing from db {field:s}",
            )

            course_run.mark_course_dirty()
            title_obj.refresh_from_db()

            self.assertEqual(
                title_obj.publisher_state,
                PUBLISHER_STATE_DIRTY,
                msg=f"After refreshing from db {field:s}",
            )
Example #16
0
    def test_admin_course_run_change_view_post_staff_user_missing_permission(self):
        """
        Staff users with missing page permissions can not update a course run via the admin
        unless CMS permissions are not activated.
        """
        course_run = CourseRunFactory()
        snapshot = CourseFactory(page_parent=course_run.direct_course.extended_object)

        user = UserFactory(is_staff=True)
        self.client.login(username=user.username, password="******")

        # Add only model permissions, not page permission on the course page
        self.add_permission(user, "add_courserun")
        self.add_permission(user, "change_courserun")
        self.add_permission(user, "change_page")

        self._prepare_change_view_post(course_run, snapshot, 403, self.assertNotEqual)

        # But it should work if CMS permissions are not activated
        with override_settings(CMS_PERMISSION=False):
            self._prepare_change_view_post(course_run, snapshot, 200, self.assertEqual)
Example #17
0
    def test_admin_course_run_delete_staff_user_missing_permission(self):
        """
        Staff users with missing page permissions can not delete a course run via the admin
        unless CMS permissions are not activated.
        """
        course_run = CourseRunFactory()

        user = UserFactory(is_staff=True)
        self.client.login(username=user.username, password="******")

        # Add only model permissions, not page permission on the course page
        self.add_permission(user, "delete_courserun")
        self.add_permission(user, "change_page")

        self._prepare_delete(course_run, 200, self.assertTrue)

        # But it should work if CMS permissions are not activated
        with override_settings(CMS_PERMISSION=False):
            self._prepare_delete(course_run, 200, self.assertFalse)

        # The course object should not be deleted
        self.assertEqual(Course.objects.count(), 1)
    def test_models_course_run_state_no_end_date(self):
        """
        A course run with no end date is deemed to be forever on-going.
        """
        course_run = CourseRunFactory(end=None)

        # The course run should be open during its enrollment period
        now = datetime.utcfromtimestamp(
            random.randrange(
                int(course_run.enrollment_start.timestamp()) + 1,
                int(course_run.enrollment_end.timestamp()) - 1,
            )
        ).replace(tzinfo=pytz.utc)

        with mock.patch.object(timezone, "now", return_value=now):
            state = course_run.state

        self.assertIn(dict(state)["priority"], [0, 1])

        # The course run should be on-going at any date after its end of enrollment
        now = datetime.utcfromtimestamp(
            random.randrange(
                int(course_run.enrollment_end.timestamp()),
                int(datetime(9999, 12, 31).timestamp()),
            )
        ).replace(tzinfo=pytz.utc)

        with mock.patch.object(timezone, "now", return_value=now):
            state = course_run.state

        self.assertEqual(
            dict(state),
            {
                "priority": 5,
                "text": "on-going",
                "call_to_action": None,
                "datetime": None,
            },
        )
Example #19
0
    def test_indexers_courses_related_objects_consistency(self):
        """
        The organization and category ids in the Elasticsearch course document should be
        the same as the ids with which the corresponding organization and category objects
        are indexed.
        """
        # Create a course with a page in both english and french
        organization = OrganizationFactory(should_publish=True)
        category = CategoryFactory(should_publish=True)
        course = CourseFactory(
            fill_organizations=[organization],
            fill_categories=[category],
            should_publish=True,
        )
        CourseRunFactory(page_parent=course.extended_object, should_publish=True)

        course_document = list(
            CoursesIndexer.get_es_documents(index="some_index", action="some_action")
        )[0]
        self.assertEqual(
            course_document["organizations"],
            [
                next(
                    OrganizationsIndexer.get_es_documents(
                        index="some_index", action="some_action"
                    )
                )["_id"]
            ],
        )
        self.assertEqual(
            course_document["categories"],
            [
                next(
                    CategoriesIndexer.get_es_documents(
                        index="some_index", action="some_action"
                    )
                )["_id"]
            ],
        )
Example #20
0
    def test_enrollment_create_closed(self, lms_mock):
        """
        Attempting to enroll in a course that is not open for enrollment anymore results
        in an error.
        """
        user = UserFactory()
        course_run = CourseRunFactory(
            start=arrow.utcnow().shift(days=-35).datetime,
            end=arrow.utcnow().shift(days=+60).datetime,
            enrollment_start=arrow.utcnow().shift(days=-65).datetime,
            enrollment_end=arrow.utcnow().shift(days=-20).datetime,
        )

        self.client.force_login(user)
        response = self.client.post("/api/v1.0/enrollments/",
                                    data={"course_run": course_run.id})

        self.assertEqual(response.status_code, 400)
        self.assertEqual(
            response.json(),
            {"errors": ["Course run is not open for enrollments."]})
        lms_mock.set_enrollment.assert_not_called()
Example #21
0
    def test_admin_course_run_change_view_get_staff_all_permissions(self):
        """
        Staff users with all permissions should allowed to view a course run's change view.
        """
        user = UserFactory(is_staff=True)
        self.client.login(username=user.username, password="******")

        course_run = CourseRunFactory()

        # Create the required permissions
        self.add_permission(user, "change_courserun")
        self.add_permission(user, "change_page")
        PagePermission.objects.create(
            page=course_run.direct_course.extended_object,
            user=user,
            can_add=False,
            can_change=True,
            can_delete=False,
            can_publish=False,
            can_move_page=False,
        )

        # Get the admin change view
        url = reverse("admin:courses_courserun_change", args=[course_run.id])
        response = self.client.get(url, follow=True)

        # Check that the page includes the page title
        self.assertContains(response, course_run.title, status_code=200)

        # Check that the page includes all our fields
        self.assertContains(response, "id_title", count=2)
        self.assertContains(response, "id_resource_link", count=2)
        self.assertContains(response, "id_start_", count=3)
        self.assertContains(response, "id_end_", count=3)
        self.assertContains(response, "id_enrollment_start_", count=3)
        self.assertContains(response, "id_enrollment_end_", count=3)
        self.assertContains(response, "id_languages", count=2)
        self.assertContains(response, "id_enrollment_count", count=2)
Example #22
0
    def prepare_to_test_state(self, state, **kwargs):
        """
        Not a test.
        Create objects and mock to help testing the impact of the state on template rendering.
        """
        course = CourseFactory(should_publish=True)
        resource_link = kwargs.get(
            "resource_link") or "https://www.example.com/enroll"
        course_run = CourseRunFactory(
            page_parent=course.extended_object,
            resource_link=resource_link,
            should_publish=True,
        )

        url = course_run.extended_object.get_absolute_url()
        with mock.patch.object(CourseRun,
                               "state",
                               new_callable=mock.PropertyMock,
                               return_value=state):
            response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        return (response, course_run)
Example #23
0
    def test_templates_course_detail_course_run_title_empty(self):
        """
        A course run title can be empty and in this case the "From ..." string should be
        capitalized.
        """
        course = CourseFactory()
        page = course.extended_object
        CourseRunFactory(
            direct_course=course,
            title=None,
            start=pytz.utc.localize(datetime(2020, 12, 12)),
            end=pytz.utc.localize(datetime(2020, 12, 15)),
        )
        self.assertTrue(page.publish("en"))

        url = course.extended_object.get_absolute_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        # The description line should start with a capital letter
        self.assertContains(response,
                            "<li>From Dec. 12, 2020 to Dec. 15, 2020</li>",
                            html=True)
    def test_models_course_state_ongoing_open(self):
        """
        Confirm course state when there is an on-going course run open for enrollment.
        """
        course = CourseFactory()
        course_run = CourseRunFactory(
            page_parent=course.extended_object,
            start=self.now - timedelta(hours=1),
            end=self.now + timedelta(hours=2),
            enrollment_end=self.now + timedelta(hours=1),
        )
        with self.assertNumQueries(2):
            state = course.state
        expected_state = CourseState(0, course_run.enrollment_end)
        self.assertEqual(state, expected_state)

        # Adding courses in less priorietary states should not change the result
        self.create_run_ongoing_closed(course)
        self.create_run_future_closed(course)
        self.create_run_future_open(course)
        with self.assertNumQueries(2):
            state = course.state
        self.assertEqual(state, expected_state)
Example #25
0
    def test_admin_course_run_change_view_post_staff_user_page_permission(self):
        """Staff users with all necessary permissions can update a course run via the admin."""
        course_run = CourseRunFactory()
        snapshot = CourseFactory(page_parent=course_run.direct_course.extended_object)

        user = UserFactory(is_staff=True)
        self.client.login(username=user.username, password="******")

        # Add all necessary model and object level permissions
        self.add_permission(user, "add_courserun")
        self.add_permission(user, "change_courserun")
        self.add_permission(user, "change_page")
        PagePermission.objects.create(
            page=course_run.direct_course.extended_object,
            user=user,
            can_add=False,
            can_change=True,
            can_delete=False,
            can_publish=False,
            can_move_page=False,
        )

        self._prepare_change_view_post(course_run, snapshot, 200, self.assertEqual)
Example #26
0
    def test_models_course_copy_relations_cloning(self):
        """When cloning a page, the course runs should not be copied."""
        course = CourseFactory(page_title="my course title")
        page = course.extended_object
        TitleFactory(
            page=course.extended_object, language="fr", title="mon titre de cours"
        )
        course_run = CourseRunFactory(direct_course=course, title="my run")
        CourseRunTranslation.objects.create(
            master=course_run, language_code="fr", title="ma session"
        )

        # Copy the course page as its own child
        copy_page = page.copy(
            page.node.site, parent_node=page.node, translations=True, extensions=True
        )

        self.assertEqual(Course.objects.count(), 2)
        self.assertEqual(CourseRun.objects.count(), 1)
        self.assertEqual(CourseRunTranslation.objects.count(), 2)
        self.assertEqual(Title.objects.count(), 4)

        self.assertIsNone(copy_page.course.runs.first())
Example #27
0
    def test_admin_course_run_list_view_superuser(self):
        """
        The admin list view of course runs should only show the id field.
        """
        user = UserFactory(is_staff=True, is_superuser=True)
        self.client.login(username=user.username, password="******")

        # Create a course run linked to a page
        course = CourseFactory()
        course_run = CourseRunFactory(direct_course=course)
        course.extended_object.publish("en")
        self.assertEqual(CourseRun.objects.count(), 2)

        # Get the admin list view
        url = reverse("admin:courses_courserun_changelist")
        response = self.client.get(url, follow=True)
        self.assertEqual(response.status_code, 200)

        # Check that only the id field is displayed
        self.assertContains(response, "field-id", 2)
        self.assertContains(response, "field-", 2)

        # Check that the page includes both course run
        self.assertContains(
            response, '<p class="paginator">2 course runs</p>', html=True
        )

        change_url_draft = reverse(
            "admin:courses_courserun_change", args=[course_run.id]
        )
        self.assertContains(response, change_url_draft)

        change_url_public = reverse(
            "admin:courses_courserun_change", args=[course_run.public_course_run.id]
        )
        self.assertContains(response, change_url_public)
    def test_models_course_run_mark_dirty_direct_course_field(self):
        """
        Changing the course to which a course run is related should mark both the source and the
        target course pages dirty (waiting to be published).
        """
        course_run = CourseRunFactory()
        course_source = course_run.direct_course
        course_target = CourseFactory(should_publish=True)
        self.assertTrue(course_source.extended_object.publish("en"))
        title_obj_source = course_source.extended_object.title_set.first()
        title_obj_target = course_target.extended_object.title_set.first()

        course_run.direct_course = course_target
        course_run.save()

        self.assertEqual(title_obj_source.publisher_state, PUBLISHER_STATE_DEFAULT)
        self.assertEqual(title_obj_target.publisher_state, PUBLISHER_STATE_DEFAULT)

        course_run.mark_course_dirty()
        title_obj_source.refresh_from_db()
        title_obj_target.refresh_from_db()

        self.assertEqual(title_obj_source.publisher_state, PUBLISHER_STATE_DIRTY)
        self.assertEqual(title_obj_target.publisher_state, PUBLISHER_STATE_DIRTY)
Example #29
0
    def test_admin_page_snapshot_with_cms_permissions(self):
        """
        Confirm the creation of a snapshot works as expected:
        - snapshot title and slug are set to a timestamp,
        - publication status of the course is respected on the snapshot,
        - course runs are moved below the snapshot,
        - publication status of course runs is respected,
        """
        user = UserFactory(is_staff=True)
        self.client.login(username=user.username, password="******")

        # Create a course page (not published in german)
        course = CourseFactory(
            page_title={"en": "a course", "fr": "un cours", "de": "ein Kurs"}
        )
        # Create a course run for this course
        course_run = CourseRunFactory(direct_course=course)

        self.assertTrue(course.extended_object.publish("en"))
        self.assertTrue(course.extended_object.publish("fr"))

        # It should have copied the course run to the published page
        self.assertEqual(CourseRun.objects.count(), 2)

        # Add the necessary permissions (global and per page)
        self.add_permission(user, "add_page")
        self.add_permission(user, "change_page")
        self.add_page_permission(
            user, course.extended_object, can_change=True, can_add=True
        )

        # Trigger the creation of a snapshot for the course
        url = f"/en/admin/courses/course/{course.id:d}/snapshot/"
        now = datetime(2010, 1, 1, tzinfo=timezone.utc)
        with mock.patch.object(timezone, "now", return_value=now):
            response = self.client.post(url, follow=True)

        self.assertEqual(response.status_code, 200)
        content = json.loads(response.content)
        self.assertEqual(Course.objects.count(), 4)
        self.assertEqual(CourseRun.objects.count(), 2)

        snapshot = (
            Course.objects.exclude(id=course.id)
            .exclude(public_extension__isnull=True)
            .get()
        )
        self.assertEqual(content, {"id": snapshot.id})

        # The snapshot title and slug should include the version with datetime of snapshot
        expected_titles = {
            "en": "a course (Archived on 2010-01-01 00:00:00)",
            "fr": "un cours (Archived on 2010-01-01 00:00:00)",
            "de": "ein Kurs (Archived on 2010-01-01 00:00:00)",
        }
        for language in ["en", "fr", "de"]:
            self.assertEqual(
                snapshot.extended_object.get_title(language), expected_titles[language]
            )
            self.assertEqual(
                snapshot.extended_object.get_slug(language),
                "archived-on-2010-01-01-000000",
            )

        # The publication status of the course should be respected on the snapshot
        self.assertTrue(snapshot.check_publication("en"))
        self.assertTrue(snapshot.check_publication("fr"))
        self.assertFalse(snapshot.check_publication("de"))

        # The course run should have moved below the snapshot
        self.assertEqual(CourseRun.objects.count(), 2)
        course_run.refresh_from_db()
        self.assertEqual(course_run.direct_course, snapshot)
Example #30
0
 def test_models_course_run_get_languages_display_one_language(self):
     """
     With one language, it should return its readable version without any comma.
     """
     course_run = CourseRunFactory(languages=["fr"])
     self.assertEqual(course_run.get_languages_display(), "French")