class StudioAPITests(TestCase): def setUp(self): super().setUp() self.client = mock.Mock() self.api = StudioAPI(self.client) def make_studio_data(self, run, add_pacing=True, add_schedule=True): key = CourseKey.from_string(run.key) data = { 'title': run.title, 'org': key.org, 'number': key.course, 'run': key.run, 'team': [], } if add_pacing: data['pacing_type'] = run.pacing_type if add_schedule: data['schedule'] = { 'start': serialize_datetime(run.start), 'end': serialize_datetime(run.end), } return data def test_create_rerun(self): run1 = CourseRunFactory() run2 = CourseRunFactory(course=run1.course) self.api.create_course_rerun_in_studio(run2, run1.key) expected_data = self.make_studio_data(run2) self.assertEqual(self.client.course_runs.call_args_list, [mock.call(run1.key)]) self.assertEqual( self.client.course_runs.return_value.rerun.post.call_args_list[0] [0][0], expected_data) def test_create_run(self): run = CourseRunFactory() self.api.create_course_run_in_studio(run) expected_data = self.make_studio_data(run) self.assertEqual(self.client.course_runs.post.call_args_list[0][0][0], expected_data) def test_update_run(self): run = CourseRunFactory() self.api.update_course_run_details_in_studio(run) expected_data = self.make_studio_data(run, add_pacing=False, add_schedule=False) self.assertEqual(self.client.course_runs.call_args_list, [mock.call(run.key)]) self.assertEqual( self.client.course_runs.return_value.patch.call_args_list[0][0][0], expected_data)
class StudioAPITests(OAuth2Mixin, APITestCase): def setUp(self): super().setUp() self.mock_access_token() self.api = StudioAPI(self.partner) self.studio_url = self.partner.studio_url def make_studio_data(self, run, add_pacing=True, add_schedule=True, team=None): key = CourseKey.from_string(run.key) data = { 'title': run.title, 'org': key.org, 'number': key.course, 'run': key.run, 'team': team or [], } if add_pacing: data['pacing_type'] = run.pacing_type if add_schedule: data['schedule'] = { 'start': serialize_datetime(run.start), 'end': serialize_datetime(run.end), } return data def assert_data_generated_correctly(self, course_run, expected_team_data, creating=False): course = course_run.course expected = { 'title': course_run.title_override or course.title, 'org': course.organizations.first().key, 'number': course.number, 'run': StudioAPI.calculate_course_run_key_run_value( course.number, course_run.start_date_temporary), 'schedule': { 'start': serialize_datetime(course_run.start_date_temporary), 'end': serialize_datetime(course_run.end_date_temporary), }, 'team': expected_team_data, 'pacing_type': course_run.pacing_type_temporary, } assert StudioAPI.generate_data_for_studio_api( course_run, creating=creating) == expected def test_create_rerun(self): run1 = CourseRunFactory() run2 = CourseRunFactory(course=run1.course) expected_data = self.make_studio_data(run2) responses.add(responses.POST, f'{self.studio_url}api/v1/course_runs/{run1.key}/rerun/', match=[responses.json_params_matcher(expected_data)]) self.api.create_course_rerun_in_studio(run2, run1.key) def test_create_run(self): run = CourseRunFactory() expected_data = self.make_studio_data(run) responses.add(responses.POST, f'{self.studio_url}api/v1/course_runs/', match=[responses.json_params_matcher(expected_data)]) self.api.create_course_run_in_studio(run) def test_update_run(self): run = CourseRunFactory() expected_data = self.make_studio_data(run, add_pacing=False, add_schedule=False) responses.add(responses.PATCH, f'{self.studio_url}api/v1/course_runs/{run.key}/', match=[responses.json_params_matcher(expected_data)]) self.api.update_course_run_details_in_studio(run) @ddt.data( *product(range(1, 5), ['1T2017']), *product(range(5, 9), ['2T2017']), *product(range(9, 13), ['3T2017']), ) @ddt.unpack def test_calculate_course_run_key_run_value(self, month, expected): start = datetime.datetime(2017, month, 1) assert StudioAPI.calculate_course_run_key_run_value( 'NONE', start=start) == expected def test_generate_data_for_studio_api(self): run = CourseRunFactory() editor = CourseEditorFactory(course=run.course) team = [ { 'user': editor.user.username, 'role': 'instructor', }, ] assert StudioAPI.generate_data_for_studio_api( run, True) == self.make_studio_data(run, team=team) def test_generate_data_for_studio_api_without_team(self): run = CourseRunFactory() with mock.patch('course_discovery.apps.api.utils.logger.warning' ) as mock_logger: assert StudioAPI.generate_data_for_studio_api( run, True) == self.make_studio_data(run) mock_logger.assert_called_with( 'No course team admin specified for course [%s]. This may result in a Studio course run ' 'being created without a course team.', run.key.split('/')[1]) def test_calculate_course_run_key_run_value_with_multiple_runs_per_trimester( self): start = datetime.datetime(2017, 2, 1) CourseRunFactory(key='course-v1:TestX+Testing101x+1T2017') assert StudioAPI.calculate_course_run_key_run_value('TestX', start) == '1T2017a' CourseRunFactory(key='course-v1:TestX+Testing101x+1T2017a') assert StudioAPI.calculate_course_run_key_run_value('TestX', start) == '1T2017b' def test_update_course_run_image_in_studio_without_course_image(self): run = CourseRunFactory(course__image=None) with mock.patch( 'course_discovery.apps.api.utils.logger') as mock_logger: self.api.update_course_run_image_in_studio(run) mock_logger.warning.assert_called_with( 'Card image for course run [%d] cannot be updated. The related course [%d] has no image defined.', run.id, run.course.id)
class StudioAPITests(TestCase): def setUp(self): super().setUp() self.client = mock.Mock() self.api = StudioAPI(self.client) def make_studio_data(self, run, add_pacing=True, add_schedule=True, team=None): key = CourseKey.from_string(run.key) data = { 'title': run.title, 'org': key.org, 'number': key.course, 'run': key.run, 'team': team or [], } if add_pacing: data['pacing_type'] = run.pacing_type if add_schedule: data['schedule'] = { 'start': serialize_datetime(run.start), 'end': serialize_datetime(run.end), } return data def assert_data_generated_correctly(self, course_run, expected_team_data, creating=False): course = course_run.course expected = { 'title': course_run.title_override or course.title, 'org': course.organizations.first().key, 'number': course.number, 'run': StudioAPI.calculate_course_run_key_run_value( course.number, course_run.start_date_temporary), 'schedule': { 'start': serialize_datetime(course_run.start_date_temporary), 'end': serialize_datetime(course_run.end_date_temporary), }, 'team': expected_team_data, 'pacing_type': course_run.pacing_type_temporary, } self.assertEqual( StudioAPI.generate_data_for_studio_api(course_run, creating=creating), expected) def test_create_rerun(self): run1 = CourseRunFactory() run2 = CourseRunFactory(course=run1.course) self.api.create_course_rerun_in_studio(run2, run1.key) expected_data = self.make_studio_data(run2) self.assertEqual(self.client.course_runs.call_args_list, [mock.call(run1.key)]) self.assertEqual( self.client.course_runs.return_value.rerun.post.call_args_list[0] [0][0], expected_data) def test_create_run(self): run = CourseRunFactory() self.api.create_course_run_in_studio(run) expected_data = self.make_studio_data(run) self.assertEqual(self.client.course_runs.post.call_args_list[0][0][0], expected_data) def test_update_run(self): run = CourseRunFactory() self.api.update_course_run_details_in_studio(run) expected_data = self.make_studio_data(run, add_pacing=False, add_schedule=False) self.assertEqual(self.client.course_runs.call_args_list, [mock.call(run.key)]) self.assertEqual( self.client.course_runs.return_value.patch.call_args_list[0][0][0], expected_data) @ddt.data( *product(range(1, 5), ['1T2017']), *product(range(5, 9), ['2T2017']), *product(range(9, 13), ['3T2017']), ) @ddt.unpack def test_calculate_course_run_key_run_value(self, month, expected): start = datetime.datetime(2017, month, 1) self.assertEqual( StudioAPI.calculate_course_run_key_run_value('NONE', start=start), expected) def test_generate_data_for_studio_api(self): run = CourseRunFactory() editor = CourseEditorFactory(course=run.course) team = [ { 'user': editor.user.username, 'role': 'instructor', }, ] self.assertEqual(StudioAPI.generate_data_for_studio_api(run, True), self.make_studio_data(run, team=team)) def test_generate_data_for_studio_api_without_team(self): run = CourseRunFactory() with mock.patch('course_discovery.apps.api.utils.logger.warning' ) as mock_logger: self.assertEqual(StudioAPI.generate_data_for_studio_api(run, True), self.make_studio_data(run)) mock_logger.assert_called_with( 'No course team admin specified for course [%s]. This may result in a Studio course run ' 'being created without a course team.', run.key.split('/')[1]) def test_calculate_course_run_key_run_value_with_multiple_runs_per_trimester( self): start = datetime.datetime(2017, 2, 1) CourseRunFactory(key='course-v1:TestX+Testing101x+1T2017') self.assertEqual( StudioAPI.calculate_course_run_key_run_value('TestX', start), '1T2017a') CourseRunFactory(key='course-v1:TestX+Testing101x+1T2017a') self.assertEqual( StudioAPI.calculate_course_run_key_run_value('TestX', start), '1T2017b') def test_update_course_run_image_in_studio_without_course_image(self): run = CourseRunFactory(course__image=None) with mock.patch( 'course_discovery.apps.api.utils.logger') as mock_logger: self.api.update_course_run_image_in_studio(run) mock_logger.warning.assert_called_with( 'Card image for course run [%d] cannot be updated. The related course [%d] has no image defined.', run.id, run.course.id)