test_overrides.py 7.37 KB
Newer Older
1
# coding=UTF-8
2 3 4
"""
tests for overrides
"""
5
import datetime
6

7 8
import mock
import pytz
9 10
from ccx_keys.locator import CCXLocator
from django.test.utils import override_settings
11
from nose.plugins.attrib import attr
12

13
from courseware.courses import get_course_by_id
14
from courseware.field_overrides import OverrideFieldData
15
from courseware.testutils import FieldOverrideTestMixin
16 17 18
from lms.djangoapps.ccx.models import CustomCourseForEdX
from lms.djangoapps.ccx.overrides import override_field_for_ccx
from lms.djangoapps.ccx.tests.utils import flatten, iter_blocks
19
from lms.djangoapps.courseware.tests.test_field_overrides import inject_field_overrides
20
from request_cache.middleware import RequestCache
21
from student.tests.factories import AdminFactory
22
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
23 24
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory

25

26
@attr(shard=1)
27 28 29 30 31
@override_settings(
    XBLOCK_FIELD_DATA_WRAPPERS=['lms.djangoapps.courseware.field_overrides:OverrideModulestoreFieldData.wrap'],
    MODULESTORE_FIELD_OVERRIDE_PROVIDERS=['ccx.overrides.CustomCoursesForEdxOverrideProvider'],
)
class TestFieldOverrides(FieldOverrideTestMixin, SharedModuleStoreTestCase):
32 33 34
    """
    Make sure field overrides behave in the expected manner.
    """
cewing committed
35
    MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
36

37 38
    @classmethod
    def setUpClass(cls):
39
        """
40
        Course is created here and shared by all the class's tests.
41
        """
42 43 44
        super(TestFieldOverrides, cls).setUpClass()
        cls.course = CourseFactory.create()
        cls.course.enable_ccx = True
45 46

        # Create a course outline
47 48 49
        start = datetime.datetime(2010, 5, 12, 2, 42, tzinfo=pytz.UTC)
        due = datetime.datetime(2010, 7, 7, 0, 0, tzinfo=pytz.UTC)
        chapters = [ItemFactory.create(start=start, parent=cls.course)
50 51 52 53 54 55 56
                    for _ in xrange(2)]
        sequentials = flatten([
            [ItemFactory.create(parent=chapter) for _ in xrange(2)]
            for chapter in chapters])
        verticals = flatten([
            [ItemFactory.create(due=due, parent=sequential) for _ in xrange(2)]
            for sequential in sequentials])
57
        blocks = flatten([  # pylint: disable=unused-variable
58 59 60
            [ItemFactory.create(parent=vertical) for _ in xrange(2)]
            for vertical in verticals])

61 62 63 64 65 66
    def setUp(self):
        """
        Set up tests
        """
        super(TestFieldOverrides, self).setUp()

cewing committed
67
        self.ccx = ccx = CustomCourseForEdX(
68
            course_id=self.course.id,
cewing committed
69
            display_name='Test CCX',
70
            coach=AdminFactory.create())
cewing committed
71
        ccx.save()
72

cewing committed
73 74 75
        patch = mock.patch('ccx.overrides.get_current_ccx')
        self.get_ccx = get_ccx = patch.start()
        get_ccx.return_value = ccx
76 77
        self.addCleanup(patch.stop)

78 79
        self.addCleanup(RequestCache.clear_request_cache)

80
        inject_field_overrides(iter_blocks(ccx.course), self.course, AdminFactory.create())
81

82 83 84
        self.ccx_key = CCXLocator.from_course_locator(self.course.id, ccx.id)
        self.ccx_course = get_course_by_id(self.ccx_key, depth=None)

cewing committed
85
        def cleanup_provider_classes():
86 87 88 89
            """
            After everything is done, clean up by un-doing the change to the
            OverrideFieldData object that is done during the wrap method.
            """
