def test_create(self): """ Create a course. """ before = course_count() course = create_course(**self.kwargs) after = course_count() self.assertTrue(after == before + 1) self.assertTrue(course is not None) # Can't create a duplicate. with self.assertRaises(ValueError): create_course(**self.kwargs) self.assertTrue(course_count() == after)
def import_course(bundle, repo_id, user_id, static_dir): """ Import a course from an XBundle object. Args: bundle (xbundle.XBundle): Course as xbundle XML repo_id (int): Primary key of repository course belongs to user_id (int): Primary key of Django user doing the import static_dir (unicode): location of static files Returns: learningresources.models.Course """ src = bundle.course course = create_course( org=src.attrib["org"], repo_id=repo_id, course_number=src.attrib["course"], run=src.attrib["semester"], user_id=user_id, ) import_static_assets(course, static_dir) import_children(course, src, None, '') populate_xanalytics_fields.delay(course.id) # This triggers a bulk indexing of all LearningResource instances # for the course at once. index_resources( get_resources(repo_id).filter(course__id=course.id).values_list( "id", flat=True)) return course
def test_list_courses(self): """ Tests the api to list courses in a repo """ res = self.get_courses(repo_slug=self.repo.slug) self.assertEqual(res['count'], 1) # check content returned course_1 = res['results'][0] self.assertDictEqual( course_1, { 'org': self.course.org, 'run': self.course.run, 'course_number': self.course.course_number, 'id': self.course.id }) # create another course course_params = { 'org': 'my org', 'repo_id': self.repo.id, 'course_number': 'second course number', 'run': "second course run", 'user_id': self.user.id } second_course = create_course(**course_params) res = self.get_courses(repo_slug=self.repo.slug) self.assertEqual(res['count'], 2) # check the courses are ordered by creation/id course_1 = res['results'][0] course_2 = res['results'][1] self.assertEqual(course_1['id'], self.course.id) self.assertEqual(course_2['id'], second_course.id) self.assertTrue(course_1['id'] < course_2['id'])
def import_course(bundle, repo_id, user_id, static_dir): """ Import a course from an XBundle object. Args: bundle (xbundle.XBundle): Course as xbundle XML repo_id (int): Primary key of repository course belongs to user_id (int): Primary key of Django user doing the import static_dir (unicode): location of static files Returns: learningresources.models.Course """ src = bundle.course course = create_course( org=src.attrib["org"], repo_id=repo_id, course_number=src.attrib["course"], run=src.attrib["semester"], user_id=user_id, ) import_static_assets(course, static_dir) import_children(course, src, None, '') populate_xanalytics_fields.delay(course.id) # This triggers a bulk indexing of all LearningResource instances # for the course at once. index_resources( get_resources(repo_id).filter( course__id=course.id).values_list("id", flat=True)) return course
def import_course(bundle, repo_id, user_id, static_dir): """ Import a course from an XBundle object. Args: bundle (xbundle.XBundle): Course as xbundle XML repo_id (int): Primary key of repository course belongs to user_id (int): Primary key of Django user doing the import static_dir (unicode): location of static files Returns: learningresources.models.Course """ src = bundle.course course = create_course( org=src.attrib["org"], repo_id=repo_id, course_number=src.attrib["course"], run=src.attrib["semester"], user_id=user_id, ) import_static_assets(course, static_dir) import_children(course, src, None, '') populate_xanalytics_fields.delay(course.id) return course
def test_delete_course(self): """ Authorizations for delete a course Only user with import permission (admin and curator) can delete a repo """ course_params = { 'org': 'my org', 'repo_id': self.repo.id, 'course_number': 'second course number', 'run': "second course run", 'user_id': self.user.id } # anonymous user self.logout() self.delete_course( self.repo.slug, self.course.id, expected_status=403 ) # user not belonging to repo self.login(self.user_norepo.username) self.delete_course( self.repo.slug, self.course.id, expected_status=403 ) self.logout() # author self.login(self.author_user.username) self.delete_course( self.repo.slug, self.course.id, expected_status=403 ) self.logout() # curator self.login(self.curator_user.username) self.delete_course( self.repo.slug, self.course.id ) self.logout() # recreate course self.course = create_course(**course_params) # administrator self.login(self.user.username) self.delete_course( self.repo.slug, self.course.id )
def test_create(self): """ Create a course. """ before = course_count() course = api.create_course(**self.kwargs) after = course_count() self.assertTrue(after == before + 1) self.assertTrue(course is not None) # Can't create a duplicate. with self.assertRaises(ValueError): api.create_course(**self.kwargs) self.assertTrue(course_count() == after) # NOT NULL constraint fails on org with self.assertRaises(IntegrityError) as ex: api.create_course(None, self.repo.id, "course", "run", self.user.id) self.assertIn("org", ex.exception.args[0])
def test_create(self): """ Create a course. """ before = course_count() course = api.create_course(**self.kwargs) after = course_count() self.assertTrue(after == before + 1) self.assertTrue(course is not None) # Can't create a duplicate. with self.assertRaises(ValueError): api.create_course(**self.kwargs) self.assertTrue(course_count() == after) # NOT NULL constraint fails on org with self.assertRaises(IntegrityError) as ex: api.create_course( None, self.repo.id, "course", "run", self.user.id) self.assertIn("org", ex.exception.args[0])
def import_course(bundle, repo_id, user_id): """ Import a course from an XBundle object. Args: bundle (xbundle.XBundle): Course as xbundle XML user_id (int): pk of Django user doing the import """ src = bundle.course course = create_course( org=src.attrib["org"], repo_id=repo_id, course_number=src.attrib["course"], run=src.attrib["semester"], user_id=user_id, ) import_children(course, src, None)
def setUp(self): """set up""" super(LoreTestCase, self).setUp() self.user = User.objects.create_user( username=self.USERNAME, password=self.PASSWORD ) add_repo_perm = Permission.objects.get(codename=self.ADD_REPO_PERM) self.user.user_permissions.add(add_repo_perm) # user without permission to add a repo self.user_norepo = User.objects.create_user( username=self.USERNAME_NO_REPO, password=self.PASSWORD ) self.repo = create_repo( name="test repo", description="just a test", user_id=self.user.id, ) self.course = create_course( org="test-org", repo_id=self.repo.id, course_number="infinity", run="Febtober", user_id=self.user.id, ) self.resource = self.create_resource( course=self.course, parent=None, resource_type="example", title="silly example", content_xml="<blah>blah</blah>", mpath="/blah", url_name="url_name1", ) assign_user_to_repo_group( self.user, self.repo, GroupTypes.REPO_ADMINISTRATOR ) self.toy_resource_count = 18 # Resources in toy course. self.toy_asset_count = 5 # Static assets in toy course. self.client = Client() self.login(username=self.USERNAME)
def setUp(self): """set up""" super(LoreTestCase, self).setUp() recreate_index() self.user = User.objects.create_user(username=self.USERNAME, password=self.PASSWORD) add_repo_perm = Permission.objects.get(codename=self.ADD_REPO_PERM) self.user.user_permissions.add(add_repo_perm) # user without permission to add a repo self.user_norepo = User.objects.create_user( username=self.USERNAME_NO_REPO, password=self.PASSWORD) self.repo = create_repo( name="test repo", description="just a test", user_id=self.user.id, ) self.course = create_course( org="test-org", repo_id=self.repo.id, course_number="infinity", run="Febtober", user_id=self.user.id, ) self.resource = self.create_resource( course=self.course, parent=None, resource_type="example", title="silly example", content_xml="<blah>blah</blah>", mpath="/blah", url_name="url_name1", ) self.toy_resource_count = 18 # Resources in toy course. self.toy_asset_count = 5 # Static assets in toy course. self.client = Client() self.login(username=self.USERNAME) refresh_index()
def test_list_courses(self): """ Tests the api to list courses in a repo """ res = self.get_courses(repo_slug=self.repo.slug) self.assertEqual(res['count'], 1) # check content returned course_1 = res['results'][0] self.assertDictEqual( course_1, { 'org': self.course.org, 'run': self.course.run, 'course_number': self.course.course_number, 'id': self.course.id } ) # create another course course_params = { 'org': 'my org', 'repo_id': self.repo.id, 'course_number': 'second course number', 'run': "second course run", 'user_id': self.user.id } second_course = create_course(**course_params) res = self.get_courses(repo_slug=self.repo.slug) self.assertEqual(res['count'], 2) # check the courses are ordered by creation/id course_1 = res['results'][0] course_2 = res['results'][1] self.assertEqual(course_1['id'], self.course.id) self.assertEqual(course_2['id'], second_course.id) self.assertTrue(course_1['id'] < course_2['id'])
def test_delete_course(self): """ REST tests to delete a Course. All learning resources and static assets connected to the course are deleted as well. Other courses, learning resources types, terms, vocabularies, repositories are not deleted. Note: learning resources types are created during the import of the course but they are related to the repository. """ # trying to delete non existing courses self.delete_course( repo_slug=self.repo.slug, course_id='foo', expected_status=404 ) self.delete_course( repo_slug=self.repo.slug, course_id=1234567, expected_status=404 ) # environment before importing a course self.count_resources( repositories=1, courses=1, learning_resources=1, learning_resource_types=1 ) # Import the course tarball self.import_course_tarball(self.repo) # create a vocabulary and term vocabulary = self.create_vocabulary(self.repo.slug) self.create_term(self.repo.slug, vocabulary['slug']) # environment before deleting the course self.count_resources( repositories=1, courses=2, learning_resources=19, learning_resource_types=8, static_assetts=5, terms=1, vocabularies=1 ) # make sure of the course that is going to be deleted courses = Course.objects.all() self.assertEqual(courses[0].id, self.course.id) self.assertNotEqual(courses[1].id, self.course.id) # try to get the right course but non existing repo self.delete_course( repo_slug='nonsense', course_id=courses[1].id, expected_status=404 ) # delete the course self.delete_course(self.repo.slug, courses[1].id) # environment after deleting the course self.count_resources( repositories=1, courses=1, learning_resources=1, learning_resource_types=8, terms=1, vocabularies=1 ) # the course remaining is the one not deleted courses = Course.objects.all() self.assertEqual(courses[0].id, self.course.id) # and the learning resources remaining are associated with # the remaining course for learning_resource in LearningResource.objects.all(): self.assertEqual( learning_resource.course.id, self.course.id ) # try to delete the remaining course but with the wrong repo # create another repo another_repo_dict = { 'name': 'another_repo_name', 'description': 'description', } repo_res = self.create_repository(another_repo_dict) # and another course in it course_params = { 'org': 'my org', 'repo_id': repo_res['id'], 'course_number': 'second course number', 'run': "second course run", 'user_id': self.user.id } second_course = create_course(**course_params) # try to get the right course but on wrong repo self.delete_course( repo_slug=repo_res['slug'], course_id=self.course.id, expected_status=404 ) # again with repo and course swapped self.delete_course( repo_slug=self.repo.slug, course_id=second_course.id, expected_status=404 )
def test_delete_course(self): """ REST tests to delete a Course. All learning resources and static assets connected to the course are deleted as well. Other courses, learning resources types, terms, vocabularies, repositories are not deleted. Note: learning resources types are created during the import of the course but they are related to the repository. """ # trying to delete non existing courses self.delete_course(repo_slug=self.repo.slug, course_id='foo', expected_status=404) self.delete_course(repo_slug=self.repo.slug, course_id=1234567, expected_status=404) # environment before importing a course self.count_resources(repositories=1, courses=1, learning_resources=1, learning_resource_types=1) # Import the course tarball self.import_course_tarball(self.repo) # create a vocabulary and term vocabulary = self.create_vocabulary(self.repo.slug) self.create_term(self.repo.slug, vocabulary['slug']) # environment before deleting the course self.count_resources(repositories=1, courses=2, learning_resources=19, learning_resource_types=8, static_assetts=5, terms=1, vocabularies=1) # make sure of the course that is going to be deleted courses = Course.objects.all() self.assertEqual(courses[0].id, self.course.id) self.assertNotEqual(courses[1].id, self.course.id) # try to get the right course but non existing repo self.delete_course(repo_slug='nonsense', course_id=courses[1].id, expected_status=404) # delete the course self.delete_course(self.repo.slug, courses[1].id) # environment after deleting the course self.count_resources(repositories=1, courses=1, learning_resources=1, learning_resource_types=8, terms=1, vocabularies=1) # the course remaining is the one not deleted courses = Course.objects.all() self.assertEqual(courses[0].id, self.course.id) # and the learning resources remaining are associated with # the remaining course for learning_resource in LearningResource.objects.all(): self.assertEqual(learning_resource.course.id, self.course.id) # try to delete the remaining course but with the wrong repo # create another repo another_repo_dict = { 'name': 'another_repo_name', 'description': 'description', } repo_res = self.create_repository(another_repo_dict) # and another course in it course_params = { 'org': 'my org', 'repo_id': repo_res['id'], 'course_number': 'second course number', 'run': "second course run", 'user_id': self.user.id } second_course = create_course(**course_params) # try to get the right course but on wrong repo self.delete_course(repo_slug=repo_res['slug'], course_id=self.course.id, expected_status=404) # again with repo and course swapped self.delete_course(repo_slug=self.repo.slug, course_id=second_course.id, expected_status=404)
def test_duplicate(self): """ Test that exporting resources with same file path is reported and handled correctly. """ with TemporaryFile() as temp1: with TemporaryFile() as temp2: temp1.write(b"first") temp2.write(b"second") # Create two static assets with the same name but different # paths so no renaming is done on storage side. static_filename = "iamafile.txt" static_path1 = os.path.join( STATIC_ASSET_BASEPATH.format( org=self.resource.course.org, course_number=self.resource.course.course_number, run=self.resource.course.run, ), static_filename ) file1 = File(temp1, name=static_filename) file2 = File(temp2, name=static_filename) default_storage.delete(static_path1) asset1 = create_static_asset(self.resource.course.id, file1) self.resource.static_assets.add(asset1) course2 = create_course( "org2", self.repo.id, self.resource.course.course_number, self.resource.course.run, self.user.id ) resource2 = create_resource( course=course2, parent=None, resource_type=self.resource.learning_resource_type.name, title=self.resource.title, dpath="", mpath="", content_xml="", url_name=self.resource.url_name ) static_path2 = os.path.join( STATIC_ASSET_BASEPATH.format( org=course2.org, course_number=course2.course_number, run=course2.run, ), static_filename ) default_storage.delete(static_path2) asset2 = create_static_asset(resource2.course.id, file2) resource2.static_assets.add(asset2) # Export the resources. The second static asset should have # the number _1 attached to it. try: resources = [self.resource, resource2] tempdir, collision = export_resources_to_directory( resources) try: self.assertTrue(collision) assert_resource_directory(self, resources, tempdir) finally: rmtree(tempdir) finally: default_storage.delete(asset1.asset.name) default_storage.delete(asset2.asset.name)
def test_duplicate(self): """ Test that exporting resources with same file path is reported and handled correctly. """ with TemporaryFile() as temp1: with TemporaryFile() as temp2: temp1.write(b"first") temp2.write(b"second") # Create two static assets with the same name but different # paths so no renaming is done on storage side. static_filename = "iamafile.txt" static_path1 = os.path.join( STATIC_ASSET_BASEPATH.format( org=self.resource.course.org, course_number=self.resource.course.course_number, run=self.resource.course.run, ), static_filename) file1 = File(temp1, name=static_filename) file2 = File(temp2, name=static_filename) default_storage.delete(static_path1) asset1 = create_static_asset(self.resource.course.id, file1) self.resource.static_assets.add(asset1) course2 = create_course("org2", self.repo.id, self.resource.course.course_number, self.resource.course.run, self.user.id) resource2 = create_resource( course=course2, parent=None, resource_type=self.resource.learning_resource_type.name, title=self.resource.title, dpath="", mpath="", content_xml="", url_name=self.resource.url_name) static_path2 = os.path.join( STATIC_ASSET_BASEPATH.format( org=course2.org, course_number=course2.course_number, run=course2.run, ), static_filename) default_storage.delete(static_path2) asset2 = create_static_asset(resource2.course.id, file2) resource2.static_assets.add(asset2) # Export the resources. The second static asset should have # the number _1 attached to it. try: resources = [self.resource, resource2] tempdir, collision = export_resources_to_directory( resources) try: self.assertTrue(collision) assert_resource_directory(self, resources, tempdir) finally: rmtree(tempdir) finally: default_storage.delete(asset1.asset.name) default_storage.delete(asset2.asset.name)