def test_basic(self): upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC) course_key = SlashSeparatedCourseKey('org', 'class', 'run') location = course_key.make_asset_key('asset', 'my_file_name.jpg') thumbnail_location = course_key.make_asset_key( 'thumbnail', 'my_file_name_thumb.jpg') # pylint: disable=protected-access output = assets._get_asset_json("my_file", upload_date, location, thumbnail_location, True) self.assertEquals(output["display_name"], "my_file") self.assertEquals(output["date_added"], "Jun 01, 2013 at 10:30 UTC") self.assertEquals(output["url"], "/c4x/org/class/asset/my_file_name.jpg") self.assertEquals(output["external_url"], "lms_base_url/c4x/org/class/asset/my_file_name.jpg") self.assertEquals(output["portable_url"], "/static/my_file_name.jpg") self.assertEquals(output["thumbnail"], "/c4x/org/class/thumbnail/my_file_name_thumb.jpg") self.assertEquals(output["id"], unicode(location)) self.assertEquals(output['locked'], True) output = assets._get_asset_json("name", upload_date, location, None, False) self.assertIsNone(output["thumbnail"])
def test_basic(self): upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC) course_key = SlashSeparatedCourseKey('org', 'class', 'run') location = course_key.make_asset_key('asset', 'my_file_name.jpg') thumbnail_location = course_key.make_asset_key('thumbnail', 'my_file_name_thumb.jpg') output = assets._get_asset_json("my_file", upload_date, location, thumbnail_location, True) self.assertEquals(output["display_name"], "my_file") self.assertEquals(output["date_added"], "Jun 01, 2013 at 10:30 UTC") self.assertEquals(output["url"], "/c4x/org/class/asset/my_file_name.jpg") self.assertEquals(output["external_url"], "lms_base_url/c4x/org/class/asset/my_file_name.jpg") self.assertEquals(output["portable_url"], "/static/my_file_name.jpg") self.assertEquals(output["thumbnail"], "/c4x/org/class/thumbnail/my_file_name_thumb.jpg") self.assertEquals(output["id"], unicode(location)) self.assertEquals(output['locked'], True) output = assets._get_asset_json("name", upload_date, location, None, False) self.assertIsNone(output["thumbnail"])
def test_basic(self): upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC) content_type = "image/jpg" course_key = SlashSeparatedCourseKey("org", "class", "run") location = course_key.make_asset_key("asset", "my_file_name.jpg") thumbnail_location = course_key.make_asset_key("thumbnail", "my_file_name_thumb.jpg") # pylint: disable=protected-access output = assets._get_asset_json("my_file", content_type, upload_date, location, thumbnail_location, True) self.assertEquals(output["display_name"], "my_file") self.assertEquals(output["date_added"], "Jun 01, 2013 at 10:30 UTC") self.assertEquals(output["url"], "/c4x/org/class/asset/my_file_name.jpg") self.assertEquals(output["external_url"], "lms_base_url/c4x/org/class/asset/my_file_name.jpg") self.assertEquals(output["portable_url"], "/static/my_file_name.jpg") self.assertEquals(output["thumbnail"], "/c4x/org/class/thumbnail/my_file_name_thumb.jpg") self.assertEquals(output["id"], unicode(location)) self.assertEquals(output["locked"], True) output = assets._get_asset_json("name", content_type, upload_date, location, None, False) self.assertIsNone(output["thumbnail"])
def test_export_course_image(self, _from_json): """ Test to make sure that we have a course image in the contentstore, then export it to ensure it gets copied to both file locations. """ course_key = SlashSeparatedCourseKey('edX', 'simple', '2012_Fall') location = course_key.make_asset_key('asset', 'images_course_image.jpg') # This will raise if the course image is missing self.content_store.find(location) root_dir = path(mkdtemp()) self.addCleanup(shutil.rmtree, root_dir) export_course_to_xml(self.draft_store, self.content_store, course_key, root_dir, 'test_export') self.assertTrue(path(root_dir / 'test_export/static/images/course_image.jpg').isfile()) self.assertTrue(path(root_dir / 'test_export/static/images_course_image.jpg').isfile())
def test_copy_assets(self, deprecated): """ copy_all_course_assets """ self.set_up_assets(deprecated) dest_course = SlashSeparatedCourseKey('test', 'destination', 'copy') self.contentstore.copy_all_course_assets(self.course1_key, dest_course) for filename in self.course1_files: asset_key = self.course1_key.make_asset_key('asset', filename) dest_key = dest_course.make_asset_key('asset', filename) source = self.contentstore.find(asset_key) copied = self.contentstore.find(dest_key) for propname in ['name', 'content_type', 'length', 'locked']: self.assertEqual(getattr(source, propname), getattr(copied, propname)) __, count = self.contentstore.get_all_content_for_course(dest_course) self.assertEqual(count, len(self.course1_files))
def test_export_course_image(self): """ Test to make sure that we have a course image in the contentstore, then export it to ensure it gets copied to both file locations. """ course_key = SlashSeparatedCourseKey("edX", "simple", "2012_Fall") location = course_key.make_asset_key("asset", "images_course_image.jpg") # This will raise if the course image is missing self.content_store.find(location) root_dir = path(mkdtemp()) try: export_course_to_xml(self.draft_store, self.content_store, course_key, root_dir, "test_export") assert_true(path(root_dir / "test_export/static/images/course_image.jpg").isfile()) assert_true(path(root_dir / "test_export/static/images_course_image.jpg").isfile()) finally: shutil.rmtree(root_dir)
def test_static_url_generation(self): course_key = SlashSeparatedCourseKey('org', 'class', 'run') location = course_key.make_asset_key('asset', 'my_file_name.jpg') path = StaticContent.get_static_path_from_location(location) self.assertEquals(path, '/static/my_file_name.jpg')
def test_static_url_generation(self): course_key = SlashSeparatedCourseKey("org", "class", "run") location = course_key.make_asset_key("asset", "my_file_name.jpg") path = StaticContent.get_static_path_from_location(location) self.assertEquals(path, "/static/my_file_name.jpg")
class ContentStoreToyCourseTest(ModuleStoreTestCase): """ Tests that use the toy course. """ def setUp(self): """ Create user and login. """ self.staff_pwd = super(ContentStoreToyCourseTest, self).setUp() self.staff_usr = self.user self.non_staff_usr, self.non_staff_pwd = self.create_non_staff_user() self.client = Client() self.contentstore = contentstore() self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') import_from_xml( modulestore(), self.user.id, 'common/test/data/', ['toy'], static_content_store=self.contentstore, verbose=True ) # A locked asset self.locked_asset = self.course_key.make_asset_key('asset', 'sample_static.txt') self.url_locked = self.locked_asset.to_deprecated_string() self.contentstore.set_attr(self.locked_asset, 'locked', True) # An unlocked asset self.unlocked_asset = self.course_key.make_asset_key('asset', 'another_static.txt') self.url_unlocked = self.unlocked_asset.to_deprecated_string() self.length_unlocked = self.contentstore.get_attr(self.unlocked_asset, 'length') def test_unlocked_asset(self): """ Test that unlocked assets are being served. """ self.client.logout() resp = self.client.get(self.url_unlocked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_not_logged_in(self): """ Test that locked assets behave appropriately in case the user is not logged in. """ self.client.logout() resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_not_registered(self): """ Test that locked assets behave appropriately in case user is logged in in but not registered for the course. """ self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_registered(self): """ Test that locked assets behave appropriately in case user is logged in and registered for the course. """ CourseEnrollment.enroll(self.non_staff_usr, self.course_key) self.assertTrue(CourseEnrollment.is_enrolled(self.non_staff_usr, self.course_key)) self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_staff(self): """ Test that locked assets behave appropriately in case user is staff. """ self.client.login(username=self.staff_usr, password=self.staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_range_request_full_file(self): """ Test that a range request from byte 0 to last, outputs partial content status code and valid Content-Range and Content-Length. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes=0-') self.assertEqual(resp.status_code, 206) # HTTP_206_PARTIAL_CONTENT self.assertEqual( resp['Content-Range'], 'bytes {first}-{last}/{length}'.format( first=0, last=self.length_unlocked - 1, length=self.length_unlocked ) ) self.assertEqual(resp['Content-Length'], str(self.length_unlocked)) def test_range_request_partial_file(self): """ Test that a range request for a partial file, outputs partial content status code and valid Content-Range and Content-Length. first_byte and last_byte are chosen to be simple but non trivial values. """ first_byte = self.length_unlocked / 4 last_byte = self.length_unlocked / 2 resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes={first}-{last}'.format( first=first_byte, last=last_byte) ) self.assertEqual(resp.status_code, 206) # HTTP_206_PARTIAL_CONTENT self.assertEqual(resp['Content-Range'], 'bytes {first}-{last}/{length}'.format( first=first_byte, last=last_byte, length=self.length_unlocked)) self.assertEqual(resp['Content-Length'], str(last_byte - first_byte + 1)) def test_range_request_malformed_missing_equal(self): """ Test that a range request with malformed Range (missing '=') outputs status 400. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes 0-') self.assertEqual(resp.status_code, 400) # HTTP_400_BAD_REQUEST def test_range_request_malformed_not_bytes(self): """ Test that a range request with malformed Range (not "bytes") outputs status 400. "Accept-Ranges: bytes" tells the user that only "bytes" ranges are allowed """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bits=0-') self.assertEqual(resp.status_code, 400) # HTTP_400_BAD_REQUEST def test_range_request_malformed_missing_minus(self): """ Test that a range request with malformed Range (missing '-') outputs status 400. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes=0') self.assertEqual(resp.status_code, 400) # HTTP_400_BAD_REQUEST def test_range_request_malformed_first_not_integer(self): """ Test that a range request with malformed Range (first is not an integer) outputs status 400. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes=one-') self.assertEqual(resp.status_code, 400) # HTTP_400_BAD_REQUEST def test_range_request_malformed_invalid_range(self): """ Test that a range request with malformed Range (first_byte > last_byte) outputs status 400. """ first_byte = self.length_unlocked / 2 last_byte = self.length_unlocked / 4 resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes={first}-{last}'.format( first=first_byte, last=last_byte) ) self.assertEqual(resp.status_code, 400) # HTTP_400_BAD_REQUEST def test_range_request_malformed_out_of_bounds(self): """ Test that a range request with malformed Range (last_byte == totalLength, offset by 1 error) outputs status 400. """ last_byte = self.length_unlocked resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes=0-{last}'.format( last=last_byte) ) self.assertEqual(resp.status_code, 400) # HTTP_400_BAD_REQUEST
class ContentStoreToyCourseTest(ModuleStoreTestCase): """ Tests that use the toy course. """ def setUp(self): """ Create user and login. """ settings.MODULESTORE['default']['OPTIONS']['fs_root'] = path('common/test/data') settings.MODULESTORE['direct']['OPTIONS']['fs_root'] = path('common/test/data') self.client = Client() self.contentstore = contentstore() self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') import_from_xml(modulestore('direct'), 'common/test/data/', ['toy'], static_content_store=self.contentstore, verbose=True) # A locked asset self.locked_asset = self.course_key.make_asset_key('asset', 'sample_static.txt') self.url_locked = self.locked_asset.to_deprecated_string() # An unlocked asset self.unlocked_asset = self.course_key.make_asset_key('asset', 'another_static.txt') self.url_unlocked = self.unlocked_asset.to_deprecated_string() self.contentstore.set_attr(self.locked_asset, 'locked', True) # Create user self.usr = '******' self.pwd = 'foo' email = '*****@*****.**' self.user = User.objects.create_user(self.usr, email, self.pwd) self.user.is_active = True self.user.save() # Create staff user self.staff_usr = '******' self.staff_pwd = 'foo' staff_email = '*****@*****.**' self.staff_user = User.objects.create_user(self.staff_usr, staff_email, self.staff_pwd) self.staff_user.is_active = True self.staff_user.is_staff = True self.staff_user.save() def tearDown(self): MongoClient().drop_database(TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db']) _CONTENTSTORE.clear() def test_unlocked_asset(self): """ Test that unlocked assets are being served. """ self.client.logout() resp = self.client.get(self.url_unlocked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_not_logged_in(self): """ Test that locked assets behave appropriately in case the user is not logged in. """ self.client.logout() resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_not_registered(self): """ Test that locked assets behave appropriately in case user is logged in in but not registered for the course. """ self.client.login(username=self.usr, password=self.pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_registered(self): """ Test that locked assets behave appropriately in case user is logged in and registered for the course. """ CourseEnrollment.enroll(self.user, self.course_key) self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_key)) self.client.login(username=self.usr, password=self.pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_staff(self): """ Test that locked assets behave appropriately in case user is staff. """ self.client.login(username=self.staff_usr, password=self.staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103
class ContentStoreToyCourseTest(ModuleStoreTestCase): """ Tests that use the toy course. """ def setUp(self): """ Create user and login. """ self.staff_pwd = super(ContentStoreToyCourseTest, self).setUp() self.staff_usr = self.user self.non_staff_usr, self.non_staff_pwd = self.create_non_staff_user() self.client = Client() self.contentstore = contentstore() self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') import_from_xml(modulestore(), self.user.id, 'common/test/data/', ['toy'], static_content_store=self.contentstore, verbose=True) # A locked asset self.locked_asset = self.course_key.make_asset_key('asset', 'sample_static.txt') self.url_locked = self.locked_asset.to_deprecated_string() # An unlocked asset self.unlocked_asset = self.course_key.make_asset_key('asset', 'another_static.txt') self.url_unlocked = self.unlocked_asset.to_deprecated_string() self.contentstore.set_attr(self.locked_asset, 'locked', True) def tearDown(self): contentstore().drop_database() _CONTENTSTORE.clear() def test_unlocked_asset(self): """ Test that unlocked assets are being served. """ self.client.logout() resp = self.client.get(self.url_unlocked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_not_logged_in(self): """ Test that locked assets behave appropriately in case the user is not logged in. """ self.client.logout() resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_not_registered(self): """ Test that locked assets behave appropriately in case user is logged in in but not registered for the course. """ self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_registered(self): """ Test that locked assets behave appropriately in case user is logged in and registered for the course. """ CourseEnrollment.enroll(self.non_staff_usr, self.course_key) self.assertTrue(CourseEnrollment.is_enrolled(self.non_staff_usr, self.course_key)) self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_staff(self): """ Test that locked assets behave appropriately in case user is staff. """ self.client.login(username=self.staff_usr, password=self.staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103
class TestContentstore(unittest.TestCase): """ Test the methods in contentstore.mongo using deprecated and non-deprecated keys """ # don't use these 2 class vars as they restore behavior once the tests are done asset_deprecated = None ssck_deprecated = None @classmethod def tearDownClass(cls): """ Restores deprecated values """ if cls.asset_deprecated is not None: setattr(AssetLocation, 'deprecated', cls.asset_deprecated) else: delattr(AssetLocation, 'deprecated') if cls.ssck_deprecated is not None: setattr(SlashSeparatedCourseKey, 'deprecated', cls.ssck_deprecated) else: delattr(SlashSeparatedCourseKey, 'deprecated') return super(TestContentstore, cls).tearDownClass() def set_up_assets(self, deprecated): """ Setup contentstore w/ proper overriding of deprecated. """ # since MongoModuleStore and MongoContentStore are basically assumed to be together, create this class # as well self.contentstore = MongoContentStore(HOST, DB, port=PORT) self.addCleanup(self.contentstore._drop_database) # pylint: disable=protected-access setattr(AssetLocation, 'deprecated', deprecated) setattr(SlashSeparatedCourseKey, 'deprecated', deprecated) self.course1_key = SlashSeparatedCourseKey('test', 'asset_test', '2014_07') self.course2_key = SlashSeparatedCourseKey('test', 'asset_test2', '2014_07') self.course1_files = ['contains.sh', 'picture1.jpg', 'picture2.jpg'] self.course2_files = ['picture1.jpg', 'picture3.jpg', 'door_2.ogg'] def load_assets(course_key, files): locked = False for filename in files: asset_key = course_key.make_asset_key('asset', filename) self.save_asset(filename, asset_key, filename, locked) locked = not locked load_assets(self.course1_key, self.course1_files) load_assets(self.course2_key, self.course2_files) def save_asset(self, filename, asset_key, displayname, locked): """ Load and save the given file. """ with open("{}/static/{}".format(DATA_DIR, filename), "rb") as f: content = StaticContent(asset_key, displayname, mimetypes.guess_type(filename)[0], f.read(), locked=locked) self.contentstore.save(content) @ddt.data(True, False) def test_delete(self, deprecated): """ Test that deleting assets works """ self.set_up_assets(deprecated) asset_key = self.course1_key.make_asset_key('asset', self.course1_files[0]) self.contentstore.delete(asset_key) with self.assertRaises(NotFoundError): self.contentstore.find(asset_key) # ensure deleting a non-existent file is a noop self.contentstore.delete(asset_key) @ddt.data(True, False) def test_find(self, deprecated): """ Test using find """ self.set_up_assets(deprecated) asset_key = self.course1_key.make_asset_key('asset', self.course1_files[0]) self.assertIsNotNone(self.contentstore.find(asset_key), "Could not find {}".format(asset_key)) self.assertIsNotNone(self.contentstore.find(asset_key, as_stream=True), "Could not find {}".format(asset_key)) unknown_asset = self.course1_key.make_asset_key( 'asset', 'no_such_file.gif') with self.assertRaises(NotFoundError): self.contentstore.find(unknown_asset) self.assertIsNone( self.contentstore.find(unknown_asset, throw_on_not_found=False), "Found unknown asset {}".format(unknown_asset)) @ddt.data(True, False) def test_export_for_course(self, deprecated): """ Test export """ self.set_up_assets(deprecated) root_dir = path.path(mkdtemp()) try: self.contentstore.export_all_for_course( self.course1_key, root_dir, path.path(root_dir / "policy.json"), ) for filename in self.course1_files: filepath = path.path(root_dir / filename) self.assertTrue(filepath.isfile(), "{} is not a file".format(filepath)) for filename in self.course2_files: if filename not in self.course1_files: filepath = path.path(root_dir / filename) self.assertFalse( filepath.isfile(), "{} is unexpected exported a file".format(filepath)) finally: shutil.rmtree(root_dir) @ddt.data(True, False) def test_get_all_content(self, deprecated): """ Test get_all_content_for_course """ self.set_up_assets(deprecated) course1_assets, count = self.contentstore.get_all_content_for_course( self.course1_key) self.assertEqual(count, len(self.course1_files), course1_assets) for asset in course1_assets: parsed = AssetLocation.from_deprecated_string(asset['filename']) self.assertIn(parsed.name, self.course1_files) course1_assets, __ = self.contentstore.get_all_content_for_course( self.course1_key, 1, 1) self.assertEqual(len(course1_assets), 1, course1_assets) fake_course = SlashSeparatedCourseKey('test', 'fake', 'non') course_assets, count = self.contentstore.get_all_content_for_course( fake_course) self.assertEqual(count, 0) self.assertEqual(course_assets, []) @ddt.data(True, False) def test_attrs(self, deprecated): """ Test setting and getting attrs """ self.set_up_assets(deprecated) for filename in self.course1_files: asset_key = self.course1_key.make_asset_key('asset', filename) prelocked = self.contentstore.get_attr(asset_key, 'locked', False) self.contentstore.set_attr(asset_key, 'locked', not prelocked) self.assertEqual( self.contentstore.get_attr(asset_key, 'locked', False), not prelocked) @ddt.data(True, False) def test_copy_assets(self, deprecated): """ copy_all_course_assets """ self.set_up_assets(deprecated) dest_course = SlashSeparatedCourseKey('test', 'destination', 'copy') self.contentstore.copy_all_course_assets(self.course1_key, dest_course) for filename in self.course1_files: asset_key = self.course1_key.make_asset_key('asset', filename) dest_key = dest_course.make_asset_key('asset', filename) source = self.contentstore.find(asset_key) copied = self.contentstore.find(dest_key) for propname in ['name', 'content_type', 'length', 'locked']: self.assertEqual(getattr(source, propname), getattr(copied, propname)) __, count = self.contentstore.get_all_content_for_course(dest_course) self.assertEqual(count, len(self.course1_files)) @ddt.data(True, False) def test_delete_assets(self, deprecated): """ delete_all_course_assets """ self.set_up_assets(deprecated) self.contentstore.delete_all_course_assets(self.course1_key) __, count = self.contentstore.get_all_content_for_course( self.course1_key) self.assertEqual(count, 0) # ensure it didn't remove any from other course __, count = self.contentstore.get_all_content_for_course( self.course2_key) self.assertEqual(count, len(self.course2_files))
class TestContentstore(unittest.TestCase): """ Test the methods in contentstore.mongo using deprecated and non-deprecated keys """ # don't use these 2 class vars as they restore behavior once the tests are done asset_deprecated = None ssck_deprecated = None @classmethod def tearDownClass(cls): """ Restores deprecated values """ if cls.asset_deprecated is not None: setattr(AssetLocation, 'deprecated', cls.asset_deprecated) else: delattr(AssetLocation, 'deprecated') if cls.ssck_deprecated is not None: setattr(SlashSeparatedCourseKey, 'deprecated', cls.ssck_deprecated) else: delattr(SlashSeparatedCourseKey, 'deprecated') return super(TestContentstore, cls).tearDownClass() def set_up_assets(self, deprecated): """ Setup contentstore w/ proper overriding of deprecated. """ # since MongoModuleStore and MongoContentStore are basically assumed to be together, create this class # as well self.contentstore = MongoContentStore(HOST, DB, port=PORT) self.addCleanup(self.contentstore._drop_database) # pylint: disable=protected-access setattr(AssetLocation, 'deprecated', deprecated) setattr(SlashSeparatedCourseKey, 'deprecated', deprecated) self.course1_key = SlashSeparatedCourseKey('test', 'asset_test', '2014_07') self.course2_key = SlashSeparatedCourseKey('test', 'asset_test2', '2014_07') self.course1_files = ['contains.sh', 'picture1.jpg', 'picture2.jpg'] self.course2_files = ['picture1.jpg', 'picture3.jpg', 'door_2.ogg'] def load_assets(course_key, files): locked = False for filename in files: asset_key = course_key.make_asset_key('asset', filename) self.save_asset(filename, asset_key, filename, locked) locked = not locked load_assets(self.course1_key, self.course1_files) load_assets(self.course2_key, self.course2_files) def save_asset(self, filename, asset_key, displayname, locked): """ Load and save the given file. """ with open("{}/static/{}".format(DATA_DIR, filename), "rb") as f: content = StaticContent( asset_key, displayname, mimetypes.guess_type(filename)[0], f.read(), locked=locked ) self.contentstore.save(content) @ddt.data(True, False) def test_delete(self, deprecated): """ Test that deleting assets works """ self.set_up_assets(deprecated) asset_key = self.course1_key.make_asset_key('asset', self.course1_files[0]) self.contentstore.delete(asset_key) with self.assertRaises(NotFoundError): self.contentstore.find(asset_key) # ensure deleting a non-existent file is a noop self.contentstore.delete(asset_key) @ddt.data(True, False) def test_find(self, deprecated): """ Test using find """ self.set_up_assets(deprecated) asset_key = self.course1_key.make_asset_key('asset', self.course1_files[0]) self.assertIsNotNone(self.contentstore.find(asset_key), "Could not find {}".format(asset_key)) self.assertIsNotNone(self.contentstore.find(asset_key, as_stream=True), "Could not find {}".format(asset_key)) unknown_asset = self.course1_key.make_asset_key('asset', 'no_such_file.gif') with self.assertRaises(NotFoundError): self.contentstore.find(unknown_asset) self.assertIsNone( self.contentstore.find(unknown_asset, throw_on_not_found=False), "Found unknown asset {}".format(unknown_asset) ) @ddt.data(True, False) def test_export_for_course(self, deprecated): """ Test export """ self.set_up_assets(deprecated) root_dir = path.path(mkdtemp()) try: self.contentstore.export_all_for_course( self.course1_key, root_dir, path.path(root_dir / "policy.json"), ) for filename in self.course1_files: filepath = path.path(root_dir / filename) self.assertTrue(filepath.isfile(), "{} is not a file".format(filepath)) for filename in self.course2_files: if filename not in self.course1_files: filepath = path.path(root_dir / filename) self.assertFalse(filepath.isfile(), "{} is unexpected exported a file".format(filepath)) finally: shutil.rmtree(root_dir) @ddt.data(True, False) def test_get_all_content(self, deprecated): """ Test get_all_content_for_course """ self.set_up_assets(deprecated) course1_assets, count = self.contentstore.get_all_content_for_course(self.course1_key) self.assertEqual(count, len(self.course1_files), course1_assets) for asset in course1_assets: parsed = AssetLocation.from_deprecated_string(asset['filename']) self.assertIn(parsed.name, self.course1_files) course1_assets, __ = self.contentstore.get_all_content_for_course(self.course1_key, 1, 1) self.assertEqual(len(course1_assets), 1, course1_assets) fake_course = SlashSeparatedCourseKey('test', 'fake', 'non') course_assets, count = self.contentstore.get_all_content_for_course(fake_course) self.assertEqual(count, 0) self.assertEqual(course_assets, []) @ddt.data(True, False) def test_attrs(self, deprecated): """ Test setting and getting attrs """ self.set_up_assets(deprecated) for filename in self.course1_files: asset_key = self.course1_key.make_asset_key('asset', filename) prelocked = self.contentstore.get_attr(asset_key, 'locked', False) self.contentstore.set_attr(asset_key, 'locked', not prelocked) self.assertEqual(self.contentstore.get_attr(asset_key, 'locked', False), not prelocked) @ddt.data(True, False) def test_copy_assets(self, deprecated): """ copy_all_course_assets """ self.set_up_assets(deprecated) dest_course = SlashSeparatedCourseKey('test', 'destination', 'copy') self.contentstore.copy_all_course_assets(self.course1_key, dest_course) for filename in self.course1_files: asset_key = self.course1_key.make_asset_key('asset', filename) dest_key = dest_course.make_asset_key('asset', filename) source = self.contentstore.find(asset_key) copied = self.contentstore.find(dest_key) for propname in ['name', 'content_type', 'length', 'locked']: self.assertEqual(getattr(source, propname), getattr(copied, propname)) __, count = self.contentstore.get_all_content_for_course(dest_course) self.assertEqual(count, len(self.course1_files)) @ddt.data(True, False) def test_delete_assets(self, deprecated): """ delete_all_course_assets """ self.set_up_assets(deprecated) self.contentstore.delete_all_course_assets(self.course1_key) __, count = self.contentstore.get_all_content_for_course(self.course1_key) self.assertEqual(count, 0) # ensure it didn't remove any from other course __, count = self.contentstore.get_all_content_for_course(self.course2_key) self.assertEqual(count, len(self.course2_files))
class ContentStoreToyCourseTest(ModuleStoreTestCase): """ Tests that use the toy course. """ def setUp(self): """ Create user and login. """ self.staff_pwd = super(ContentStoreToyCourseTest, self).setUp() self.staff_usr = self.user self.non_staff_usr, self.non_staff_pwd = self.create_non_staff_user() self.client = Client() self.contentstore = contentstore() self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') import_from_xml(modulestore(), self.user.id, 'common/test/data/', ['toy'], static_content_store=self.contentstore, verbose=True) # A locked asset self.locked_asset = self.course_key.make_asset_key( 'asset', 'sample_static.txt') self.url_locked = self.locked_asset.to_deprecated_string() # An unlocked asset self.unlocked_asset = self.course_key.make_asset_key( 'asset', 'another_static.txt') self.url_unlocked = self.unlocked_asset.to_deprecated_string() self.contentstore.set_attr(self.locked_asset, 'locked', True) def test_unlocked_asset(self): """ Test that unlocked assets are being served. """ self.client.logout() resp = self.client.get(self.url_unlocked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_not_logged_in(self): """ Test that locked assets behave appropriately in case the user is not logged in. """ self.client.logout() resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_not_registered(self): """ Test that locked assets behave appropriately in case user is logged in in but not registered for the course. """ self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) # pylint: disable=E1103 def test_locked_asset_registered(self): """ Test that locked assets behave appropriately in case user is logged in and registered for the course. """ CourseEnrollment.enroll(self.non_staff_usr, self.course_key) self.assertTrue( CourseEnrollment.is_enrolled(self.non_staff_usr, self.course_key)) self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103 def test_locked_asset_staff(self): """ Test that locked assets behave appropriately in case user is staff. """ self.client.login(username=self.staff_usr, password=self.staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) # pylint: disable=E1103
class ContentStoreToyCourseTest(ModuleStoreTestCase): """ Tests that use the toy course. """ def setUp(self): """ Create user and login. """ self.staff_pwd = super(ContentStoreToyCourseTest, self).setUp() self.staff_usr = self.user self.non_staff_usr, self.non_staff_pwd = self.create_non_staff_user() self.client = Client() self.contentstore = contentstore() self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') import_from_xml( modulestore(), self.user.id, TEST_DATA_DIR, ['toy'], static_content_store=self.contentstore, verbose=True ) # A locked asset self.locked_asset = self.course_key.make_asset_key('asset', 'sample_static.txt') self.url_locked = self.locked_asset.to_deprecated_string() self.contentstore.set_attr(self.locked_asset, 'locked', True) # An unlocked asset self.unlocked_asset = self.course_key.make_asset_key('asset', 'another_static.txt') self.url_unlocked = self.unlocked_asset.to_deprecated_string() self.length_unlocked = self.contentstore.get_attr(self.unlocked_asset, 'length') def test_unlocked_asset(self): """ Test that unlocked assets are being served. """ self.client.logout() resp = self.client.get(self.url_unlocked) self.assertEqual(resp.status_code, 200) def test_locked_asset_not_logged_in(self): """ Test that locked assets behave appropriately in case the user is not logged in. """ self.client.logout() resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) def test_locked_asset_not_registered(self): """ Test that locked assets behave appropriately in case user is logged in in but not registered for the course. """ self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 403) def test_locked_asset_registered(self): """ Test that locked assets behave appropriately in case user is logged in and registered for the course. """ CourseEnrollment.enroll(self.non_staff_usr, self.course_key) self.assertTrue(CourseEnrollment.is_enrolled(self.non_staff_usr, self.course_key)) self.client.login(username=self.non_staff_usr, password=self.non_staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) def test_locked_asset_staff(self): """ Test that locked assets behave appropriately in case user is staff. """ self.client.login(username=self.staff_usr, password=self.staff_pwd) resp = self.client.get(self.url_locked) self.assertEqual(resp.status_code, 200) def test_range_request_full_file(self): """ Test that a range request from byte 0 to last, outputs partial content status code and valid Content-Range and Content-Length. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes=0-') self.assertEqual(resp.status_code, 206) # HTTP_206_PARTIAL_CONTENT self.assertEqual( resp['Content-Range'], 'bytes {first}-{last}/{length}'.format( first=0, last=self.length_unlocked - 1, length=self.length_unlocked ) ) self.assertEqual(resp['Content-Length'], str(self.length_unlocked)) def test_range_request_partial_file(self): """ Test that a range request for a partial file, outputs partial content status code and valid Content-Range and Content-Length. first_byte and last_byte are chosen to be simple but non trivial values. """ first_byte = self.length_unlocked / 4 last_byte = self.length_unlocked / 2 resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes={first}-{last}'.format( first=first_byte, last=last_byte) ) self.assertEqual(resp.status_code, 206) # HTTP_206_PARTIAL_CONTENT self.assertEqual(resp['Content-Range'], 'bytes {first}-{last}/{length}'.format( first=first_byte, last=last_byte, length=self.length_unlocked)) self.assertEqual(resp['Content-Length'], str(last_byte - first_byte + 1)) def test_range_request_multiple_ranges(self): """ Test that multiple ranges in request outputs the full content. """ first_byte = self.length_unlocked / 4 last_byte = self.length_unlocked / 2 resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes={first}-{last}, -100'.format( first=first_byte, last=last_byte) ) self.assertEqual(resp.status_code, 200) self.assertNotIn('Content-Range', resp) self.assertEqual(resp['Content-Length'], str(self.length_unlocked)) @ddt.data( 'bytes 0-', 'bits=0-', 'bytes=0', 'bytes=one-', ) def test_syntax_errors_in_range(self, header_value): """ Test that syntactically invalid Range values result in a 200 OK full content response. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE=header_value) self.assertEqual(resp.status_code, 200) self.assertNotIn('Content-Range', resp) def test_range_request_malformed_invalid_range(self): """ Test that a range request with malformed Range (first_byte > last_byte) outputs 416 Requested Range Not Satisfiable. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes={first}-{last}'.format( first=(self.length_unlocked / 2), last=(self.length_unlocked / 4)) ) self.assertEqual(resp.status_code, 416) def test_range_request_malformed_out_of_bounds(self): """ Test that a range request with malformed Range (first_byte, last_byte == totalLength, offset by 1 error) outputs 416 Requested Range Not Satisfiable. """ resp = self.client.get(self.url_unlocked, HTTP_RANGE='bytes={first}-{last}'.format( first=(self.length_unlocked), last=(self.length_unlocked)) ) self.assertEqual(resp.status_code, 416)