Files
edx-platform/cms/djangoapps/contentstore/tests/test_tasks.py
Kyle McCormick 4dda73d797 [BD-14] Migrate all environments to use database-backed organizations (#25153)
* Install `organizations` app into LMS and Studio non-optionally.
* Add toggle `ORGANIZATIONS_AUTOCREATE` to Studio.
* Remove the `FEATURES["ORGANIZATIONS_APP"]` toggle.
* Use the new `organizations.api.ensure_organization` function to
  either validate or get-or-create organizations, depending
  on the value of `ORGANIZATIONS_AUTOCREATE`,
  when creating course runs and V2 content libraries.
  We'll soon use it for V1 content libraries as well.
* Remove the `util.organizations_helpers` wrapper layer
  that had to exist because `organizations` was an optional app.
* Add `.get_library_keys()` method to the Split modulestore.
* Add Studio management command for backfilling organizations tables
  (`backfill_orgs_and_org_courses`).

For full details, see
https://github.com/edx/edx-organizations/blob/master/docs/decisions/0001-phase-in-db-backed-organizations-to-all.rst

TNL-7646
2020-12-02 13:58:40 -05:00

164 lines
6.4 KiB
Python

"""
Unit tests for course import and export Celery tasks
"""
import copy
import json
from uuid import uuid4
import mock
from django.conf import settings
from django.contrib.auth.models import User
from django.test.utils import override_settings
from opaque_keys.edx.locator import CourseLocator
from organizations.models import OrganizationCourse
from organizations.tests.factories import OrganizationFactory
from user_tasks.models import UserTaskArtifact, UserTaskStatus
from cms.djangoapps.contentstore.tasks import export_olx, rerun_course
from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from common.djangoapps.course_action_state.models import CourseRerunState
from openedx.core.djangoapps.embargo.models import Country, CountryAccessRule, RestrictedCourse
from xmodule.modulestore.django import modulestore
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex
def side_effect_exception(*args, **kwargs):
"""
Side effect for mocking which raises an exception
"""
raise Exception('Boom!')
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class ExportCourseTestCase(CourseTestCase):
"""
Tests of the export_olx task applied to courses
"""
def test_success(self):
"""
Verify that a routine course export task succeeds
"""
key = str(self.course.location.course_key)
result = export_olx.delay(self.user.id, key, u'en')
status = UserTaskStatus.objects.get(task_id=result.id)
self.assertEqual(status.state, UserTaskStatus.SUCCEEDED)
artifacts = UserTaskArtifact.objects.filter(status=status)
self.assertEqual(len(artifacts), 1)
output = artifacts[0]
self.assertEqual(output.name, 'Output')
@mock.patch('cms.djangoapps.contentstore.tasks.export_course_to_xml', side_effect=side_effect_exception)
def test_exception(self, mock_export): # pylint: disable=unused-argument
"""
The export task should fail gracefully if an exception is thrown
"""
key = str(self.course.location.course_key)
result = export_olx.delay(self.user.id, key, u'en')
self._assert_failed(result, json.dumps({u'raw_error_msg': u'Boom!'}))
def test_invalid_user_id(self):
"""
Verify that attempts to export a course as an invalid user fail
"""
user_id = User.objects.order_by(u'-id').first().pk + 100
key = str(self.course.location.course_key)
result = export_olx.delay(user_id, key, u'en')
self._assert_failed(result, u'Unknown User ID: {}'.format(user_id))
def test_non_course_author(self):
"""
Verify that users who aren't authors of the course are unable to export it
"""
_, nonstaff_user = self.create_non_staff_authed_user_client()
key = str(self.course.location.course_key)
result = export_olx.delay(nonstaff_user.id, key, u'en')
self._assert_failed(result, u'Permission denied')
def _assert_failed(self, task_result, error_message):
"""
Verify that a task failed with the specified error message
"""
status = UserTaskStatus.objects.get(task_id=task_result.id)
self.assertEqual(status.state, UserTaskStatus.FAILED)
artifacts = UserTaskArtifact.objects.filter(status=status)
self.assertEqual(len(artifacts), 1)
error = artifacts[0]
self.assertEqual(error.name, u'Error')
self.assertEqual(error.text, error_message)
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class ExportLibraryTestCase(LibraryTestCase):
"""
Tests of the export_olx task applied to libraries
"""
def test_success(self):
"""
Verify that a routine library export task succeeds
"""
key = str(self.lib_key)
result = export_olx.delay(self.user.id, key, u'en')
status = UserTaskStatus.objects.get(task_id=result.id)
self.assertEqual(status.state, UserTaskStatus.SUCCEEDED)
artifacts = UserTaskArtifact.objects.filter(status=status)
self.assertEqual(len(artifacts), 1)
output = artifacts[0]
self.assertEqual(output.name, 'Output')
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class RerunCourseTaskTestCase(CourseTestCase):
def _rerun_course(self, old_course_key, new_course_key):
CourseRerunState.objects.initiated(old_course_key, new_course_key, self.user, 'Test Re-run')
rerun_course(str(old_course_key), str(new_course_key), self.user.id)
def test_success(self):
""" The task should clone the OrganizationCourse and RestrictedCourse data. """
old_course_key = self.course.id
new_course_key = CourseLocator(org=old_course_key.org, course=old_course_key.course, run='rerun')
old_course_id = str(old_course_key)
new_course_id = str(new_course_key)
organization = OrganizationFactory(short_name=old_course_key.org)
OrganizationCourse.objects.create(course_id=old_course_id, organization=organization)
restricted_course = RestrictedCourse.objects.create(course_key=self.course.id)
restricted_country = Country.objects.create(country='US')
CountryAccessRule.objects.create(
rule_type=CountryAccessRule.BLACKLIST_RULE,
restricted_course=restricted_course,
country=restricted_country
)
# Run the task!
self._rerun_course(old_course_key, new_course_key)
# Verify the new course run exists
course = modulestore().get_course(new_course_key)
self.assertIsNotNone(course)
# Verify the OrganizationCourse is cloned
self.assertEqual(OrganizationCourse.objects.count(), 2)
# This will raise an error if the OrganizationCourse object was not cloned
OrganizationCourse.objects.get(course_id=new_course_id, organization=organization)
# Verify the RestrictedCourse and related objects are cloned
self.assertEqual(RestrictedCourse.objects.count(), 2)
restricted_course = RestrictedCourse.objects.get(course_key=new_course_key)
self.assertEqual(CountryAccessRule.objects.count(), 2)
CountryAccessRule.objects.get(
rule_type=CountryAccessRule.BLACKLIST_RULE,
restricted_course=restricted_course,
country=restricted_country
)