Example #1
0
    def test_create(self):
        """
        Ensure the API supports creation of Programs with a valid organization.
        """
        # Create a valid organization
        OrganizationFactory.create(key="test-org-key", display_name="test-org-display_name")

        data = self._build_post_data()
        # Add the valid organization in POST data while creating a Program
        data["organizations"] = [{"key": "test-org-key"}]
        response = self._make_request(method='post', data=data, admin=True)

        self.assertEqual(response.status_code, 201)

        program = Program.objects.all().last()
        self.assertEqual(
            response.data,
            {
                'name': data['name'],
                'subtitle': data['subtitle'],
                'category': data['category'],
                'status': data['status'],
                'organizations': [{'key': 'test-org-key', 'display_name': 'test-org-display_name'}],
                'course_codes': [],
                'id': ANY,
                'created': ANY,
                'modified': ANY,
                'marketing_slug': '',
                'banner_image_urls': {},
                'uuid': str(program.uuid),
            }
        )
Example #2
0
    def test_missing_fields(self, field):
        """
        Ensure that missing fields cause validation errors if required, and create with correct defaults otherwise.
        """
        defaults = {
            'subtitle': '',
            'status': ProgramStatus.UNPUBLISHED,
        }
        # Create a valid organization
        OrganizationFactory.create(key='test-org-key',
                                   display_name='test-org-display_name')

        data = self._build_post_data()
        # Add the valid organization in POST data while creating a Program
        data['organizations'] = [{'key': 'test-org-key'}]

        del data[field]
        if field in defaults:
            expected_status = 201
        else:
            expected_status = 400

        response = self._make_request(method='post', data=data, admin=True)
        self.assertEqual(response.status_code, expected_status)
        if expected_status == 201:
            self.assertEqual(response.data[field], defaults[field])
        else:
            self.assertIn('field is required', response.data[field][0])
Example #3
0
    def test_missing_fields(self, field):
        """
        Ensure that missing fields cause validation errors if required, and create with correct defaults otherwise.
        """
        defaults = {
            'subtitle': '',
            'status': ProgramStatus.UNPUBLISHED,
        }
        # Create a valid organization
        OrganizationFactory.create(key='test-org-key', display_name='test-org-display_name')

        data = self._build_post_data()
        # Add the valid organization in POST data while creating a Program
        data['organizations'] = [{'key': 'test-org-key'}]

        del data[field]
        if field in defaults:
            expected_status = 201
        else:
            expected_status = 400

        response = self._make_request(method='post', data=data, admin=True)
        self.assertEqual(response.status_code, expected_status)
        if expected_status == 201:
            self.assertEqual(response.data[field], defaults[field])
        else:
            self.assertIn('field is required', response.data[field][0])
Example #4
0
    def test_create(self):
        """
        Ensure the API supports creation of Programs with a valid organization.
        """
        # Create a valid organization
        OrganizationFactory.create(key="test-org-key", display_name="test-org-display_name")

        data = self._build_post_data()
        # Add the valid organization in POST data while creating a Program
        data["organizations"] = [{"key": "test-org-key"}]
        response = self._make_request(method='post', data=data, admin=True)

        self.assertEqual(response.status_code, 201)
        self.assertEqual(
            response.data,
            {
                u"name": data["name"],
                u"subtitle": data["subtitle"],
                u"category": data["category"],
                u"status": data["status"],
                u"organizations": [{"key": "test-org-key", "display_name": "test-org-display_name"}],
                u"course_codes": [],
                u"id": ANY,
                u"created": ANY,
                u"modified": ANY,
                u'marketing_slug': '',
                u'banner_image_urls': {}
            }
        )
Example #5
0
 def test_list_unauthorized(self):
     """
     Ensure the API prevents unauthorized users from listing organizations.
     """
     for _ in range(3):
         OrganizationFactory.create()
     client = self.get_authenticated_client(Role.LEARNERS)
     response = client.get(reverse("api:v1:organizations-list"))
     self.assertEqual(response.status_code, 403)
Example #6
0
 def test_list_unauthorized(self):
     """
     Ensure the API prevents unauthorized users from listing organizations.
     """
     for _ in range(3):
         OrganizationFactory.create()
     client = self.get_authenticated_client(Role.LEARNERS)
     response = client.get(reverse("api:v1:organizations-list"))
     self.assertEqual(response.status_code, 403)
Example #7
0
 def test_list(self):
     """
     Ensure the API supports listing of Organizations by admins.
     """
     for _ in range(3):
         OrganizationFactory.create()
     client = self.get_authenticated_client(Role.ADMINS)
     response = client.get(reverse("api:v1:organizations-list"))
     self.assertEqual(response.status_code, 200)
     self.assertEqual(len(response.data['results']), 3)  # pylint: disable=no-member
Example #8
0
 def test_list(self):
     """
     Ensure the API supports listing of Organizations by admins.
     """
     for _ in range(3):
         OrganizationFactory.create()
     client = self.get_authenticated_client(Role.ADMINS)
     response = client.get(reverse("api:v1:organizations-list"))
     self.assertEqual(response.status_code, 200)
     self.assertEqual(len(response.data['results']), 3)  # pylint: disable=no-member
