def test_add_several_organizations(self): """ Test that the query_count of bulk_add_organizations does not increase when given more organizations. """ existing_org = api.add_organization( self.make_organization_data("existing_org")) api.remove_organization( api.add_organization( self.make_organization_data("org_to_reactivate"))["id"]) # 1 query to load list of existing orgs, # 1 query to filter for only inactive existing orgs, # 1 query for activate-existing, and 1 query for create-new. with self.assertNumQueries(4): api.bulk_add_organizations([ existing_org, existing_org, existing_org, self.make_organization_data("org_to_reactivate"), self.make_organization_data("new_org_1"), self.make_organization_data("new_org_2"), self.make_organization_data("new_org_3"), self.make_organization_data("new_org_4"), self.make_organization_data("new_org_5"), self.make_organization_data("new_org_6"), self.make_organization_data("new_org_7"), self.make_organization_data("new_org_8"), self.make_organization_data("new_org_9"), self.make_organization_data("new_org_9"), # Redundant. self.make_organization_data("new_org_9"), # Redundant. ]) assert len(api.get_organizations()) == 11
def get_organizations(): """ 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_organizations()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) organizations = get_organizations() org_choices = [(org["id"], org["name"]) for org in organizations] org_choices.insert(0, ('', 'None')) self.fields['organization_id'] = forms.TypedChoiceField( choices=org_choices, required=False, coerce=int, empty_value=None) languages = list(settings.CERTIFICATE_TEMPLATE_LANGUAGES.items()) lang_choices = sorted(languages, key=itemgetter(1)) lang_choices.insert(0, (None, 'All Languages')) self.fields['language'] = forms.ChoiceField(choices=lang_choices, required=False)
def test_get_organizations(self): """ Unit Test: test_get_organizations """ api.add_organization({ 'name': 'local_organization_1ßßß', 'description': 'Local Organization 1 Descriptionßßß' }) api.add_organization({ 'name': 'local_organization_2ßßß', 'description': 'Local Organization 2 Descriptionßßß' }) with self.assertNumQueries(1): organizations = api.get_organizations() self.assertEqual(len(organizations), 3) # One from SetUp, two from local
def test_dry_run(self, mock_log_info): """ Test that `bulk_add_organizations` does nothing when `dry_run` is specified (except logging). """ api.bulk_add_organizations( [self.make_organization_data("org_a")], dry_run=True, ) assert api.get_organizations() == [] # One for reactivations, one for creations. assert mock_log_info.call_count == 2
def test_validation_errors(self): """ Test the `bulk_add_organizations` raises validation errors on bad input, and no organizations are created. """ with self.assertRaises(exceptions.InvalidOrganizationException): api.bulk_add_organizations([ self.make_organization_data("valid_org"), { "description": "org with no short_name!" }, ]) assert len(api.get_organizations()) == 0
def get_organizations(): """ Client API operation adapter/wrapper """ if not organizations_enabled(): return [] from organizations import api as organizations_api # Due to the way unit tests run for edx-platform, models are not yet available at the time # of Django admin form instantiation. This unfortunately results in an invocation of the following # workflow, because the test configuration is (correctly) configured to exercise the application # The good news is that this case does not manifest in the Real World, because migrations have # been run ahead of application instantiation and the flag set only when that is truly the case. try: return organizations_api.get_organizations() except DatabaseError: return []
def get_organizations(): """ Client API operation adapter/wrapper """ if not organizations_enabled(): return [] from organizations import api as organizations_api # Due to the way unit tests run for edx-platform, models are not yet available at the time # of Django admin form instantiation. This unfortunately results in an invocation of the following # workflow, because the test configuration is (correctly) configured to exercise the application # The good news is that this case does not manifest in the Real World, because migrations have # been run ahead of application instantiation and the flag set only when that is truly the case. try: return organizations_api.get_organizations() except DatabaseError: return []
def index(request): from organizations.api import get_organizations # org_list = [] course_discovery_meanings = getattr(settings, 'COURSE_DISCOVERY_MEANINGS', {}) org_list = get_organizations() sorted_assoc_list = sorted(org_list, key=lambda organizations: organizations['name']) return render_to_response( "associations/associations.html", { 'organizations': sorted_assoc_list, 'course_discovery_meanings': course_discovery_meanings })
def test_get_organizations(self): """ Unit Test: test_get_organizations """ api.add_organization({ 'name': 'local_organization_1ßßß', 'description': 'Local Organization 1 Descriptionßßß' }) api.add_organization({ 'name': 'local_organization_2ßßß', 'description': 'Local Organization 2 Descriptionßßß' }) with self.assertNumQueries(1): organizations = api.get_organizations() self.assertEqual(len(organizations), 3) # One from SetUp, two from local
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 get(self, request, *args, **kwargs): """Returns organization list as json.""" organizations = get_organizations() org_names_list = [(org["short_name"]) for org in organizations] return HttpResponse(dump_js_escaped_json(org_names_list), content_type='application/json; charset=utf-8')
def get(self, request, *args, **kwargs): # lint-amnesty, pylint: disable=unused-argument """Returns organization list as json.""" organizations = get_organizations() org_names_list = [(org["short_name"]) for org in organizations] return HttpResponse(dump_js_escaped_json(org_names_list), content_type='application/json; charset=utf-8') # lint-amnesty, pylint: disable=http-response-with-content-type-json
def test_edge_cases(self, mock_log_info): """ Test that bulk_add_organizations handles a few edge cases as expected. """ # Add three orgs, and remove all but the first. # Use capitalized name to confirm case insensitivity when checking # for existing orgs. api.add_organization(self.make_organization_data("EXISTING_ORG")) api.remove_organization( api.add_organization( self.make_organization_data("org_to_reactivate"))["id"]) api.remove_organization( api.add_organization( self.make_organization_data("org_to_leave_inactive"))["id"]) # 1 query to load list of existing orgs, # 1 query to filter for only inactive existing orgs, # 1 query for create, and 1 query for update. with self.assertNumQueries(4): api.bulk_add_organizations([ # New organization. self.make_organization_data("org_X"), # Modify existing active organization; should be no-op. { **self.make_organization_data("existing_org"), "description": "this name should be ignored" }, # Deleted organizations are still stored in the DB as "inactive". # Bulk-adding should reactivate it. self.make_organization_data("org_to_reactivate"), # Another new organizaiton. self.make_organization_data("org_Y"), # Another org with same short name (case-insensitively) # as first new organization; should be ignored. { **self.make_organization_data("ORG_x"), "name": "this name should be ignored" } ]) # There should exist the already-existing org, the org that existed as inactive # but is not activated, and the two new orgs. # This should not include `org_to_leave_inactive`. organizations = api.get_organizations() assert {organization["short_name"] for organization in organizations } == {"EXISTING_ORG", "org_to_reactivate", "org_X", "org_Y"} # Organization dicts with already-taken short_names shouldn't have modified # the existing orgs. assert "this name should be ignored" not in { organization["name"] for organization in organizations } # Based on logging messages, make sure we dropped the appropriate # organization dict from the bulk-add batch. logging_of_drop_from_batch = mock_log_info.call_args_list[0][0] assert logging_of_drop_from_batch[1]["short_name"] == ( # We dropped this org data: self.make_organization_data("ORG_x")["short_name"]) assert logging_of_drop_from_batch[2]["short_name"] == ( # in favor of this org data, which came earlier in the batch. self.make_organization_data("org_X")["short_name"]) # Based on logging messages, make sure the expected breakdown of # created vs. reactivated vs. not touched # is true for the organizations passed to `bulk_add_organizations`. logged_orgs_to_reactivate = mock_log_info.call_args_list[1][0][2] assert set(logged_orgs_to_reactivate) == {"org_to_reactivate"} logged_orgs_to_create = mock_log_info.call_args_list[2][0][2] assert set(logged_orgs_to_create) == {"org_X", "org_Y"}
def test_add_no_organizations(self): """ Test that `bulk_add_organizations` is a no-op when given an empty list. """ api.bulk_add_organizations([]) assert len(api.get_organizations()) == 0