243 lines
9.1 KiB
Python
243 lines
9.1 KiB
Python
"""
|
|
Unit tests for the asset upload endpoint.
|
|
"""
|
|
|
|
#pylint: disable=C0111
|
|
#pylint: disable=W0621
|
|
#pylint: disable=W0212
|
|
|
|
from datetime import datetime, timedelta
|
|
from io import BytesIO
|
|
from pytz import UTC
|
|
import json
|
|
import re
|
|
from unittest import TestCase, skip
|
|
from .utils import CourseTestCase
|
|
from contentstore.views import assets
|
|
from xmodule.contentstore.content import StaticContent, XASSET_LOCATION_TAG
|
|
from xmodule.modulestore import Location
|
|
from xmodule.contentstore.django import contentstore
|
|
from xmodule.modulestore.django import modulestore
|
|
from xmodule.modulestore.xml_importer import import_from_xml
|
|
from xmodule.modulestore.django import loc_mapper
|
|
from xmodule.modulestore.mongo.base import location_to_query
|
|
|
|
|
|
class AssetsTestCase(CourseTestCase):
|
|
def setUp(self):
|
|
super(AssetsTestCase, self).setUp()
|
|
location = loc_mapper().translate_location(self.course.location.course_id, self.course.location, False, True)
|
|
self.url = location.url_reverse('assets/', '')
|
|
|
|
def test_basic(self):
|
|
resp = self.client.get(self.url, HTTP_ACCEPT='text/html')
|
|
self.assertEquals(resp.status_code, 200)
|
|
|
|
def test_static_url_generation(self):
|
|
location = Location(['i4x', 'foo', 'bar', 'asset', 'my_file_name.jpg'])
|
|
path = StaticContent.get_static_path_from_location(location)
|
|
self.assertEquals(path, '/static/my_file_name.jpg')
|
|
|
|
|
|
class AssetsToyCourseTestCase(CourseTestCase):
|
|
"""
|
|
Tests the assets returned from assets_handler (full page content) for the toy test course.
|
|
"""
|
|
def test_toy_assets(self):
|
|
module_store = modulestore('direct')
|
|
_, course_items = import_from_xml(
|
|
module_store,
|
|
'common/test/data/',
|
|
['toy'],
|
|
static_content_store=contentstore(),
|
|
verbose=True
|
|
)
|
|
course = course_items[0]
|
|
location = loc_mapper().translate_location(course.location.course_id, course.location, False, True)
|
|
url = location.url_reverse('assets/', '')
|
|
|
|
resp = self.client.get(url, HTTP_ACCEPT='text/html')
|
|
# Test a small portion of the asset data passed to the client.
|
|
self.assertContains(resp, "new AssetCollection([{")
|
|
self.assertContains(resp, "/c4x/edX/toy/asset/handouts_sample_handout.txt")
|
|
|
|
|
|
class UploadTestCase(CourseTestCase):
|
|
"""
|
|
Unit tests for uploading a file
|
|
"""
|
|
def setUp(self):
|
|
super(UploadTestCase, self).setUp()
|
|
location = loc_mapper().translate_location(self.course.location.course_id, self.course.location, False, True)
|
|
self.url = location.url_reverse('assets/', '')
|
|
|
|
@skip("CorruptGridFile error on continuous integration server")
|
|
def test_happy_path(self):
|
|
f = BytesIO("sample content")
|
|
f.name = "sample.txt"
|
|
resp = self.client.post(self.url, {"name": "my-name", "file": f})
|
|
self.assertEquals(resp.status_code, 200)
|
|
|
|
def test_no_file(self):
|
|
resp = self.client.post(self.url, {"name": "file.txt"}, "application/json")
|
|
self.assertEquals(resp.status_code, 400)
|
|
|
|
def test_get(self):
|
|
with self.assertRaises(NotImplementedError):
|
|
self.client.get(self.url)
|
|
|
|
|
|
class AssetToJsonTestCase(TestCase):
|
|
"""
|
|
Unit test for transforming asset information into something
|
|
we can send out to the client via JSON.
|
|
"""
|
|
def test_basic(self):
|
|
upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC)
|
|
|
|
location = Location(['i4x', 'foo', 'bar', 'asset', 'my_file_name.jpg'])
|
|
thumbnail_location = Location(['i4x', 'foo', 'bar', 'asset', '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"], "/i4x/foo/bar/asset/my_file_name.jpg")
|
|
self.assertEquals(output["portable_url"], "/static/my_file_name.jpg")
|
|
self.assertEquals(output["thumbnail"], "/i4x/foo/bar/asset/my_file_name_thumb.jpg")
|
|
self.assertEquals(output["id"], output["url"])
|
|
self.assertEquals(output['locked'], True)
|
|
|
|
output = assets._get_asset_json("name", upload_date, location, None, False)
|
|
self.assertIsNone(output["thumbnail"])
|
|
|
|
|
|
class LockAssetTestCase(CourseTestCase):
|
|
"""
|
|
Unit test for locking and unlocking an asset.
|
|
"""
|
|
|
|
def test_locking(self):
|
|
"""
|
|
Tests a simple locking and unlocking of an asset in the toy course.
|
|
"""
|
|
def verify_asset_locked_state(locked):
|
|
""" Helper method to verify lock state in the contentstore """
|
|
asset_location = StaticContent.get_location_from_path('/c4x/edX/toy/asset/sample_static.txt')
|
|
content = contentstore().find(asset_location)
|
|
self.assertEqual(content.locked, locked)
|
|
|
|
def post_asset_update(lock):
|
|
""" Helper method for posting asset update. """
|
|
upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC)
|
|
asset_location = Location(['c4x', 'edX', 'toy', 'asset', 'sample_static.txt'])
|
|
location = loc_mapper().translate_location(course.location.course_id, course.location, False, True)
|
|
url = location.url_reverse('assets/', '')
|
|
|
|
resp = self.client.post(
|
|
url,
|
|
json.dumps(assets._get_asset_json("sample_static.txt", upload_date, asset_location, None, lock)),
|
|
"application/json"
|
|
)
|
|
self.assertEqual(resp.status_code, 201)
|
|
return json.loads(resp.content)
|
|
|
|
# Load the toy course.
|
|
module_store = modulestore('direct')
|
|
_, course_items = import_from_xml(
|
|
module_store,
|
|
'common/test/data/',
|
|
['toy'],
|
|
static_content_store=contentstore(),
|
|
verbose=True
|
|
)
|
|
course = course_items[0]
|
|
verify_asset_locked_state(False)
|
|
|
|
# Lock the asset
|
|
resp_asset = post_asset_update(True)
|
|
self.assertTrue(resp_asset['locked'])
|
|
verify_asset_locked_state(True)
|
|
|
|
# Unlock the asset
|
|
resp_asset = post_asset_update(False)
|
|
self.assertFalse(resp_asset['locked'])
|
|
verify_asset_locked_state(False)
|
|
|
|
|
|
class TestAssetIndex(CourseTestCase):
|
|
"""
|
|
Test getting asset lists via http (Note, the assets don't actually exist)
|
|
"""
|
|
def setUp(self):
|
|
"""
|
|
Create fake asset entries for the other tests to use
|
|
"""
|
|
super(TestAssetIndex, self).setUp()
|
|
self.entry_filter = self.create_asset_entries(contentstore(), 100)
|
|
location = loc_mapper().translate_location(self.course.location.course_id, self.course.location, False, True)
|
|
self.url = location.url_reverse('assets/', '')
|
|
|
|
def tearDown(self):
|
|
"""
|
|
Get rid of the entries
|
|
"""
|
|
contentstore().fs_files.remove(self.entry_filter)
|
|
|
|
def create_asset_entries(self, cstore, number):
|
|
"""
|
|
Create the fake entries
|
|
"""
|
|
course_filter = Location(
|
|
XASSET_LOCATION_TAG, category='asset', course=self.course.location.course, org=self.course.location.org
|
|
)
|
|
# purge existing entries (a bit brutal but hopefully tests are independent enuf to not trip on this)
|
|
cstore.fs_files.remove(location_to_query(course_filter))
|
|
base_entry = {
|
|
'displayname': 'foo.jpg',
|
|
'chunkSize': 262144,
|
|
'length': 0,
|
|
'uploadDate': datetime(2012, 1, 2, 0, 0),
|
|
'contentType': 'image/jpeg',
|
|
}
|
|
for i in range(number):
|
|
base_entry['displayname'] = '{:03x}.jpeg'.format(i)
|
|
base_entry['uploadDate'] += timedelta(hours=i)
|
|
base_entry['_id'] = course_filter.replace(name=base_entry['displayname']).dict()
|
|
cstore.fs_files.insert(base_entry)
|
|
|
|
return course_filter.dict()
|
|
|
|
ASSET_LIST_RE = re.compile(r'AssetCollection\((.*)\);$', re.MULTILINE)
|
|
|
|
def check_page_content(self, resp_content, entry_count, last_date=None):
|
|
"""
|
|
:param entry_count:
|
|
:param last_date:
|
|
"""
|
|
match = self.ASSET_LIST_RE.search(resp_content)
|
|
asset_list = json.loads(match.group(1))
|
|
self.assertEqual(len(asset_list), entry_count)
|
|
for row in asset_list:
|
|
datetext = row['date_added']
|
|
parsed_date = datetime.strptime(datetext, "%b %d, %Y at %H:%M UTC")
|
|
if last_date is None:
|
|
last_date = parsed_date
|
|
else:
|
|
self.assertGreaterEqual(last_date, parsed_date)
|
|
return last_date
|
|
|
|
def test_query_assets(self):
|
|
"""
|
|
The actual test
|
|
"""
|
|
# get all
|
|
resp = self.client.get(self.url, HTTP_ACCEPT='text/html')
|
|
self.check_page_content(resp.content, 100)
|
|
# get first page of 10
|
|
resp = self.client.get(self.url + "?max=10", HTTP_ACCEPT='text/html')
|
|
last_date = self.check_page_content(resp.content, 10)
|
|
# get next of 20
|
|
resp = self.client.get(self.url + "?start=10&max=20", HTTP_ACCEPT='text/html')
|
|
self.check_page_content(resp.content, 20, last_date)
|