cewing committed
90 91 92
            OverrideFieldData.provider_classes = None
        self.addCleanup(cleanup_provider_classes)

93 94 95 96
    def test_override_start(self):
        """
        Test that overriding start date on a chapter works.
        """
cewing committed
97
        ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
98
        chapter = self.ccx_course.get_children()[0]
cewing committed
99 100
        override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)
        self.assertEquals(chapter.start, ccx_start)
101

102
    def test_override_num_queries_new_field(self):
103
        """
104
        Test that for creating new field executed only create query
105 106
        """
        ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
107
        chapter = self.ccx_course.get_children()[0]
108 109 110 111 112 113
        # One outer SAVEPOINT/RELEASE SAVEPOINT pair around everything caused by the
        # transaction.atomic decorator wrapping override_field_for_ccx.
        # One SELECT and one INSERT.
        # One inner SAVEPOINT/RELEASE SAVEPOINT pair around the INSERT caused by the
        # transaction.atomic down in Django's get_or_create()/_create_object_from_params().
        with self.assertNumQueries(6):
114 115 116 117 118 119 120 121
            override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)

    def test_override_num_queries_update_existing_field(self):
        """
        Test that overriding existing field executed create, fetch and update queries.
        """
        ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
        new_ccx_start = datetime.datetime(2015, 12, 25, 00, 00, tzinfo=pytz.UTC)
122
        chapter = self.ccx_course.get_children()[0]
123
        override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)
124
        with self.assertNumQueries(3):
125 126 127 128 129 130 131
            override_field_for_ccx(self.ccx, chapter, 'start', new_ccx_start)

    def test_override_num_queries_field_value_not_changed(self):
        """
        Test that if value of field does not changed no query execute.
        """
        ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
132
        chapter = self.ccx_course.get_children()[0]
133
        override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)
134
        with self.assertNumQueries(2):      # 2 savepoints
135 136 137 138 139 140 141
            override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)

    def test_overriden_field_access_produces_no_extra_queries(self):
        """
        Test no extra queries when accessing an overriden field more than once.
        """
        ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
142
        chapter = self.ccx_course.get_children()[0]
143 144 145 146 147 148
        # One outer SAVEPOINT/RELEASE SAVEPOINT pair around everything caused by the
        # transaction.atomic decorator wrapping override_field_for_ccx.
        # One SELECT and one INSERT.
        # One inner SAVEPOINT/RELEASE SAVEPOINT pair around the INSERT caused by the
        # transaction.atomic down in Django's get_or_create()/_create_object_from_params().
        with self.assertNumQueries(6):
149 150
            override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)

151 152 153 154
    def test_override_is_inherited(self):
        """
        Test that sequentials inherit overridden start date from chapter.
        """
cewing committed
155
        ccx_start = datetime.datetime(2014, 12, 25, 00, 00, tzinfo=pytz.UTC)
156
        chapter = self.ccx_course.get_children()[0]
cewing committed
157 158 159
        override_field_for_ccx(self.ccx, chapter, 'start', ccx_start)
        self.assertEquals(chapter.get_children()[0].start, ccx_start)
        self.assertEquals(chapter.get_children()[1].start, ccx_start)
160 161 162 163 164 165 166

    def test_override_is_inherited_even_if_set_in_mooc(self):
        """
        Test that a due date set on a chapter is inherited by grandchildren
        (verticals) even if a due date is set explicitly on grandchildren in
        the mooc.
        """
cewing committed
167
        ccx_due = datetime.datetime(2015, 1, 1, 00, 00, tzinfo=pytz.UTC)
168
        chapter = self.ccx_course.get_children()[0]
169
        chapter.display_name = 'itsme!'
cewing committed
170
        override_field_for_ccx(self.ccx, chapter, 'due', ccx_due)
171
        vertical = chapter.get_children()[0].get_children()[0]
cewing committed
172
        self.assertEqual(vertical.due, ccx_due)