Example #9
0
    def test_create_with_invalid_org_data(self):
        """
        Ensure the create Programs API fails if provided organization is
        invalid or there are multiple organizations.
        """
        # validate without providing parameter 'organization' in request data
        data = self._build_post_data()
        error_msg = u'This field is required.'
        self._validate_org_data_errors(request_data=data,
                                       validation_error=error_msg)

        # validate with empty list as value for the parameter 'organization' in
        # request data
        error_msg = u'Provide exactly one valid/existing Organization while creating a Program.'
        self._validate_org_data_errors(request_data=data,
                                       validation_error=error_msg,
                                       organizations_data=[])

        # validate with non existing organization key as value for the parameter
        # 'organization' in request data
        org_key = "non-existing-org-key"
        error_msg = u"Provided Organization with key '{org_key}' doesn't exist.".format(
            org_key=org_key)
        self._validate_org_data_errors(request_data=data,
                                       validation_error=error_msg,
                                       organizations_data=[{
                                           "key": org_key
                                       }])

        # create two valid organizations
        OrganizationFactory.create(key="test-org-key-1",
                                   display_name="test-org-display_name-1")
        OrganizationFactory.create(key="test-org-key-2",
                                   display_name="test-org-display_name-2")
        # now add these multiple valid organizations in POST data while creating
        # a Program and test that the user get validation error for providing
        # multiple organizations
        error_msg = u'Provide exactly one valid/existing Organization while creating a Program.'
        self._validate_org_data_errors(request_data=data,
                                       validation_error=error_msg,
                                       organizations_data=[
                                           {
                                               "key": "test-org-key-2"
                                           },
                                           {
                                               "key": "test-org-key-2"
                                           },
                                       ])
Example #10
0
    def test_invalid_nested_run_modes(self, invalid_mode):
        """
        Ensure that invalid nested run mode data causes a 400 response.
        """
        org = OrganizationFactory.create()
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(organization=org)

        patch_data = {
            u'course_codes': [
                {
                    u'key': course_code.key,
                    u'organization': {
                        u'key': course_code.organization.key,
                    },
                    u'run_modes': [invalid_mode],
                },
            ],
        }
        response = self._make_request(program_id=program.id,
                                      admin=True,
                                      method='patch',
                                      data=patch_data)
        self.assertEqual(response.status_code, 400)
Example #11
0
    def test_program_completion_filtering(self, status, is_admin):
        """Verify that program candidates for completion are filtered."""
        program = ProgramFactory.create(status=status)
        org = OrganizationFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)

        course_code = CourseCodeFactory.create(organization=org)
        program_course_code = ProgramCourseCodeFactory.create(
            program=program,
            course_code=course_code
        )

        course_key = 'org/code/run'
        ProgramCourseRunModeFactory.create(
            course_key=course_key,
            program_course_code=program_course_code,
        )

        complete_run_modes = {'completed_courses': [
            {'course_id': course_key, 'mode': 'verified'},
        ]}

        response = self._make_request(method='post', complete=True, data=complete_run_modes, admin=is_admin)

        self.assertEqual(response.status_code, 200)

        program_ids = response.data['program_ids']
        if status in [ProgramStatus.ACTIVE, ProgramStatus.RETIRED]:
            self.assertEqual(program_ids, [1])
        else:
            # No one should be able to complete deleted or unpublished programs.
            self.assertEqual(program_ids, [])
