test_course_listing.py 6.69 KB
Newer Older
1 2 3 4
"""
Unit tests for getting the list of courses for a user through iterating all courses and
by reversing group name formats.
"""
5 6 7 8
import unittest

from django.conf import settings
from django.test.client import Client
9
import mock
10

11 12
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from student.models import CourseEnrollment
13
from student.roles import GlobalStaff
14 15 16
from student.tests.factories import UserFactory
from student.views import get_course_enrollments
from xmodule.error_module import ErrorDescriptor
17
from xmodule.modulestore import ModuleStoreEnum
18
from xmodule.modulestore.django import modulestore
19
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
20
from xmodule.modulestore.tests.factories import CourseFactory
21 22 23 24 25
from util.milestones_helpers import (
    get_pre_requisite_courses_not_completed,
    set_prerequisite_courses,
    seed_milestone_relationship_types
)
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43


class TestCourseListing(ModuleStoreTestCase):
    """
    Unit tests for getting the list of courses for a logged in user
    """
    def setUp(self):
        """
        Add a student & teacher
        """
        super(TestCourseListing, self).setUp()

        self.student = UserFactory()
        self.teacher = UserFactory()
        GlobalStaff().add_users(self.teacher)
        self.client = Client()
        self.client.login(username=self.teacher.username, password='test')

44
    def _create_course_with_access_groups(self, course_location, metadata=None, default_store=None):
45 46 47
        """
        Create dummy course with 'CourseFactory' and enroll the student
        """
48
        metadata = {} if not metadata else metadata
49 50 51
        course = CourseFactory.create(
            org=course_location.org,
            number=course_location.course,
52
            run=course_location.run,
53 54
            metadata=metadata,
            default_store=default_store
55 56 57 58 59 60 61 62 63 64 65 66 67
        )

        CourseEnrollment.enroll(self.student, course.id)

        return course

    def tearDown(self):
        """
        Reverse the setup
        """
        self.client.logout()
        super(TestCourseListing, self).tearDown()

68
    @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
69 70 71 72
    def test_get_course_list(self):
        """
        Test getting courses
        """
73
        course_location = self.store.make_course_key('Org1', 'Course1', 'Run1')
74 75 76
        self._create_course_with_access_groups(course_location)

        # get dashboard
77
        courses_list = list(get_course_enrollments(self.student, None, []))
78
        self.assertEqual(len(courses_list), 1)
79
        self.assertEqual(courses_list[0].course_id, course_location)
80 81 82

        CourseEnrollment.unenroll(self.student, course_location)
        # get dashboard
83
        courses_list = list(get_course_enrollments(self.student, None, []))
84 85 86 87 88 89
        self.assertEqual(len(courses_list), 0)

    def test_errored_course_regular_access(self):
        """
        Test the course list for regular staff when get_course returns an ErrorDescriptor
        """
90 91 92 93
        # pylint: disable=protected-access
        mongo_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
        course_key = mongo_store.make_course_key('Org1', 'Course1', 'Run1')
        self._create_course_with_access_groups(course_key, default_store=ModuleStoreEnum.Type.mongo)
94

95
        with mock.patch('xmodule.modulestore.mongo.base.MongoKeyValueStore', mock.Mock(side_effect=Exception)):
96
            self.assertIsInstance(modulestore().get_course(course_key), ErrorDescriptor)
97

98 99 100
            # Invalidate (e.g., delete) the corresponding CourseOverview, forcing get_course to be called.
            CourseOverview.objects.filter(id=course_key).delete()

101
            courses_list = list(get_course_enrollments(self.student, None, []))
102 103 104 105 106 107 108
            self.assertEqual(courses_list, [])

    def test_course_listing_errored_deleted_courses(self):
        """
        Create good courses, courses that won't load, and deleted courses which still have
        roles. Test course listing.
        """
109
        mongo_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
110

111 112
        good_location = mongo_store.make_course_key('testOrg', 'testCourse', 'RunBabyRun')
        self._create_course_with_access_groups(good_location, default_store=ModuleStoreEnum.Type.mongo)
113

114 115
        course_location = mongo_store.make_course_key('testOrg', 'doomedCourse', 'RunBabyRun')
        self._create_course_with_access_groups(course_location, default_store=ModuleStoreEnum.Type.mongo)
116
        mongo_store.delete_course(course_location, ModuleStoreEnum.UserID.test)
117

118
        courses_list = list(get_course_enrollments(self.student, None, []))
119
        self.assertEqual(len(courses_list), 1, courses_list)
120
        self.assertEqual(courses_list[0].course_id, good_location)
121 122 123 124 125 126 127 128 129

    @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_PREREQUISITE_COURSES': True, 'MILESTONES_APP': True})
    def test_course_listing_has_pre_requisite_courses(self):
        """
        Creates four courses. Enroll test user in all courses
        Sets two of them as pre-requisites of another course.
        Checks course where pre-requisite course is set has appropriate info.
        """
        seed_milestone_relationship_types()
130
        course_location2 = self.store.make_course_key('Org1', 'Course2', 'Run2')
131
        self._create_course_with_access_groups(course_location2)
132
        pre_requisite_course_location = self.store.make_course_key('Org1', 'Course3', 'Run3')
133
        self._create_course_with_access_groups(pre_requisite_course_location)
134
        pre_requisite_course_location2 = self.store.make_course_key('Org1', 'Course4', 'Run4')
135 136 137 138 139 140
        self._create_course_with_access_groups(pre_requisite_course_location2)
        # create a course with pre_requisite_courses
        pre_requisite_courses = [
            unicode(pre_requisite_course_location),
            unicode(pre_requisite_course_location2),
        ]
141
        course_location = self.store.make_course_key('Org1', 'Course1', 'Run1')
142 143 144 145 146 147
        self._create_course_with_access_groups(course_location, {
            'pre_requisite_courses': pre_requisite_courses
        })

        set_prerequisite_courses(course_location, pre_requisite_courses)
        # get dashboard
148 149 150 151 152
        course_enrollments = list(get_course_enrollments(self.student, None, []))
        courses_having_prerequisites = frozenset(
            enrollment.course_id for enrollment in course_enrollments
            if enrollment.course_overview.pre_requisite_courses
        )
153 154 155 156 157
        courses_requirements_not_met = get_pre_requisite_courses_not_completed(
            self.student,
            courses_having_prerequisites
        )
        self.assertEqual(len(courses_requirements_not_met[course_location]['courses']), len(pre_requisite_courses))