def test_remove_course_references(self): """ Unit Test: test_remove_course_references """ # Add a course dependency on the test organization api.add_organization_course( self.test_organization, self.test_course_key ) self.assertEqual(len(api.get_organization_courses(self.test_organization)), 1) # Remove the course dependency with self.assertNumQueries(2): api.remove_course_references(self.test_course_key) self.assertEqual(len(api.get_organization_courses(self.test_organization)), 0)
def test_remove_course_references(self): """ Unit Test: test_remove_course_references """ # Add a course dependency on the test organization api.add_organization_course(self.test_organization, self.test_course_key) self.assertEqual( len(api.get_organization_courses(self.test_organization)), 1) # Remove the course dependency with self.assertNumQueries(2): api.remove_course_references(self.test_course_key) self.assertEqual( len(api.get_organization_courses(self.test_organization)), 0)
def get_organization_courses(organization_id): """ Client API operation adapter/wrapper """ if not organizations_enabled(): return [] from organizations import api as organizations_api return organizations_api.get_organization_courses(organization_id)
def get_organization_courses(organization_id): """ Client API operation adapter/wrapper """ if not organizations_enabled(): return [] from organizations import api as organizations_api return organizations_api.get_organization_courses(organization_id)
def get_organization_courses(organization_id): """ Client API operation adapter/wrapper """ if not settings.FEATURES.get('ORGANIZATIONS_APP', False): return [] from organizations import api as organizations_api return organizations_api.get_organization_courses(organization_id)
def get_organization_courses(organization_id): """ Client API operation adapter/wrapper """ if not settings.FEATURES.get('ORGANIZATIONS_APP', False): return [] from organizations import api as organizations_api return organizations_api.get_organization_courses(organization_id)
def test_add_several_organization_courses(self): """ Test that the query_count of bulk_add_organization_courses does not increase when given more organization-course linkages to add. """ org_a = api.add_organization(self.make_organization_data("org_a")) org_b = api.add_organization(self.make_organization_data("org_b")) org_c = api.add_organization(self.make_organization_data("org_c")) course_key_x = CourseKey.from_string("course-v1:x+x+x") course_key_y = CourseKey.from_string("course-v1:y+y+y") course_key_z = CourseKey.from_string("course-v1:z+z+z") # Add linkage A->X. api.add_organization_course(org_a, course_key_x) # Add linkage A->Y, and then remove (actually: deactivate). api.add_organization_course(org_a, course_key_y) api.remove_organization_course(org_a, course_key_y) # 1 query to load list of existing linkages, # 1 query to fetch organizations for existing linkages, # 1 query to ensure all existing linkages active, # 1 query to get organiations for new linkages, # 1 query to create new linkages. with self.assertNumQueries(5): api.bulk_add_organization_courses([ (org_a, course_key_x), # Already existing. (org_a, course_key_x), # Already existing. (org_a, course_key_y), # Reactivation. (org_a, course_key_z), # The rest are new. (org_b, course_key_x), (org_b, course_key_y), (org_b, course_key_z), (org_c, course_key_x), (org_c, course_key_y), (org_c, course_key_z), (org_c, course_key_z), # Redundant. (org_c, course_key_z), # Redundant. ]) assert len(api.get_organization_courses(org_a)) == 3 assert len(api.get_organization_courses(org_b)) == 3 assert len(api.get_organization_courses(org_c)) == 3
def test_dry_run(self, mock_log_info): """ Test that `bulk_add_organization_courses` does nothing when `dry_run` is specified (except logging). """ org_a = api.add_organization(self.make_organization_data("org_a")) course_key_x = CourseKey.from_string("course-v1:x+x+x") api.bulk_add_organization_courses([(org_a, course_key_x)], dry_run=True) assert api.get_organization_courses(org_a) == [] # One for reactivations, one for creations. assert mock_log_info.call_count == 2
def test_validation_errors(self): """ Test the `bulk_add_organization_courses` raises validation errors on bad input, and no organization-course linkages are created. """ valid_org = self.make_organization_data("valid_org") invalid_org = {"description": "org with no short_name!"} valid_course_key = "course-v1:a+b+c" invalid_course_key = "NOT-A-COURSE-KEY" # Any bad org data or bad course key should cause the bulk-add to raise. with self.assertRaises(exceptions.InvalidCourseKeyException): api.bulk_add_organization_courses([ (valid_org, valid_course_key), (valid_org, invalid_course_key), ]) with self.assertRaises(exceptions.InvalidOrganizationException): api.bulk_add_organization_courses([ (valid_org, valid_course_key), (invalid_org, valid_course_key), ]) # In either case, no data should've been written for `valid_org`. assert len(api.get_organization_courses(valid_org)) == 0
def test_end_to_end(self, run_type): """ Test the happy path of the backfill command without any mocking. """ # org_A: already existing, with courses and a library. org_a = add_organization({"short_name": "org_A", "name": "Org A"}) course_a1_key = CourseOverviewFactory(org="org_A", run="1").id CourseOverviewFactory(org="org_A", run="2") LibraryFactory(org="org_A") # Write linkage for org_a->course_a1. # (Linkage for org_a->course_a2 is purposefully left out here; # it should be created by the backfill). add_organization_course(org_a, course_a1_key) # org_B: already existing, but has no content. add_organization({"short_name": "org_B", "name": "Org B"}) # org_C: has a few courses; should be created. CourseOverviewFactory(org="org_C", run="1") CourseOverviewFactory(org="org_C", run="2") # Include an Old Mongo Modulestore -style deprecated course key. # This can be safely removed when Old Mongo Modulestore support is # removed. CourseOverviewFactory( id=CourseLocator.from_string("org_C/toy/3"), org="org_C", run="3", ) # org_D: has both a course and a library; should be created. CourseOverviewFactory(org="org_D", run="1") LibraryFactory(org="org_D") # org_E: just has a library; should be created. LibraryFactory(org="org_E") # Confirm starting condition: # Only orgs are org_A and org_B, and only linkage is org_a->course_a1. assert set( org["short_name"] for org in get_organizations() ) == { "org_A", "org_B" } assert len(get_organization_courses(get_organization_by_short_name('org_A'))) == 1 assert len(get_organization_courses(get_organization_by_short_name('org_B'))) == 0 # Run the backfill. call_command("backfill_orgs_and_org_courses", run_type) if run_type == "--dry": # Confirm ending conditions are the same as the starting conditions. assert set( org["short_name"] for org in get_organizations() ) == { "org_A", "org_B" } assert len(get_organization_courses(get_organization_by_short_name('org_A'))) == 1 assert len(get_organization_courses(get_organization_by_short_name('org_B'))) == 0 else: # Confirm ending condition: # All five orgs present. Each org a has expected number of org-course linkages. assert set( org["short_name"] for org in get_organizations() ) == { "org_A", "org_B", "org_C", "org_D", "org_E" } assert len(get_organization_courses(get_organization_by_short_name('org_A'))) == 2 assert len(get_organization_courses(get_organization_by_short_name('org_B'))) == 0 assert len(get_organization_courses(get_organization_by_short_name('org_C'))) == 3 assert len(get_organization_courses(get_organization_by_short_name('org_D'))) == 1 assert len(get_organization_courses(get_organization_by_short_name('org_E'))) == 0
def test_end_to_end(self): """ Test the happy path of the backfill command without any mocking. """ # org_A: already existing, with courses and a library. org_a = add_organization({"short_name": "org_A", "name": "Org A"}) course_a1_key = CourseOverviewFactory(org="org_A", run="1").id CourseOverviewFactory(org="org_A", run="2") LibraryFactory(org="org_A") # Write linkage for org_a->course_a1. # (Linkage for org_a->course_a2 is purposefully left out here; # it should be created by the backfill). add_organization_course(org_a, course_a1_key) # org_B: already existing, but has no content. add_organization({"short_name": "org_B", "name": "Org B"}) # org_C: has a couple courses; should be created. CourseOverviewFactory(org="org_C", run="1") CourseOverviewFactory(org="org_C", run="2") # org_D: has both a course and a library; should be created. CourseOverviewFactory(org="org_D", run="1") LibraryFactory(org="org_D") # org_E: just has a library; should be created. LibraryFactory(org="org_E") # Confirm starting condition: # Only orgs are org_A and org_B, and only linkage is org_a->course_a1. assert set(org["short_name"] for org in get_organizations()) == {"org_A", "org_B"} assert len( get_organization_courses( get_organization_by_short_name('org_A'))) == 1 assert len( get_organization_courses( get_organization_by_short_name('org_B'))) == 0 # Run the backfill. call_command("backfill_orgs_and_org_courses", "--apply") # Confirm ending condition: # All five orgs present. Each org a has expected number of org-course linkages. assert set(org["short_name"] for org in get_organizations()) == { "org_A", "org_B", "org_C", "org_D", "org_E" } assert len( get_organization_courses( get_organization_by_short_name('org_A'))) == 2 assert len( get_organization_courses( get_organization_by_short_name('org_B'))) == 0 assert len( get_organization_courses( get_organization_by_short_name('org_C'))) == 2 assert len( get_organization_courses( get_organization_by_short_name('org_D'))) == 1 assert len( get_organization_courses( get_organization_by_short_name('org_E'))) == 0
def test_edge_cases(self, mock_log_info): """ Test that bulk_add_organization_courses handles a few edge cases as expected. """ org_a = api.add_organization(self.make_organization_data("org_a")) org_b = api.add_organization(self.make_organization_data("org_b")) course_key_x = CourseKey.from_string("course-v1:x+x+x") course_key_y = CourseKey.from_string("course-v1:y+y+y") course_key_z = CourseKey.from_string("course-v1:z+z+z") # Add linkage A->X api.add_organization_course(org_a, course_key_x) # Add and then remove (under the hood: deactivate) linkage between A->Y. api.add_organization_course(org_a, course_key_y) api.remove_organization_course(org_a, course_key_y) # Add and then remove (under the hood: deactivate) linkage between A->Z. # This should NOT be reactivated, as we don't include it in the bulk_add call. api.add_organization_course(org_a, course_key_z) api.remove_organization_course(org_a, course_key_z) # 1 query to load list of existing linkages, # 1 query to fetch organizations for existing linkages, # 1 query to ensure all existing linkages active, # 1 query to get organiations for new linkages, # 1 query to create new linkages. with self.assertNumQueries(5): api.bulk_add_organization_courses([ # A->X: Existing linkage, should be a no-op. (org_a, course_key_x), # B->Y: Should create new linkage. (org_b, course_key_y), # A->Y: Is an inactive linkage; should be re-activated. (org_a, course_key_y), # B->Y: Is already in this list; shouldn't affect anything. (org_b, course_key_y), # B->Z: Adding with a stringified course id; should work as if we # used the course key object. (org_b, str(course_key_z)), # B->Z: Adding again with the course key object; should be a no-op. (org_b, course_key_z), ]) # Org A was linked to courses X and Y. # Org A also has an inactive link to course Z that we never re-activated. org_a_courses = api.get_organization_courses(org_a) assert {org_course["course_id"] for org_course in org_a_courses } == {"course-v1:x+x+x", "course-v1:y+y+y"} # Org B was linked to courses Y and Z. org_b_courses = api.get_organization_courses(org_b) assert {org_course["course_id"] for org_course in org_b_courses } == {"course-v1:y+y+y", "course-v1:z+z+z"} # Based on logging messages, make sure the expected breakdown of # created vs. reactivated vs. not touched # is true for the org-course linkages passed to `bulk_add_organization_courses`. logged_linkages_to_reactivate = mock_log_info.call_args_list[0][0][2] assert set(logged_linkages_to_reactivate) == { ("org_a", str(course_key_y)), } logged_linkages_to_create = mock_log_info.call_args_list[1][0][2] assert set(logged_linkages_to_create) == { ("org_b", str(course_key_y)), ("org_b", str(course_key_z)), }