Example #12
0
    def test_update_run_modes(self):
        """
        Ensure that nested run modes can be updated.
        """
        org = OrganizationFactory.create()
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(organization=org)
        program_course_code = ProgramCourseCodeFactory.create(
            program=program, course_code=course_code)

        for n in range(2):
            ProgramCourseRunModeFactory.create(
                program_course_code=program_course_code,
                course_key='course-v1:org+course+run-{}'.format(n))

        # PATCH the course code's run modes: send one matching an existing record and a second new one
        patch_data = {
            u'course_codes': [
                {
                    u'key':
                    course_code.key,
                    u'organization': {
                        u'key': course_code.organization.key,
                    },
                    u'run_modes': [
                        {
                            u'course_key': u'course-v1:org+course+run-0',
                            u'mode_slug': u'verified',
                        },
                        {
                            u'course_key': u'course-v1:org+course+run-2',
                            u'mode_slug': u'verified',
                            u'start_date': u'2015-12-09T21:20:26.491639Z',
                        },
                    ],
                },
            ],
        }
        response = self._make_request(program_id=program.id,
                                      admin=True,
                                      method='patch',
                                      data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        patched_run_modes = response.data['course_codes'][0]['run_modes']
        self.assertEqual(
            [u'course-v1:org+course+run-0', u'course-v1:org+course+run-2'],
            sorted([rm[u'course_key'] for rm in patched_run_modes]))

        # check models (ensure things were saved properly)
        db_run_modes = ProgramCourseRunMode.objects.filter(
            program_course_code=program_course_code)
        self.assertEqual(
            [u'course-v1:org+course+run-0', u'course-v1:org+course+run-2'],
            sorted([rm.course_key for rm in db_run_modes]))
Example #13
0
    def test_create(self):
        """
        Ensure the API supports creation of Programs with a valid organization.
        """
        # Create a valid organization
        OrganizationFactory.create(key="test-org-key",
                                   display_name="test-org-display_name")

        data = self._build_post_data()
        # Add the valid organization in POST data while creating a Program
        data["organizations"] = [{"key": "test-org-key"}]
        response = self._make_request(method='post', data=data, admin=True)

        self.assertEqual(response.status_code, 201)

        program = Program.objects.all().last()
        self.assertEqual(
            response.data, {
                'name':
                data['name'],
                'subtitle':
                data['subtitle'],
                'category':
                data['category'],
                'status':
                data['status'],
                'organizations': [{
                    'key': 'test-org-key',
                    'display_name': 'test-org-display_name'
                }],
                'course_codes': [],
                'id':
                ANY,
                'created':
                ANY,
                'modified':
                ANY,
                'marketing_slug':
                '',
                'banner_image_urls': {},
                'uuid':
                str(program.uuid),
            })
Example #14
0
    def test_update_run_modes(self):
        """
        Ensure that nested run modes can be updated.
        """
        org = OrganizationFactory.create()
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(organization=org)
        program_course_code = ProgramCourseCodeFactory.create(program=program, course_code=course_code)

        for n in range(2):
            ProgramCourseRunModeFactory.create(
                program_course_code=program_course_code,
                course_key='course-v1:org+course+run-{}'.format(n)
            )

        # PATCH the course code's run modes: send one matching an existing record and a second new one
        patch_data = {
            'course_codes': [
                {
                    'key': course_code.key,
                    'organization': {
                        'key': course_code.organization.key,
                    },
                    'run_modes': [
                        {
                            'course_key': 'course-v1:org+course+run-0',
                            'mode_slug': 'verified',
                        },
                        {
                            'course_key': 'course-v1:org+course+run-2',
                            'mode_slug': 'verified',
                            'start_date': '2015-12-09T21:20:26.491639Z',
                        },
                    ],
                },
            ],
        }
        response = self._make_request(program_id=program.id, admin=True, method='patch', data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        patched_run_modes = response.data['course_codes'][0]['run_modes']
        self.assertEqual(
            ['course-v1:org+course+run-0', 'course-v1:org+course+run-2'],
            sorted([rm['course_key'] for rm in patched_run_modes])
        )

        # check models (ensure things were saved properly)
        db_run_modes = ProgramCourseRunMode.objects.filter(program_course_code=program_course_code)
        self.assertEqual(
            ['course-v1:org+course+run-0', 'course-v1:org+course+run-2'],
            sorted([rm.course_key for rm in db_run_modes])
        )
Example #15
0
    def test_create_with_invalid_org_data(self):
        """
        Ensure the create Programs API fails if provided organization is
        invalid or there are multiple organizations.
        """
        # validate without providing parameter 'organization' in request data
        data = self._build_post_data()
        error_msg = 'This field is required.'
        self._validate_org_data_errors(request_data=data, validation_error=error_msg)

        # validate with empty list as value for the parameter 'organization' in
        # request data
        error_msg = 'Provide exactly one valid/existing Organization while creating a Program.'
        self._validate_org_data_errors(
            request_data=data, validation_error=error_msg, organizations_data=[]
        )

        # validate with non existing organization key as value for the parameter
        # 'organization' in request data
        org_key = "non-existing-org-key"
        error_msg = "Provided Organization with key '{org_key}' doesn't exist.".format(org_key=org_key)
        self._validate_org_data_errors(
            request_data=data, validation_error=error_msg, organizations_data=[{"key": org_key}]
        )

        # create two valid organizations
        OrganizationFactory.create(key="test-org-key-1", display_name="test-org-display_name-1")
        OrganizationFactory.create(key="test-org-key-2", display_name="test-org-display_name-2")
        # now add these multiple valid organizations in POST data while creating
        # a Program and test that the user get validation error for providing
        # multiple organizations
        error_msg = 'Provide exactly one valid/existing Organization while creating a Program.'
        self._validate_org_data_errors(
            request_data=data,
            validation_error=error_msg,
            organizations_data=[
                {"key": "test-org-key-2"},
                {"key": "test-org-key-2"},
            ]
        )
Example #16
0
    def test_update_course_codes(self):
        """
        Ensure that nested program course codes can be updated.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        for n in range(3):
            course_code = CourseCodeFactory.create(
                key='test-course-key-{}'.format(n),
                display_name='test-course-display_name-{}'.format(n),
                organization=org,
            )
            # associate the first and second course codes, not the third.
            if n < 2:
                ProgramCourseCodeFactory.create(program=program,
                                                course_code=course_code)

        # PATCH the course codes: send one already associated, and another not yet associated
        patch_data = {
            u'course_codes': [
                {
                    u'key': 'test-course-key-0',
                    u'organization': {
                        u'key': 'test-org-key',
                    },
                },
                {
                    u'key': 'test-course-key-2',
                    u'organization': {
                        u'key': 'test-org-key',
                    },
                },
            ],
        }
        response = self._make_request(program_id=program.id,
                                      admin=True,
                                      method='patch',
                                      data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        patched_course_codes = response.data['course_codes']
        self.assertEqual([u'test-course-key-0', u'test-course-key-2'],
                         sorted([cc[u'key'] for cc in patched_course_codes]))

        # check models (ensure things were saved properly)
        db_course_codes = ProgramCourseCode.objects.filter(
            program__id=program.id)
        self.assertEqual([u'test-course-key-0', u'test-course-key-2'],
                         sorted(
                             [pcc.course_code.key for pcc in db_course_codes]))
Example #17
0
    def test_create(self):
        """
        Ensure the API supports creation of Programs with a valid organization.
        """
        # Create a valid organization
        OrganizationFactory.create(key="test-org-key",
                                   display_name="test-org-display_name")

        data = self._build_post_data()
        # Add the valid organization in POST data while creating a Program
        data["organizations"] = [{"key": "test-org-key"}]
        response = self._make_request(method='post', data=data, admin=True)

        self.assertEqual(response.status_code, 201)
        self.assertEqual(
            response.data, {
                u"name":
                data["name"],
                u"subtitle":
                data["subtitle"],
                u"category":
                data["category"],
                u"status":
                data["status"],
                u"organizations": [{
                    "key": "test-org-key",
                    "display_name": "test-org-display_name"
                }],
                u"course_codes": [],
                u"id":
                ANY,
                u"created":
                ANY,
                u"modified":
                ANY,
                u'marketing_slug':
                '',
                u'banner_image_urls': {}
            })
Example #18
0
    def test_invalid_nested_course_codes(self, invalid_code):
        """
        Ensure that invalid nested course code data causes a 400 response.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        CourseCodeFactory.create(key='test-course-key', organization=org)

        patch_data = {
            'course_codes': [invalid_code],
        }
        response = self._make_request(program_id=program.id, admin=True, method='patch', data=patch_data)
        self.assertEqual(response.status_code, 400)
Example #19
0
    def test_update_course_codes(self):
        """
        Ensure that nested program course codes can be updated.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        for n in range(3):
            course_code = CourseCodeFactory.create(
                key='test-course-key-{}'.format(n),
                display_name='test-course-display_name-{}'.format(n),
                organization=org,
            )
            # associate the first and second course codes, not the third.
            if n < 2:
                ProgramCourseCodeFactory.create(program=program, course_code=course_code)

        # PATCH the course codes: send one already associated, and another not yet associated
        patch_data = {
            'course_codes': [
                {
                    'key': 'test-course-key-0',
                    'organization': {
                        'key': 'test-org-key',
                    },
                },
                {
                    'key': 'test-course-key-2',
                    'organization': {
                        'key': 'test-org-key',
                    },
                },
            ],
        }
        response = self._make_request(program_id=program.id, admin=True, method='patch', data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        patched_course_codes = response.data['course_codes']
        self.assertEqual(
            ['test-course-key-0', 'test-course-key-2'],
            sorted([cc['key'] for cc in patched_course_codes])
        )

        # check models (ensure things were saved properly)
        db_course_codes = ProgramCourseCode.objects.filter(program__id=program.id)
        self.assertEqual(
            ['test-course-key-0', 'test-course-key-2'],
            sorted([pcc.course_code.key for pcc in db_course_codes])
        )
Example #20
0
    def test_org_list_filter(self):
        """
        """
        org_keys = ('org1', 'org2')
        for org_key in org_keys:
            org = OrganizationFactory.create(key=org_key)
            CourseCodeFactory.create(organization=org)

        client = self.get_authenticated_client(Role.ADMINS)
        for org_key in org_keys:
            response = client.get(reverse("api:v1:course_codes-list"), {"organization": org_key})
            self.assertEqual(response.status_code, 200)
            results = response.data['results']  # pylint: disable=no-member
            self.assertEqual(len(results), 1)
            self.assertEqual(results[0]['organization']['key'], org_key)
Example #21
0
    def test_org_list_filter(self):
        """
        """
        org_keys = (u'org1', u'org2')
        for org_key in org_keys:
            org = OrganizationFactory.create(key=org_key)
            CourseCodeFactory.create(organization=org)

        client = self.get_authenticated_client(Role.ADMINS)
        for org_key in org_keys:
            response = client.get(reverse("api:v1:course_codes-list"),
                                  {"organization": org_key})
            self.assertEqual(response.status_code, 200)
            results = response.data['results']  # pylint: disable=no-member
            self.assertEqual(len(results), 1)
            self.assertEqual(results[0]['organization']['key'], org_key)
Example #22
0
    def test_org_list_filter(self):
        """
        Verify that list results can be filtered by an 'organization' query string argument.
        """
        org_keys = ("org1", "org2")
        for org_key in org_keys:
            org = OrganizationFactory.create(key=org_key)
            program = ProgramFactory.create()
            ProgramOrganizationFactory.create(organization=org, program=program)

        for org_key in org_keys:
            response = self._make_request(admin=True, data={'organization': org_key})
            self.assertEqual(response.status_code, 200)
            results = response.data['results']
            self.assertEqual(len(results), 1)
            self.assertEqual(results[0]['organizations'][0]['key'], org_key)
Example #23
0
    def test_invalid_nested_course_codes(self, invalid_code):
        """
        Ensure that invalid nested course code data causes a 400 response.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        CourseCodeFactory.create(key='test-course-key', organization=org)

        patch_data = {
            u'course_codes': [invalid_code],
        }
        response = self._make_request(program_id=program.id,
                                      admin=True,
                                      method='patch',
                                      data=patch_data)
        self.assertEqual(response.status_code, 400)
Example #24
0
    def test_org_list_filter(self):
        """
        Verify that list results can be filtered by an 'organization' query string argument.
        """
        org_keys = ("org1", "org2")
        for org_key in org_keys:
            org = OrganizationFactory.create(key=org_key)
            program = ProgramFactory.create()
            ProgramOrganizationFactory.create(organization=org,
                                              program=program)

        for org_key in org_keys:
            response = self._make_request(admin=True,
                                          data={'organization': org_key})
            self.assertEqual(response.status_code, 200)
            results = response.data['results']
            self.assertEqual(len(results), 1)
            self.assertEqual(results[0]['organizations'][0]['key'], org_key)
Example #25
0
    def test_update_course_code_display_name(self):
        """
        Ensure that course codes' display names can be updated on the fly from
        request data when referenced as part of nested program updates.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        CourseCodeFactory.create(
            key='test-course-code-key',
            display_name='original-display-name',
            organization=org,
        )

        patch_data = {
            u'course_codes': [
                {
                    u'key': 'test-course-code-key',
                    u'display_name': 'changed-display-name',
                    u'organization': {
                        u'key': 'test-org-key',
                    },
                },
            ],
        }
        response = self._make_request(program_id=program.id,
                                      admin=True,
                                      method='patch',
                                      data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        response_course_code = response.data['course_codes'][0]
        self.assertEqual(response_course_code['display_name'],
                         'changed-display-name')

        # check models (ensure things were saved properly)
        CourseCode.objects.get(
            key='test-course-code-key',
            display_name='changed-display-name',
            organization__key='test-org-key',
        )
Example #26
0
    def test_create_course_codes(self):
        """
        Ensure that course codes can be created on the fly from request data
        when referenced as part of nested program updates.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)

        patch_data = {
            'course_codes': [
                {
                    'key': 'test-course-code-key',
                    'display_name': 'test-course-code-name',
                    'organization': {
                        'key': 'test-org-key',
                    },
                },
            ],
        }
        response = self._make_request(program_id=program.id,
                                      admin=True,
                                      method='patch',
                                      data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        response_course_code = response.data['course_codes'][0]
        self.assertEqual(response_course_code['key'], 'test-course-code-key')
        self.assertEqual(response_course_code['display_name'],
                         'test-course-code-name')
        self.assertEqual(response_course_code['organization']['key'],
                         'test-org-key')

        # check models (ensure things were saved properly)
        db_course_code = CourseCode.objects.get(
            key='test-course-code-key',
            display_name='test-course-code-name',
            organization__key='test-org-key',
        )
        self.assertEqual(db_course_code.programs.count(), 1)
        self.assertEqual(db_course_code.programs.first(), program)
Example #27
0
    def test_invalid_nested_run_modes(self, invalid_mode):
        """
        Ensure that invalid nested run mode data causes a 400 response.
        """
        org = OrganizationFactory.create()
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(organization=org)

        patch_data = {
            'course_codes': [
                {
                    'key': course_code.key,
                    'organization': {
                        'key': course_code.organization.key,
                    },
                    'run_modes': [invalid_mode],
                },
            ],
        }
        response = self._make_request(program_id=program.id, admin=True, method='patch', data=patch_data)
        self.assertEqual(response.status_code, 400)
Example #28
0
    def test_update_course_code_display_name(self):
        """
        Ensure that course codes' display names can be updated on the fly from
        request data when referenced as part of nested program updates.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        CourseCodeFactory.create(
            key='test-course-code-key',
            display_name='original-display-name',
            organization=org,
        )

        patch_data = {
            'course_codes': [
                {
                    'key': 'test-course-code-key',
                    'display_name': 'changed-display-name',
                    'organization': {
                        'key': 'test-org-key',
                    },
                },
            ],
        }
        response = self._make_request(program_id=program.id, admin=True, method='patch', data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        response_course_code = response.data['course_codes'][0]
        self.assertEqual(response_course_code['display_name'], 'changed-display-name')

        # check models (ensure things were saved properly)
        CourseCode.objects.get(
            key='test-course-code-key',
            display_name='changed-display-name',
            organization__key='test-org-key',
        )
Example #29
0
    def test_program_completion_filtering(self, status, is_admin):
        """Verify that program candidates for completion are filtered."""
        program = ProgramFactory.create(status=status)
        org = OrganizationFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)

        course_code = CourseCodeFactory.create(organization=org)
        program_course_code = ProgramCourseCodeFactory.create(
            program=program, course_code=course_code)

        course_key = 'org/code/run'
        ProgramCourseRunModeFactory.create(
            course_key=course_key,
            program_course_code=program_course_code,
        )

        complete_run_modes = {
            'completed_courses': [
                {
                    'course_id': course_key,
                    'mode': 'verified'
                },
            ]
        }

        response = self._make_request(method='post',
                                      complete=True,
                                      data=complete_run_modes,
                                      admin=is_admin)

        self.assertEqual(response.status_code, 200)

        program_ids = response.data['program_ids']
        if status in [ProgramStatus.ACTIVE, ProgramStatus.RETIRED]:
            self.assertEqual(program_ids, [1])
        else:
            # No one should be able to complete deleted or unpublished programs.
            self.assertEqual(program_ids, [])
Example #30
0
    def test_create_course_codes(self):
        """
        Ensure that course codes can be created on the fly from request data
        when referenced as part of nested program updates.
        """
        org = OrganizationFactory.create(key='test-org-key')
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)

        patch_data = {
            u'course_codes': [
                {
                    u'key': 'test-course-code-key',
                    u'display_name': 'test-course-code-name',
                    u'organization': {
                        u'key': 'test-org-key',
                    },
                },
            ],
        }
        response = self._make_request(program_id=program.id, admin=True, method='patch', data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        response_course_code = response.data['course_codes'][0]
        self.assertEqual(response_course_code['key'], 'test-course-code-key')
        self.assertEqual(response_course_code['display_name'], 'test-course-code-name')
        self.assertEqual(response_course_code['organization']['key'], 'test-org-key')

        # check models (ensure things were saved properly)
        db_course_code = CourseCode.objects.get(
            key='test-course-code-key',
            display_name='test-course-code-name',
            organization__key='test-org-key',
        )
        self.assertEqual(db_course_code.programs.count(), 1)
        self.assertEqual(db_course_code.programs.first(), program)
Example #31
0
    def test_create_course_code_with_run_modes(self):
        """
        Ensure that nested program course codes and run modes can be correctly
        created during updates.
        """
        org = OrganizationFactory.create()
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)

        course_codes = [CourseCodeFactory.create(organization=org, key='test-cc-{}'.format(i)) for i in range(2)]

        # associate two course codes with two run modes each, in one PATCH request.
        patch_data = {
            'course_codes': [
                {
                    'key': course_codes[0].key,
                    'organization': {
                        'key': course_codes[0].organization.key,
                    },
                    'run_modes': [
                        {
                            'course_key': 'course-v1:org+{}+run-0'.format(course_codes[0].key),
                            'mode_slug': 'verified',
                            'start_date': '2015-12-09T21:20:26.491639Z',
                        },
                        {
                            'course_key': 'course-v1:org+{}+run-1'.format(course_codes[0].key),
                            'mode_slug': 'verified',
                            'start_date': '2015-12-09T21:20:26.491639Z',
                        },
                    ],
                },
                {
                    'key': course_codes[1].key,
                    'organization': {
                        'key': course_codes[1].organization.key,
                    },
                    'run_modes': [
                        {
                            'course_key': 'course-v1:org+{}+run-0'.format(course_codes[1].key),
                            'mode_slug': 'verified',
                            'start_date': '2015-12-09T21:20:26.491639Z',
                        },
                        {
                            'course_key': 'course-v1:org+{}+run-1'.format(course_codes[1].key),
                            'mode_slug': 'verified',
                            'start_date': '2015-12-09T21:20:26.491639Z',
                        },
                    ],
                },
            ],
        }
        response = self._make_request(program_id=program.id, admin=True, method='patch', data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        response_course_codes = response.data['course_codes']
        response_course_codes = sorted(response_course_codes, key=lambda d: d['key'])
        for course_code, response_course_code in zip(course_codes, response_course_codes):
            self.assertEqual(response_course_code['key'], course_code.key)
            self.assertEqual(response_course_code['organization']['key'], course_code.organization.key)
            self.assertEqual(len(response_course_code['run_modes']), 2)

            # check db values. the following call should throw DoesNotExist if things didn't go as expected
            db_program_course_code = ProgramCourseCode.objects.get(program=program, course_code=course_code)
            run_modes = sorted(db_program_course_code.run_modes.all(), key=lambda o: o.course_key)
            for i, run_mode in enumerate(run_modes):
                self.assertEqual('course-v1:org+{}+run-{}'.format(course_code.key, i), run_mode.course_key)
Example #32
0
    def test_view_with_nested(self):
        """
        Ensure that nested serializers are working in program detail views.
        """
        start_date = datetime.datetime.now(tz=pytz.UTC)
        course_key = "edX/DemoX/Demo_Course"
        run_key = "Demo_Course"

        org = OrganizationFactory.create(key="test-org-key", display_name="test-org-display_name")
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(
            key="test-course-key",
            display_name="test-course-display_name",
            organization=org,
        )

        program_course_code = ProgramCourseCodeFactory.create(program=program, course_code=course_code)

        ProgramCourseRunModeFactory.create(
            course_key=course_key,
            program_course_code=program_course_code,
            start_date=start_date
        )

        response = self._make_request(program_id=program.id, admin=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.data,
            {
                u"name": program.name,
                u"subtitle": program.subtitle,
                u"category": program.category,
                u"status": ProgramStatus.UNPUBLISHED,
                u"organizations": [
                    {
                        u"key": "test-org-key",
                        u"display_name": "test-org-display_name",
                    }
                ],
                u"course_codes": [
                    {
                        u"key": "test-course-key",
                        u"display_name": "test-course-display_name",
                        u"organization": {
                            u"key": "test-org-key",
                            u"display_name": "test-org-display_name",
                        },
                        u"run_modes": [
                            {
                                u"course_key": course_key,
                                u"run_key": run_key,
                                u"mode_slug": "verified",
                                u"sku": '',
                                u"start_date": start_date.strftime(DRF_DATE_FORMAT)
                            }
                        ],
                    }
                ],
                u"id": program.id,
                u"created": program.created.strftime(DRF_DATE_FORMAT),
                u"modified": program.modified.strftime(DRF_DATE_FORMAT),
                u'marketing_slug': program.marketing_slug,
                u'banner_image_urls': {}
            }
        )
Example #33
0
    def test_create_course_code_with_run_modes(self):
        """
        Ensure that nested program course codes and run modes can be correctly
        created during updates.
        """
        org = OrganizationFactory.create()
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)

        course_codes = [
            CourseCodeFactory.create(organization=org,
                                     key='test-cc-{}'.format(i))
            for i in range(2)
        ]

        # associate two course codes with two run modes each, in one PATCH request.
        patch_data = {
            u'course_codes': [
                {
                    u'key':
                    course_codes[0].key,
                    u'organization': {
                        u'key': course_codes[0].organization.key,
                    },
                    u'run_modes': [
                        {
                            u'course_key':
                            u'course-v1:org+{}+run-0'.format(
                                course_codes[0].key),
                            u'mode_slug':
                            u'verified',
                            u'start_date':
                            u'2015-12-09T21:20:26.491639Z',
                        },
                        {
                            u'course_key':
                            u'course-v1:org+{}+run-1'.format(
                                course_codes[0].key),
                            u'mode_slug':
                            u'verified',
                            u'start_date':
                            u'2015-12-09T21:20:26.491639Z',
                        },
                    ],
                },
                {
                    u'key':
                    course_codes[1].key,
                    u'organization': {
                        u'key': course_codes[1].organization.key,
                    },
                    u'run_modes': [
                        {
                            u'course_key':
                            u'course-v1:org+{}+run-0'.format(
                                course_codes[1].key),
                            u'mode_slug':
                            u'verified',
                            u'start_date':
                            u'2015-12-09T21:20:26.491639Z',
                        },
                        {
                            u'course_key':
                            u'course-v1:org+{}+run-1'.format(
                                course_codes[1].key),
                            u'mode_slug':
                            u'verified',
                            u'start_date':
                            u'2015-12-09T21:20:26.491639Z',
                        },
                    ],
                },
            ],
        }
        response = self._make_request(program_id=program.id,
                                      admin=True,
                                      method='patch',
                                      data=patch_data)
        self.assertEqual(response.status_code, 200)

        # check response data
        response_course_codes = response.data['course_codes']
        response_course_codes = sorted(response_course_codes,
                                       key=lambda d: d['key'])
        for course_code, response_course_code in zip(course_codes,
                                                     response_course_codes):
            self.assertEqual(response_course_code['key'], course_code.key)
            self.assertEqual(response_course_code['organization']['key'],
                             course_code.organization.key)
            self.assertEqual(len(response_course_code['run_modes']), 2)

            # check db values. the following call should throw DoesNotExist if things didn't go as expected
            db_program_course_code = ProgramCourseCode.objects.get(
                program=program, course_code=course_code)
            run_modes = sorted(db_program_course_code.run_modes.all(),
                               key=lambda o: o.course_key)
            for i, run_mode in enumerate(run_modes):
                self.assertEqual(
                    'course-v1:org+{}+run-{}'.format(course_code.key, i),
                    run_mode.course_key)
Example #34
0
    def test_view_with_nested(self):
        """
        Ensure that nested serializers are working in program detail views.
        """
        start_date = datetime.datetime.now(tz=pytz.UTC)
        course_key = "edX/DemoX/Demo_Course"
        run_key = "Demo_Course"

        org = OrganizationFactory.create(key="test-org-key",
                                         display_name="test-org-display_name")
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(
            key="test-course-key",
            display_name="test-course-display_name",
            organization=org,
        )

        program_course_code = ProgramCourseCodeFactory.create(
            program=program, course_code=course_code)

        ProgramCourseRunModeFactory.create(
            course_key=course_key,
            program_course_code=program_course_code,
            start_date=start_date)

        response = self._make_request(program_id=program.id, admin=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.data, {
                'name':
                program.name,
                'subtitle':
                program.subtitle,
                'category':
                program.category,
                'status':
                ProgramStatus.UNPUBLISHED,
                'organizations': [{
                    'key': 'test-org-key',
                    'display_name': 'test-org-display_name',
                }],
                'course_codes': [{
                    'key':
                    'test-course-key',
                    'display_name':
                    'test-course-display_name',
                    'organization': {
                        'key': 'test-org-key',
                        'display_name': 'test-org-display_name',
                    },
                    'run_modes':
                    [{
                        'course_key': course_key,
                        'run_key': run_key,
                        'mode_slug': 'verified',
                        'sku': '',
                        'start_date': start_date.strftime(DRF_DATE_FORMAT)
                    }],
                }],
                'id':
                program.id,
                'created':
                program.created.strftime(DRF_DATE_FORMAT),
                'modified':
                program.modified.strftime(DRF_DATE_FORMAT),
                'marketing_slug':
                program.marketing_slug,
                'banner_image_urls': {},
                'uuid':
                unicode(program.uuid),
            })
Example #35
0
    def test_view_with_nested(self):
        """
        Ensure that nested serializers are working in program detail views.
        """
        start_date = datetime.datetime.now(tz=pytz.UTC)
        course_key = "edX/DemoX/Demo_Course"
        run_key = "Demo_Course"

        org = OrganizationFactory.create(key="test-org-key", display_name="test-org-display_name")
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(
            key="test-course-key",
            display_name="test-course-display_name",
            organization=org,
        )

        program_course_code = ProgramCourseCodeFactory.create(program=program, course_code=course_code)

        ProgramCourseRunModeFactory.create(
            course_key=course_key,
            program_course_code=program_course_code,
            start_date=start_date
        )

        response = self._make_request(program_id=program.id, admin=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.data,
            {
                'name': program.name,
                'subtitle': program.subtitle,
                'category': program.category,
                'status': ProgramStatus.UNPUBLISHED,
                'organizations': [
                    {
                        'key': 'test-org-key',
                        'display_name': 'test-org-display_name',
                    }
                ],
                'course_codes': [
                    {
                        'key': 'test-course-key',
                        'display_name': 'test-course-display_name',
                        'organization': {
                            'key': 'test-org-key',
                            'display_name': 'test-org-display_name',
                        },
                        'run_modes': [
                            {
                                'course_key': course_key,
                                'run_key': run_key,
                                'mode_slug': 'verified',
                                'sku': '',
                                'start_date': start_date.strftime(DRF_DATE_FORMAT)
                            }
                        ],
                    }
                ],
                'id': program.id,
                'created': program.created.strftime(DRF_DATE_FORMAT),
                'modified': program.modified.strftime(DRF_DATE_FORMAT),
                'marketing_slug': program.marketing_slug,
                'banner_image_urls': {},
                'uuid': unicode(program.uuid),
            }
        )
Example #36
0
    def test_view_with_nested(self):
        """
        Ensure that nested serializers are working in program detail views.
        """
        start_date = datetime.datetime.now(tz=pytz.UTC)
        course_key = "edX/DemoX/Demo_Course"
        run_key = "Demo_Course"

        org = OrganizationFactory.create(key="test-org-key",
                                         display_name="test-org-display_name")
        program = ProgramFactory.create()
        ProgramOrganizationFactory.create(program=program, organization=org)
        course_code = CourseCodeFactory.create(
            key="test-course-key",
            display_name="test-course-display_name",
            organization=org,
        )

        program_course_code = ProgramCourseCodeFactory.create(
            program=program, course_code=course_code)

        ProgramCourseRunModeFactory.create(
            course_key=course_key,
            program_course_code=program_course_code,
            start_date=start_date)

        response = self._make_request(program_id=program.id, admin=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.data, {
                u"name":
                program.name,
                u"subtitle":
                program.subtitle,
                u"category":
                program.category,
                u"status":
                ProgramStatus.UNPUBLISHED,
                u"organizations": [{
                    u"key": "test-org-key",
                    u"display_name": "test-org-display_name",
                }],
                u"course_codes": [{
                    u"key":
                    "test-course-key",
                    u"display_name":
                    "test-course-display_name",
                    u"organization": {
                        u"key": "test-org-key",
                        u"display_name": "test-org-display_name",
                    },
                    u"run_modes":
                    [{
                        u"course_key": course_key,
                        u"run_key": run_key,
                        u"mode_slug": "verified",
                        u"sku": '',
                        u"start_date": start_date.strftime(DRF_DATE_FORMAT)
                    }],
                }],
                u"id":
                program.id,
                u"created":
                program.created.strftime(DRF_DATE_FORMAT),
                u"modified":
                program.modified.strftime(DRF_DATE_FORMAT),
                u'marketing_slug':
                program.marketing_slug,
                u'banner_image_urls': {}
            })