test_managers.py 6.99 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
# pylint: disable=invalid-name, attribute-defined-outside-init
"""
Tests for basic common operations related to Course Action State managers
"""
from ddt import ddt, data
from django.test import TestCase
from collections import namedtuple
from opaque_keys.edx.locations import CourseLocator
from course_action_state.models import CourseRerunState
from course_action_state.managers import CourseActionStateItemNotFoundError


# Sequence of Action models to be tested with ddt.
COURSE_ACTION_STATES = (CourseRerunState, )


class TestCourseActionStateManagerBase(TestCase):
    """
    Base class for testing Course Action State Managers.
    """
    def setUp(self):
        self.course_key = CourseLocator("test_org", "test_course_num", "test_run")


@ddt
class TestCourseActionStateManager(TestCourseActionStateManagerBase):
    """
    Test class for testing the CourseActionStateManager.
    """
    @data(*COURSE_ACTION_STATES)
    def test_update_state_allow_not_found_is_false(self, action_class):
        with self.assertRaises(CourseActionStateItemNotFoundError):
            action_class.objects.update_state(self.course_key, "fake_state", allow_not_found=False)

    @data(*COURSE_ACTION_STATES)
    def test_update_state_allow_not_found(self, action_class):
        action_class.objects.update_state(self.course_key, "initial_state", allow_not_found=True)
        self.assertIsNotNone(
            action_class.objects.find_first(course_key=self.course_key)
        )

    @data(*COURSE_ACTION_STATES)
    def test_delete(self, action_class):
        obj = action_class.objects.update_state(self.course_key, "initial_state", allow_not_found=True)
        action_class.objects.delete(obj.id)
        with self.assertRaises(CourseActionStateItemNotFoundError):
            action_class.objects.find_first(course_key=self.course_key)


@ddt
class TestCourseActionUIStateManager(TestCourseActionStateManagerBase):
    """
    Test class for testing the CourseActionUIStateManager.
    """
    def init_course_action_states(self, action_class):
        """
        Creates course action state entries with different states for the given action model class.
        Creates both displayable (should_display=True) and non-displayable (should_display=False) entries.
        """
        def create_course_states(starting_course_num, ending_course_num, state, should_display=True):
            """
            Creates a list of course state tuples by creating unique course locators with course-numbers
            from starting_course_num to ending_course_num.
            """
            CourseState = namedtuple('CourseState', 'course_key, state, should_display')
            return [
                CourseState(CourseLocator("org", "course", "run" + str(num)), state, should_display)
                for num in range(starting_course_num, ending_course_num)
            ]

        NUM_COURSES_WITH_STATE1 = 3
        NUM_COURSES_WITH_STATE2 = 3
        NUM_COURSES_WITH_STATE3 = 3
        NUM_COURSES_NON_DISPLAYABLE = 3

        # courses with state1 and should_display=True
        self.courses_with_state1 = create_course_states(
            0,
            NUM_COURSES_WITH_STATE1,
            'state1'
        )
        # courses with state2 and should_display=True
        self.courses_with_state2 = create_course_states(
            NUM_COURSES_WITH_STATE1,
            NUM_COURSES_WITH_STATE1 + NUM_COURSES_WITH_STATE2,
            'state2'
        )
        # courses with state3 and should_display=True
        self.courses_with_state3 = create_course_states(
            NUM_COURSES_WITH_STATE1 + NUM_COURSES_WITH_STATE2,
            NUM_COURSES_WITH_STATE1 + NUM_COURSES_WITH_STATE2 + NUM_COURSES_WITH_STATE3,
            'state3'
        )
        # all courses with should_display=True
        self.course_actions_displayable_states = (
            self.courses_with_state1 + self.courses_with_state2 + self.courses_with_state3
        )
        # courses with state3 and should_display=False
        self.courses_with_state3_non_displayable = create_course_states(
            NUM_COURSES_WITH_STATE1 + NUM_COURSES_WITH_STATE2 + NUM_COURSES_WITH_STATE3,
            NUM_COURSES_WITH_STATE1 + NUM_COURSES_WITH_STATE2 + NUM_COURSES_WITH_STATE3 + NUM_COURSES_NON_DISPLAYABLE,
            'state3',
            should_display=False,
        )

        # create course action states for all courses
        for CourseState in (self.course_actions_displayable_states + self.courses_with_state3_non_displayable):
            action_class.objects.update_state(
                CourseState.course_key,
                CourseState.state,
                should_display=CourseState.should_display,
                allow_not_found=True
            )

    def assertCourseActionStatesEqual(self, expected, found):
        """Asserts that the set of course keys in the expected state equal those that are found"""
        self.assertSetEqual(
            set(course_action_state.course_key for course_action_state in expected),
            set(course_action_state.course_key for course_action_state in found))

    @data(*COURSE_ACTION_STATES)
    def test_find_all_for_display(self, action_class):
        self.init_course_action_states(action_class)
        self.assertCourseActionStatesEqual(
            self.course_actions_displayable_states,
            action_class.objects.find_all(should_display=True),
        )

    @data(*COURSE_ACTION_STATES)
    def test_find_all_for_display_filter_exclude(self, action_class):
        self.init_course_action_states(action_class)
        for course_action_state, filter_state, exclude_state in (
            (self.courses_with_state1, 'state1', None),  # filter for state1
            (self.courses_with_state2, 'state2', None),  # filter for state2
            (self.courses_with_state2 + self.courses_with_state3, None, 'state1'),  # exclude state1
            (self.courses_with_state1 + self.courses_with_state3, None, 'state2'),  # exclude state2
            (self.courses_with_state1, 'state1', 'state2'),  # filter for state1, exclude state2
            ([], 'state1', 'state1'),  # filter for state1, exclude state1
        ):
            self.assertCourseActionStatesEqual(
                course_action_state,
                action_class.objects.find_all(
                    exclude_args=({'state': exclude_state} if exclude_state else None),
                    should_display=True,
                    **({'state': filter_state} if filter_state else {})
                )
            )

    def test_kwargs_in_update_state(self):
        destination_course_key = CourseLocator("org", "course", "run")
        source_course_key = CourseLocator("source_org", "source_course", "source_run")
        CourseRerunState.objects.update_state(
            course_key=destination_course_key,
            new_state='state1',
            allow_not_found=True,
            source_course_key=source_course_key,
        )
        found_action_state = CourseRerunState.objects.find_first(course_key=destination_course_key)
        self.assertEquals(source_course_key, found_action_state.source_course_key)