Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
e8d01a08
Commit
e8d01a08
authored
Feb 06, 2013
by
Diana Huang
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into feature/diana/close-oe-problems
parents
78948210
e89948a5
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
667 additions
and
547 deletions
+667
-547
cms/djangoapps/contentstore/tests/factories.py
+32
-106
cms/djangoapps/contentstore/tests/test_contentstore.py
+400
-0
cms/djangoapps/contentstore/tests/test_core_caching.py
+1
-5
cms/djangoapps/contentstore/tests/test_course_settings.py
+30
-41
cms/djangoapps/contentstore/tests/test_utils.py
+1
-1
cms/djangoapps/contentstore/tests/tests.py
+12
-380
cms/djangoapps/contentstore/tests/utils.py
+63
-0
cms/djangoapps/contentstore/views.py
+10
-2
cms/envs/test.py
+0
-12
common/lib/xmodule/xmodule/modulestore/tests/factories.py
+118
-0
No files found.
cms/djangoapps/contentstore/tests/factories.py
View file @
e8d01a08
from
factory
import
Factory
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
time
import
gmtime
from
datetime
import
datetime
from
uuid
import
uuid4
from
xmodule.timeparse
import
stringify_time
from
student.models
import
(
User
,
UserProfile
,
Registration
,
CourseEnrollmentAllowed
)
from
django.contrib.auth.models
import
Group
class
UserProfileFactory
(
Factory
):
FACTORY_FOR
=
UserProfile
def
XMODULE_COURSE_CREATION
(
class_to_create
,
**
kwargs
):
return
XModuleCourseFactory
.
_create
(
class_to_create
,
**
kwargs
)
user
=
None
name
=
'Robot Studio'
courseware
=
'course.xml'
def
XMODULE_ITEM_CREATION
(
class_to_create
,
**
kwargs
):
return
XModuleItemFactory
.
_create
(
class_to_create
,
**
kwargs
)
class
RegistrationFactory
(
Factory
):
FACTORY_FOR
=
Registration
class
XModuleCourseFactory
(
Factory
):
"""
Factory for XModule courses.
"""
user
=
None
activation_key
=
uuid4
()
.
hex
ABSTRACT_FACTORY
=
True
_creation_function
=
(
XMODULE_COURSE_CREATION
,)
class
UserFactory
(
Factory
):
FACTORY_FOR
=
User
@classmethod
def
_create
(
cls
,
target_class
,
*
args
,
**
kwargs
):
username
=
'robot'
email
=
'robot@edx.org'
password
=
'test'
first_name
=
'Robot'
last_name
=
'Tester'
is_staff
=
False
is_active
=
True
is_superuser
=
False
last_login
=
datetime
.
now
()
date_joined
=
datetime
.
now
()
template
=
Location
(
'i4x'
,
'edx'
,
'templates'
,
'course'
,
'Empty'
)
org
=
kwargs
.
get
(
'org'
)
number
=
kwargs
.
get
(
'number'
)
display_name
=
kwargs
.
get
(
'display_name'
)
location
=
Location
(
'i4x'
,
org
,
number
,
'course'
,
Location
.
clean
(
display_name
))
class
GroupFactory
(
Factory
):
FACTORY_FOR
=
Group
store
=
modulestore
(
'direct'
)
name
=
'test_group'
# Write the data to the mongo datastore
new_course
=
store
.
clone_item
(
template
,
location
)
class
CourseEnrollmentAllowedFactory
(
Factory
):
FACTORY_FOR
=
CourseEnrollmentAllowed
# This metadata code was copied from cms/djangoapps/contentstore/views.py
if
display_name
is
not
None
:
new_course
.
metadata
[
'display_name'
]
=
display_name
new_course
.
metadata
[
'data_dir'
]
=
uuid4
()
.
hex
new_course
.
metadata
[
'start'
]
=
stringify_time
(
gmtime
())
new_course
.
tabs
=
[{
"type"
:
"courseware"
},
{
"type"
:
"course_info"
,
"name"
:
"Course Info"
},
{
"type"
:
"discussion"
,
"name"
:
"Discussion"
},
{
"type"
:
"wiki"
,
"name"
:
"Wiki"
},
{
"type"
:
"progress"
,
"name"
:
"Progress"
}]
# Update the data in the mongo datastore
store
.
update_metadata
(
new_course
.
location
.
url
(),
new_course
.
own_metadata
)
return
new_course
class
Course
:
pass
class
CourseFactory
(
XModuleCourseFactory
):
FACTORY_FOR
=
Course
template
=
'i4x://edx/templates/course/Empty'
org
=
'MITx'
number
=
'999'
display_name
=
'Robot Super Course'
class
XModuleItemFactory
(
Factory
):
"""
Factory for XModule items.
"""
ABSTRACT_FACTORY
=
True
_creation_function
=
(
XMODULE_ITEM_CREATION
,)
@classmethod
def
_create
(
cls
,
target_class
,
*
args
,
**
kwargs
):
"""
kwargs must include parent_location, template. Can contain display_name
target_class is ignored
"""
DETACHED_CATEGORIES
=
[
'about'
,
'static_tab'
,
'course_info'
]
parent_location
=
Location
(
kwargs
.
get
(
'parent_location'
))
template
=
Location
(
kwargs
.
get
(
'template'
))
display_name
=
kwargs
.
get
(
'display_name'
)
store
=
modulestore
(
'direct'
)
# This code was based off that in cms/djangoapps/contentstore/views.py
parent
=
store
.
get_item
(
parent_location
)
dest_location
=
parent_location
.
_replace
(
category
=
template
.
category
,
name
=
uuid4
()
.
hex
)
new_item
=
store
.
clone_item
(
template
,
dest_location
)
# TODO: This needs to be deleted when we have proper storage for static content
new_item
.
metadata
[
'data_dir'
]
=
parent
.
metadata
[
'data_dir'
]
# replace the display name with an optional parameter passed in from the caller
if
display_name
is
not
None
:
new_item
.
metadata
[
'display_name'
]
=
display_name
store
.
update_metadata
(
new_item
.
location
.
url
(),
new_item
.
own_metadata
)
if
new_item
.
location
.
category
not
in
DETACHED_CATEGORIES
:
store
.
update_children
(
parent_location
,
parent
.
definition
.
get
(
'children'
,
[])
+
[
new_item
.
location
.
url
()])
return
new_item
class
Item
:
pass
class
ItemFactory
(
XModuleItemFactory
):
FACTORY_FOR
=
Item
parent_location
=
'i4x://MITx/999/course/Robot_Super_Course'
template
=
'i4x://edx/templates/chapter/Empty'
display_name
=
'Section One'
\ No newline at end of file
email
=
'test@edx.org'
course_id
=
'edX/test/2012_Fall'
cms/djangoapps/contentstore/tests/test_contentstore.py
0 → 100644
View file @
e8d01a08
import
json
import
shutil
from
django.test.client
import
Client
from
override_settings
import
override_settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
path
import
path
from
tempfile
import
mkdtemp
import
json
from
fs.osfs
import
OSFS
import
copy
from
mock
import
Mock
from
student.models
import
Registration
from
django.contrib.auth.models
import
User
from
cms.djangoapps.contentstore.utils
import
get_modulestore
from
utils
import
ModuleStoreTestCase
,
parse_json
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.store_utilities
import
clone_course
from
xmodule.modulestore.store_utilities
import
delete_course
from
xmodule.modulestore.django
import
modulestore
,
_MODULESTORES
from
xmodule.contentstore.django
import
contentstore
from
xmodule.templates
import
update_templates
from
xmodule.modulestore.xml_exporter
import
export_to_xml
from
xmodule.modulestore.xml_importer
import
import_from_xml
from
xmodule.capa_module
import
CapaDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.seq_module
import
SequenceDescriptor
TEST_DATA_MODULESTORE
=
copy
.
deepcopy
(
settings
.
MODULESTORE
)
TEST_DATA_MODULESTORE
[
'default'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
TEST_DATA_MODULESTORE
[
'direct'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MODULESTORE
)
class
ContentStoreToyCourseTest
(
ModuleStoreTestCase
):
"""
Tests that rely on the toy courses.
TODO: refactor using CourseFactory so they do not.
"""
def
setUp
(
self
):
uname
=
'testuser'
email
=
'test+courses@edx.org'
password
=
'foo'
# Create the use so we can log them in.
self
.
user
=
User
.
objects
.
create_user
(
uname
,
email
,
password
)
# Note that we do not actually need to do anything
# for registration if we directly mark them active.
self
.
user
.
is_active
=
True
# Staff has access to view all courses
self
.
user
.
is_staff
=
True
self
.
user
.
save
()
self
.
client
=
Client
()
self
.
client
.
login
(
username
=
uname
,
password
=
password
)
def
check_edit_unit
(
self
,
test_course_name
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
test_course_name
])
for
descriptor
in
modulestore
()
.
get_items
(
Location
(
None
,
None
,
'vertical'
,
None
,
None
)):
print
"Checking "
,
descriptor
.
location
.
url
()
print
descriptor
.
__class__
,
descriptor
.
location
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
descriptor
.
location
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
def
test_edit_unit_toy
(
self
):
self
.
check_edit_unit
(
'toy'
)
def
test_edit_unit_full
(
self
):
self
.
check_edit_unit
(
'full'
)
def
test_static_tab_reordering
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
course
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'course'
,
'6.002_Spring_2012'
,
None
]))
# reverse the ordering
reverse_tabs
=
[]
for
tab
in
course
.
tabs
:
if
tab
[
'type'
]
==
'static_tab'
:
reverse_tabs
.
insert
(
0
,
'i4x://edX/full/static_tab/{0}'
.
format
(
tab
[
'url_slug'
]))
resp
=
self
.
client
.
post
(
reverse
(
'reorder_static_tabs'
),
json
.
dumps
({
'tabs'
:
reverse_tabs
}),
"application/json"
)
course
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'course'
,
'6.002_Spring_2012'
,
None
]))
# compare to make sure that the tabs information is in the expected order after the server call
course_tabs
=
[]
for
tab
in
course
.
tabs
:
if
tab
[
'type'
]
==
'static_tab'
:
course_tabs
.
append
(
'i4x://edX/full/static_tab/{0}'
.
format
(
tab
[
'url_slug'
]))
self
.
assertEqual
(
reverse_tabs
,
course_tabs
)
def
test_about_overrides
(
self
):
'''
This test case verifies that a course can use specialized override for about data, e.g. /about/Fall_2012/effort.html
while there is a base definition in /about/effort.html
'''
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
effort
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'about'
,
'effort'
,
None
]))
self
.
assertEqual
(
effort
.
definition
[
'data'
],
'6 hours'
)
# this one should be in a non-override folder
effort
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'about'
,
'end_date'
,
None
]))
self
.
assertEqual
(
effort
.
definition
[
'data'
],
'TBD'
)
def
test_remove_hide_progress_tab
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
source_location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
course
=
ms
.
get_item
(
source_location
)
self
.
assertNotIn
(
'hide_progress_tab'
,
course
.
metadata
)
def
test_clone_course
(
self
):
course_data
=
{
'template'
:
'i4x://edx/templates/course/Empty'
,
'org'
:
'MITx'
,
'number'
:
'999'
,
'display_name'
:
'Robot Super Course'
,
}
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
course_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
data
[
'id'
],
'i4x://MITx/999/course/Robot_Super_Course'
)
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
source_location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
dest_location
=
CourseDescriptor
.
id_to_location
(
'MITx/999/Robot_Super_Course'
)
clone_course
(
ms
,
cs
,
source_location
,
dest_location
)
# now loop through all the units in the course and verify that the clone can render them, which
# means the objects are at least present
items
=
ms
.
get_items
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'vertical'
,
None
]))
self
.
assertGreater
(
len
(
items
),
0
)
clone_items
=
ms
.
get_items
(
Location
([
'i4x'
,
'MITx'
,
'999'
,
'vertical'
,
None
]))
self
.
assertGreater
(
len
(
clone_items
),
0
)
for
descriptor
in
items
:
new_loc
=
descriptor
.
location
.
_replace
(
org
=
'MITx'
,
course
=
'999'
)
print
"Checking {0} should now also be at {1}"
.
format
(
descriptor
.
location
.
url
(),
new_loc
.
url
())
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
new_loc
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
def
test_delete_course
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
delete_course
(
ms
,
cs
,
location
)
items
=
ms
.
get_items
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'vertical'
,
None
]))
self
.
assertEqual
(
len
(
items
),
0
)
def
verify_content_existence
(
self
,
modulestore
,
root_dir
,
location
,
dirname
,
category_name
,
filename_suffix
=
''
):
fs
=
OSFS
(
root_dir
/
'test_export'
)
self
.
assertTrue
(
fs
.
exists
(
dirname
))
query_loc
=
Location
(
'i4x'
,
location
.
org
,
location
.
course
,
category_name
,
None
)
items
=
modulestore
.
get_items
(
query_loc
)
for
item
in
items
:
fs
=
OSFS
(
root_dir
/
(
'test_export/'
+
dirname
))
self
.
assertTrue
(
fs
.
exists
(
item
.
location
.
name
+
filename_suffix
))
def
test_export_course
(
self
):
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
import_from_xml
(
ms
,
'common/test/data/'
,
[
'full'
])
location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
root_dir
=
path
(
mkdtemp
())
print
'Exporting to tempdir = {0}'
.
format
(
root_dir
)
# export out to a tempdir
export_to_xml
(
ms
,
cs
,
location
,
root_dir
,
'test_export'
)
# check for static tabs
self
.
verify_content_existence
(
ms
,
root_dir
,
location
,
'tabs'
,
'static_tab'
,
'.html'
)
# check for custom_tags
self
.
verify_content_existence
(
ms
,
root_dir
,
location
,
'info'
,
'course_info'
,
'.html'
)
# check for custom_tags
self
.
verify_content_existence
(
ms
,
root_dir
,
location
,
'custom_tags'
,
'custom_tag_template'
)
# remove old course
delete_course
(
ms
,
cs
,
location
)
# reimport
import_from_xml
(
ms
,
root_dir
,
[
'test_export'
])
items
=
ms
.
get_items
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'vertical'
,
None
]))
self
.
assertGreater
(
len
(
items
),
0
)
for
descriptor
in
items
:
print
"Checking {0}...."
.
format
(
descriptor
.
location
.
url
())
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
descriptor
.
location
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
shutil
.
rmtree
(
root_dir
)
def
test_course_handouts_rewrites
(
self
):
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
# import a test course
import_from_xml
(
ms
,
'common/test/data/'
,
[
'full'
])
handout_location
=
Location
([
'i4x'
,
'edX'
,
'full'
,
'course_info'
,
'handouts'
])
# get module info
resp
=
self
.
client
.
get
(
reverse
(
'module_info'
,
kwargs
=
{
'module_location'
:
handout_location
}))
# make sure we got a successful response
self
.
assertEqual
(
resp
.
status_code
,
200
)
# check that /static/ has been converted to the full path
# note, we know the link it should be because that's what in the 'full' course in the test data
self
.
assertContains
(
resp
,
'/c4x/edX/full/asset/handouts_schematic_tutorial.pdf'
)
class
ContentStoreTest
(
ModuleStoreTestCase
):
"""
Tests for the CMS ContentStore application.
"""
def
setUp
(
self
):
"""
These tests need a user in the DB so that the django Test Client
can log them in.
They inherit from the ModuleStoreTestCase class so that the mongodb collection
will be cleared out before each test case execution and deleted
afterwards.
"""
uname
=
'testuser'
email
=
'test+courses@edx.org'
password
=
'foo'
# Create the use so we can log them in.
self
.
user
=
User
.
objects
.
create_user
(
uname
,
email
,
password
)
# Note that we do not actually need to do anything
# for registration if we directly mark them active.
self
.
user
.
is_active
=
True
# Staff has access to view all courses
self
.
user
.
is_staff
=
True
self
.
user
.
save
()
self
.
client
=
Client
()
self
.
client
.
login
(
username
=
uname
,
password
=
password
)
self
.
course_data
=
{
'template'
:
'i4x://edx/templates/course/Empty'
,
'org'
:
'MITx'
,
'number'
:
'999'
,
'display_name'
:
'Robot Super Course'
,
}
def
test_create_course
(
self
):
"""Test new course creation - happy path"""
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
data
[
'id'
],
'i4x://MITx/999/course/Robot_Super_Course'
)
def
test_create_course_duplicate_course
(
self
):
"""Test new course creation - error path"""
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
data
[
'ErrMsg'
],
'There is already a course defined with this name.'
)
def
test_create_course_duplicate_number
(
self
):
"""Test new course creation - error path"""
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
self
.
course_data
[
'display_name'
]
=
'Robot Super Course Two'
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
data
[
'ErrMsg'
],
'There is already a course defined with the same organization and course number.'
)
def
test_create_course_with_bad_organization
(
self
):
"""Test new course creation - error path for bad organization name"""
self
.
course_data
[
'org'
]
=
'University of California, Berkeley'
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
data
[
'ErrMsg'
],
"Unable to create course 'Robot Super Course'.
\n\n
Invalid characters in 'University of California, Berkeley'."
)
def
test_course_index_view_with_no_courses
(
self
):
"""Test viewing the index page with no courses"""
# Create a course so there is something to view
resp
=
self
.
client
.
get
(
reverse
(
'index'
))
self
.
assertContains
(
resp
,
'<h1>My Courses</h1>'
,
status_code
=
200
,
html
=
True
)
def
test_course_factory
(
self
):
"""Test that the course factory works correctly."""
course
=
CourseFactory
.
create
()
self
.
assertIsInstance
(
course
,
CourseDescriptor
)
def
test_item_factory
(
self
):
"""Test that the item factory works correctly."""
course
=
CourseFactory
.
create
()
item
=
ItemFactory
.
create
(
parent_location
=
course
.
location
)
self
.
assertIsInstance
(
item
,
SequenceDescriptor
)
def
test_course_index_view_with_course
(
self
):
"""Test viewing the index page with an existing course"""
CourseFactory
.
create
(
display_name
=
'Robot Super Educational Course'
)
resp
=
self
.
client
.
get
(
reverse
(
'index'
))
self
.
assertContains
(
resp
,
'<span class="class-name">Robot Super Educational Course</span>'
,
status_code
=
200
,
html
=
True
)
def
test_course_overview_view_with_course
(
self
):
"""Test viewing the course overview page with an existing course"""
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
data
=
{
'org'
:
'MITx'
,
'course'
:
'999'
,
'name'
:
Location
.
clean
(
'Robot Super Course'
),
}
resp
=
self
.
client
.
get
(
reverse
(
'course_index'
,
kwargs
=
data
))
self
.
assertContains
(
resp
,
'<a href="/MITx/999/course/Robot_Super_Course" class="class-name">Robot Super Course</a>'
,
status_code
=
200
,
html
=
True
)
def
test_clone_item
(
self
):
"""Test cloning an item. E.g. creating a new section"""
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
section_data
=
{
'parent_location'
:
'i4x://MITx/999/course/Robot_Super_Course'
,
'template'
:
'i4x://edx/templates/chapter/Empty'
,
'display_name'
:
'Section One'
,
}
resp
=
self
.
client
.
post
(
reverse
(
'clone_item'
),
section_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
self
.
assertRegexpMatches
(
data
[
'id'
],
'^i4x:
\
/
\
/MITx
\
/999
\
/chapter
\
/([0-9]|[a-f]){32}$'
)
def
test_capa_module
(
self
):
"""Test that a problem treats markdown specially."""
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
problem_data
=
{
'parent_location'
:
'i4x://MITx/999/course/Robot_Super_Course'
,
'template'
:
'i4x://edx/templates/problem/Empty'
}
resp
=
self
.
client
.
post
(
reverse
(
'clone_item'
),
problem_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
payload
=
parse_json
(
resp
)
problem_loc
=
payload
[
'id'
]
problem
=
get_modulestore
(
problem_loc
)
.
get_item
(
problem_loc
)
# should be a CapaDescriptor
self
.
assertIsInstance
(
problem
,
CapaDescriptor
,
"New problem is not a CapaDescriptor"
)
context
=
problem
.
get_context
()
self
.
assertIn
(
'markdown'
,
context
,
"markdown is missing from context"
)
self
.
assertIn
(
'markdown'
,
problem
.
metadata
,
"markdown is missing from metadata"
)
self
.
assertNotIn
(
'markdown'
,
problem
.
editable_metadata_fields
,
"Markdown slipped into the editable metadata fields"
)
cms/djangoapps/contentstore/tests/test_core_caching.py
View file @
e8d01a08
from
django.test.testcases
import
TestCase
from
cache_toolbox.core
import
get_cached_content
,
set_cached_content
,
del_cached_content
from
xmodule.modulestore
import
Location
from
xmodule.contentstore.content
import
StaticContent
from
django.test
import
TestCase
class
Content
:
def
__init__
(
self
,
location
,
content
):
...
...
@@ -32,7 +32,3 @@ class CachingTestCase(TestCase):
'should not be stored in cache with unicodeLocation'
)
self
.
assertEqual
(
None
,
get_cached_content
(
self
.
nonUnicodeLocation
),
'should not be stored in cache with nonUnicodeLocation'
)
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
e8d01a08
from
django.test.testcases
import
TestCase
import
datetime
import
time
from
django.contrib.auth.models
import
User
import
xmodule
from
django.test.client
import
Client
from
django.core.urlresolvers
import
reverse
from
xmodule.modulestore
import
Location
from
cms.djangoapps.models.settings.course_details
import
CourseDetails
,
\
CourseSettingsEncoder
import
json
from
util
import
converters
import
calendar
import
copy
from
util
import
converters
from
util.converters
import
jsdate_to_time
from
django.contrib.auth.models
import
User
from
django.test.client
import
Client
from
django.core.urlresolvers
import
reverse
from
django.utils.timezone
import
UTC
import
xmodule
from
xmodule.modulestore
import
Location
from
cms.djangoapps.models.settings.course_details
import
(
CourseDetails
,
CourseSettingsEncoder
)
from
cms.djangoapps.models.settings.course_grading
import
CourseGradingModel
from
cms.djangoapps.contentstore.utils
import
get_modulestore
import
copy
from
django.test
import
TestCase
from
utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
# YYYY-MM-DDThh:mm:ss.s+/-HH:MM
class
ConvertersTestCase
(
TestCase
):
...
...
@@ -36,8 +42,15 @@ class ConvertersTestCase(TestCase):
self
.
compare_dates
(
converters
.
jsdate_to_time
(
"2013-01-01T00:00:00"
),
converters
.
jsdate_to_time
(
"2012-12-31T23:59:59"
),
datetime
.
timedelta
(
seconds
=
1
))
class
CourseTestCase
(
TestCase
):
class
CourseTestCase
(
ModuleStore
TestCase
):
def
setUp
(
self
):
"""
These tests need a user in the DB so that the django Test Client
can log them in.
They inherit from the ModuleStoreTestCase class so that the mongodb collection
will be cleared out before each test case execution and deleted
afterwards.
"""
uname
=
'testuser'
email
=
'test+courses@edx.org'
password
=
'foo'
...
...
@@ -52,36 +65,15 @@ class CourseTestCase(TestCase):
self
.
user
.
is_staff
=
True
self
.
user
.
save
()
# Flush and initialize the module store
# It needs the templates because it creates new records
# by cloning from the template.
# Note that if your test module gets in some weird state
# (though it shouldn't), do this manually
# from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()"
xmodule
.
modulestore
.
django
.
_MODULESTORES
=
{}
xmodule
.
modulestore
.
django
.
modulestore
()
.
collection
.
drop
()
xmodule
.
templates
.
update_templates
()
self
.
client
=
Client
()
self
.
client
.
login
(
username
=
uname
,
password
=
password
)
self
.
course_data
=
{
'template'
:
'i4x://edx/templates/course/Empty'
,
'org'
:
'MITx'
,
'number'
:
'999'
,
'display_name'
:
'Robot Super Course'
,
}
self
.
course_location
=
Location
(
'i4x'
,
'MITx'
,
'999'
,
'course'
,
'Robot_Super_Course'
)
self
.
create_course
()
def
tearDown
(
self
):
xmodule
.
modulestore
.
django
.
_MODULESTORES
=
{}
xmodule
.
modulestore
.
django
.
modulestore
()
.
collection
.
drop
()
def
create_course
(
self
):
"""Create new course"""
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
t
=
'i4x://edx/templates/course/Empty'
o
=
'MITx'
n
=
'999'
dn
=
'Robot Super Course'
self
.
course_location
=
Location
(
'i4x'
,
o
,
n
,
'course'
,
'Robot_Super_Course'
)
CourseFactory
.
create
(
template
=
t
,
org
=
o
,
number
=
n
,
display_name
=
dn
)
class
CourseDetailsTestCase
(
CourseTestCase
):
def
test_virgin_fetch
(
self
):
...
...
@@ -145,7 +137,6 @@ class CourseDetailsViewTest(CourseTestCase):
return
datetime
.
isoformat
(
"T"
)
else
:
return
None
def
test_update_and_fetch
(
self
):
details
=
CourseDetails
.
fetch
(
self
.
course_location
)
...
...
@@ -271,5 +262,3 @@ class CourseGradingTest(CourseTestCase):
test_grader
.
graders
[
1
][
'drop_count'
]
=
test_grader
.
graders
[
1
]
.
get
(
'drop_count'
)
+
1
altered_grader
=
CourseGradingModel
.
update_grader_from_json
(
test_grader
.
course_location
,
test_grader
.
graders
[
1
])
self
.
assertDictEqual
(
test_grader
.
graders
[
1
],
altered_grader
,
"drop_count[1] + 2"
)
cms/djangoapps/contentstore/tests/test_utils.py
View file @
e8d01a08
from
django.test.testcases
import
TestCase
from
cms.djangoapps.contentstore
import
utils
import
mock
from
django.test
import
TestCase
class
LMSLinksTestCase
(
TestCase
):
def
about_page_test
(
self
):
...
...
cms/djangoapps/contentstore/tests/tests.py
View file @
e8d01a08
import
json
import
shutil
from
django.test
import
TestCase
from
django.test.client
import
Client
from
override_settings
import
override_settings
from
django.conf
import
settings
...
...
@@ -9,40 +8,27 @@ from path import path
from
tempfile
import
mkdtemp
import
json
from
fs.osfs
import
OSFS
from
student.models
import
Registration
from
django.contrib.auth.models
import
User
import
xmodule.modulestore.django
from
xmodule.modulestore.xml_importer
import
import_from_xml
import
copy
from
factories
import
*
from
cms.djangoapps.contentstore.utils
import
get_modulestore
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.store_utilities
import
clone_course
from
xmodule.modulestore.store_utilities
import
delete_course
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
,
_MODULESTORES
from
xmodule.contentstore.django
import
contentstore
from
xmodule.
course_module
import
CourseDescriptor
from
xmodule.
templates
import
update_templates
from
xmodule.modulestore.xml_exporter
import
export_to_xml
from
cms.djangoapps.contentstore.utils
import
get_modulestore
from
xmodule.capa_module
import
CapaDescriptor
def
parse_json
(
response
):
"""Parse response, which is assumed to be json"""
return
json
.
loads
(
response
.
content
)
def
user
(
email
):
"""look up a user by email"""
return
User
.
objects
.
get
(
email
=
email
)
from
xmodule.modulestore.xml_importer
import
import_from_xml
def
registration
(
email
):
"""look up registration object by email"""
return
Registration
.
objects
.
get
(
user__email
=
email
)
from
xmodule.capa_module
import
CapaDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.seq_module
import
SequenceDescriptor
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
utils
import
ModuleStoreTestCase
,
parse_json
,
user
,
registration
class
ContentStoreTestCase
(
TestCase
):
class
ContentStoreTestCase
(
ModuleStore
TestCase
):
def
_login
(
self
,
email
,
pw
):
"""Login. View should always return 200. The success/fail is in the
returned json"""
...
...
@@ -187,356 +173,3 @@ class AuthTestCase(ContentStoreTestCase):
self
.
assertEqual
(
resp
.
status_code
,
302
)
# Logged in should work.
TEST_DATA_MODULESTORE
=
copy
.
deepcopy
(
settings
.
MODULESTORE
)
TEST_DATA_MODULESTORE
[
'default'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
TEST_DATA_MODULESTORE
[
'direct'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MODULESTORE
)
class
ContentStoreTest
(
TestCase
):
def
setUp
(
self
):
uname
=
'testuser'
email
=
'test+courses@edx.org'
password
=
'foo'
# Create the use so we can log them in.
self
.
user
=
User
.
objects
.
create_user
(
uname
,
email
,
password
)
# Note that we do not actually need to do anything
# for registration if we directly mark them active.
self
.
user
.
is_active
=
True
# Staff has access to view all courses
self
.
user
.
is_staff
=
True
self
.
user
.
save
()
# Flush and initialize the module store
# It needs the templates because it creates new records
# by cloning from the template.
# Note that if your test module gets in some weird state
# (though it shouldn't), do this manually
# from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()"
xmodule
.
modulestore
.
django
.
_MODULESTORES
=
{}
xmodule
.
modulestore
.
django
.
modulestore
()
.
collection
.
drop
()
xmodule
.
templates
.
update_templates
()
self
.
client
=
Client
()
self
.
client
.
login
(
username
=
uname
,
password
=
password
)
self
.
course_data
=
{
'template'
:
'i4x://edx/templates/course/Empty'
,
'org'
:
'MITx'
,
'number'
:
'999'
,
'display_name'
:
'Robot Super Course'
,
}
def
tearDown
(
self
):
# Make sure you flush out the test modulestore after the end
# of the last test because otherwise on the next run
# cms/djangoapps/contentstore/__init__.py
# update_templates() will try to update the templates
# via upsert and it sometimes seems to be messing things up.
xmodule
.
modulestore
.
django
.
_MODULESTORES
=
{}
xmodule
.
modulestore
.
django
.
modulestore
()
.
collection
.
drop
()
def
test_create_course
(
self
):
"""Test new course creation - happy path"""
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
data
[
'id'
],
'i4x://MITx/999/course/Robot_Super_Course'
)
def
test_create_course_duplicate_course
(
self
):
"""Test new course creation - error path"""
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
data
[
'ErrMsg'
],
'There is already a course defined with this name.'
)
def
test_create_course_duplicate_number
(
self
):
"""Test new course creation - error path"""
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
self
.
course_data
[
'display_name'
]
=
'Robot Super Course Two'
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
data
[
'ErrMsg'
],
'There is already a course defined with the same organization and course number.'
)
def
test_create_course_with_bad_organization
(
self
):
"""Test new course creation - error path for bad organization name"""
self
.
course_data
[
'org'
]
=
'University of California, Berkeley'
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
data
[
'ErrMsg'
],
"Unable to create course 'Robot Super Course'.
\n\n
Invalid characters in 'University of California, Berkeley'."
)
def
test_course_index_view_with_no_courses
(
self
):
"""Test viewing the index page with no courses"""
# Create a course so there is something to view
resp
=
self
.
client
.
get
(
reverse
(
'index'
))
self
.
assertContains
(
resp
,
'<h1>My Courses</h1>'
,
status_code
=
200
,
html
=
True
)
def
test_course_factory
(
self
):
course
=
CourseFactory
.
create
()
self
.
assertIsInstance
(
course
,
xmodule
.
course_module
.
CourseDescriptor
)
def
test_item_factory
(
self
):
course
=
CourseFactory
.
create
()
item
=
ItemFactory
.
create
(
parent_location
=
course
.
location
)
self
.
assertIsInstance
(
item
,
xmodule
.
seq_module
.
SequenceDescriptor
)
def
test_course_index_view_with_course
(
self
):
"""Test viewing the index page with an existing course"""
CourseFactory
.
create
(
display_name
=
'Robot Super Educational Course'
)
resp
=
self
.
client
.
get
(
reverse
(
'index'
))
self
.
assertContains
(
resp
,
'<span class="class-name">Robot Super Educational Course</span>'
,
status_code
=
200
,
html
=
True
)
def
test_course_overview_view_with_course
(
self
):
"""Test viewing the course overview page with an existing course"""
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
data
=
{
'org'
:
'MITx'
,
'course'
:
'999'
,
'name'
:
Location
.
clean
(
'Robot Super Course'
),
}
resp
=
self
.
client
.
get
(
reverse
(
'course_index'
,
kwargs
=
data
))
self
.
assertContains
(
resp
,
'<a href="/MITx/999/course/Robot_Super_Course" class="class-name">Robot Super Course</a>'
,
status_code
=
200
,
html
=
True
)
def
test_clone_item
(
self
):
"""Test cloning an item. E.g. creating a new section"""
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
section_data
=
{
'parent_location'
:
'i4x://MITx/999/course/Robot_Super_Course'
,
'template'
:
'i4x://edx/templates/chapter/Empty'
,
'display_name'
:
'Section One'
,
}
resp
=
self
.
client
.
post
(
reverse
(
'clone_item'
),
section_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
self
.
assertRegexpMatches
(
data
[
'id'
],
'^i4x:
\
/
\
/MITx
\
/999
\
/chapter
\
/([0-9]|[a-f]){32}$'
)
def
check_edit_unit
(
self
,
test_course_name
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
test_course_name
])
for
descriptor
in
modulestore
()
.
get_items
(
Location
(
None
,
None
,
'vertical'
,
None
,
None
)):
print
"Checking "
,
descriptor
.
location
.
url
()
print
descriptor
.
__class__
,
descriptor
.
location
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
descriptor
.
location
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
def
test_edit_unit_toy
(
self
):
self
.
check_edit_unit
(
'toy'
)
def
test_edit_unit_full
(
self
):
self
.
check_edit_unit
(
'full'
)
def
test_static_tab_reordering
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
course
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'course'
,
'6.002_Spring_2012'
,
None
]))
# reverse the ordering
reverse_tabs
=
[]
for
tab
in
course
.
tabs
:
if
tab
[
'type'
]
==
'static_tab'
:
reverse_tabs
.
insert
(
0
,
'i4x://edX/full/static_tab/{0}'
.
format
(
tab
[
'url_slug'
]))
resp
=
self
.
client
.
post
(
reverse
(
'reorder_static_tabs'
),
json
.
dumps
({
'tabs'
:
reverse_tabs
}),
"application/json"
)
course
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'course'
,
'6.002_Spring_2012'
,
None
]))
# compare to make sure that the tabs information is in the expected order after the server call
course_tabs
=
[]
for
tab
in
course
.
tabs
:
if
tab
[
'type'
]
==
'static_tab'
:
course_tabs
.
append
(
'i4x://edX/full/static_tab/{0}'
.
format
(
tab
[
'url_slug'
]))
self
.
assertEqual
(
reverse_tabs
,
course_tabs
)
def
test_about_overrides
(
self
):
'''
This test case verifies that a course can use specialized override for about data, e.g. /about/Fall_2012/effort.html
while there is a base definition in /about/effort.html
'''
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
effort
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'about'
,
'effort'
,
None
]))
self
.
assertEqual
(
effort
.
definition
[
'data'
],
'6 hours'
)
# this one should be in a non-override folder
effort
=
ms
.
get_item
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'about'
,
'end_date'
,
None
]))
self
.
assertEqual
(
effort
.
definition
[
'data'
],
'TBD'
)
def
test_remove_hide_progress_tab
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
source_location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
course
=
ms
.
get_item
(
source_location
)
self
.
assertNotIn
(
'hide_progress_tab'
,
course
.
metadata
)
def
test_clone_course
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
resp
=
self
.
client
.
post
(
reverse
(
'create_new_course'
),
self
.
course_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
data
=
parse_json
(
resp
)
self
.
assertEqual
(
data
[
'id'
],
'i4x://MITx/999/course/Robot_Super_Course'
)
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
source_location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
dest_location
=
CourseDescriptor
.
id_to_location
(
'MITx/999/Robot_Super_Course'
)
clone_course
(
ms
,
cs
,
source_location
,
dest_location
)
# now loop through all the units in the course and verify that the clone can render them, which
# means the objects are at least present
items
=
ms
.
get_items
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'vertical'
,
None
]))
self
.
assertGreater
(
len
(
items
),
0
)
clone_items
=
ms
.
get_items
(
Location
([
'i4x'
,
'MITx'
,
'999'
,
'vertical'
,
None
]))
self
.
assertGreater
(
len
(
clone_items
),
0
)
for
descriptor
in
items
:
new_loc
=
descriptor
.
location
.
_replace
(
org
=
'MITx'
,
course
=
'999'
)
print
"Checking {0} should now also be at {1}"
.
format
(
descriptor
.
location
.
url
(),
new_loc
.
url
())
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
new_loc
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
def
test_delete_course
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
delete_course
(
ms
,
cs
,
location
)
items
=
ms
.
get_items
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'vertical'
,
None
]))
self
.
assertEqual
(
len
(
items
),
0
)
def
verify_content_existence
(
self
,
modulestore
,
root_dir
,
location
,
dirname
,
category_name
,
filename_suffix
=
''
):
fs
=
OSFS
(
root_dir
/
'test_export'
)
self
.
assertTrue
(
fs
.
exists
(
dirname
))
query_loc
=
Location
(
'i4x'
,
location
.
org
,
location
.
course
,
category_name
,
None
)
items
=
modulestore
.
get_items
(
query_loc
)
for
item
in
items
:
fs
=
OSFS
(
root_dir
/
(
'test_export/'
+
dirname
))
self
.
assertTrue
(
fs
.
exists
(
item
.
location
.
name
+
filename_suffix
))
def
test_export_course
(
self
):
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
import_from_xml
(
ms
,
'common/test/data/'
,
[
'full'
])
location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
root_dir
=
path
(
mkdtemp
())
print
'Exporting to tempdir = {0}'
.
format
(
root_dir
)
# export out to a tempdir
export_to_xml
(
ms
,
cs
,
location
,
root_dir
,
'test_export'
)
# check for static tabs
self
.
verify_content_existence
(
ms
,
root_dir
,
location
,
'tabs'
,
'static_tab'
,
'.html'
)
# check for custom_tags
self
.
verify_content_existence
(
ms
,
root_dir
,
location
,
'info'
,
'course_info'
,
'.html'
)
# check for custom_tags
self
.
verify_content_existence
(
ms
,
root_dir
,
location
,
'custom_tags'
,
'custom_tag_template'
)
# remove old course
delete_course
(
ms
,
cs
,
location
)
# reimport
import_from_xml
(
ms
,
root_dir
,
[
'test_export'
])
items
=
ms
.
get_items
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'vertical'
,
None
]))
self
.
assertGreater
(
len
(
items
),
0
)
for
descriptor
in
items
:
print
"Checking {0}...."
.
format
(
descriptor
.
location
.
url
())
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
descriptor
.
location
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
shutil
.
rmtree
(
root_dir
)
def
test_course_handouts_rewrites
(
self
):
ms
=
modulestore
(
'direct'
)
cs
=
contentstore
()
# import a test course
import_from_xml
(
ms
,
'common/test/data/'
,
[
'full'
])
handout_location
=
Location
([
'i4x'
,
'edX'
,
'full'
,
'course_info'
,
'handouts'
])
# get module info
resp
=
self
.
client
.
get
(
reverse
(
'module_info'
,
kwargs
=
{
'module_location'
:
handout_location
}))
# make sure we got a successful response
self
.
assertEqual
(
resp
.
status_code
,
200
)
# check that /static/ has been converted to the full path
# note, we know the link it should be because that's what in the 'full' course in the test data
self
.
assertContains
(
resp
,
'/c4x/edX/full/asset/handouts_schematic_tutorial.pdf'
)
def
test_missing_static_content
(
self
):
resp
=
self
.
client
.
get
(
"/c4x/asd/asd/asd/asd"
)
self
.
assertEqual
(
resp
.
status_code
,
404
)
def
test_capa_module
(
self
):
"""Test that a problem treats markdown specially."""
CourseFactory
.
create
(
org
=
'MITx'
,
course
=
'999'
,
display_name
=
'Robot Super Course'
)
problem_data
=
{
'parent_location'
:
'i4x://MITx/999/course/Robot_Super_Course'
,
'template'
:
'i4x://edx/templates/problem/Empty'
}
resp
=
self
.
client
.
post
(
reverse
(
'clone_item'
),
problem_data
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
payload
=
parse_json
(
resp
)
problem_loc
=
payload
[
'id'
]
problem
=
get_modulestore
(
problem_loc
)
.
get_item
(
problem_loc
)
# should be a CapaDescriptor
self
.
assertIsInstance
(
problem
,
CapaDescriptor
,
"New problem is not a CapaDescriptor"
)
context
=
problem
.
get_context
()
self
.
assertIn
(
'markdown'
,
context
,
"markdown is missing from context"
)
self
.
assertIn
(
'markdown'
,
problem
.
metadata
,
"markdown is missing from metadata"
)
self
.
assertNotIn
(
'markdown'
,
problem
.
editable_metadata_fields
,
"Markdown slipped into the editable metadata fields"
)
\ No newline at end of file
cms/djangoapps/contentstore/tests/utils.py
0 → 100644
View file @
e8d01a08
import
json
import
copy
from
time
import
time
from
django.test
import
TestCase
from
override_settings
import
override_settings
from
django.conf
import
settings
from
student.models
import
Registration
from
django.contrib.auth.models
import
User
import
xmodule.modulestore.django
from
xmodule.templates
import
update_templates
class
ModuleStoreTestCase
(
TestCase
):
""" Subclass for any test case that uses the mongodb
module store. This populates a uniquely named modulestore
collection with templates before running the TestCase
and drops it they are finished. """
def
_pre_setup
(
self
):
super
(
ModuleStoreTestCase
,
self
)
.
_pre_setup
()
# Use the current seconds since epoch to differentiate
# the mongo collections on jenkins.
sec_since_epoch
=
'
%
s'
%
int
(
time
()
*
100
)
self
.
orig_MODULESTORE
=
copy
.
deepcopy
(
settings
.
MODULESTORE
)
self
.
test_MODULESTORE
=
self
.
orig_MODULESTORE
self
.
test_MODULESTORE
[
'default'
][
'OPTIONS'
][
'collection'
]
=
'modulestore_
%
s'
%
sec_since_epoch
self
.
test_MODULESTORE
[
'direct'
][
'OPTIONS'
][
'collection'
]
=
'modulestore_
%
s'
%
sec_since_epoch
settings
.
MODULESTORE
=
self
.
test_MODULESTORE
# Flush and initialize the module store
# It needs the templates because it creates new records
# by cloning from the template.
# Note that if your test module gets in some weird state
# (though it shouldn't), do this manually
# from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()"
xmodule
.
modulestore
.
django
.
_MODULESTORES
=
{}
update_templates
()
def
_post_teardown
(
self
):
# Make sure you flush out the modulestore.
# Drop the collection at the end of the test,
# otherwise there will be lingering collections leftover
# from executing the tests.
xmodule
.
modulestore
.
django
.
_MODULESTORES
=
{}
xmodule
.
modulestore
.
django
.
modulestore
()
.
collection
.
drop
()
settings
.
MODULESTORE
=
self
.
orig_MODULESTORE
super
(
ModuleStoreTestCase
,
self
)
.
_post_teardown
()
def
parse_json
(
response
):
"""Parse response, which is assumed to be json"""
return
json
.
loads
(
response
.
content
)
def
user
(
email
):
"""look up a user by email"""
return
User
.
objects
.
get
(
email
=
email
)
def
registration
(
email
):
"""look up registration object by email"""
return
Registration
.
objects
.
get
(
user__email
=
email
)
cms/djangoapps/contentstore/views.py
View file @
e8d01a08
...
...
@@ -1240,6 +1240,11 @@ def edge(request):
@login_required
@expect_json
def
create_new_course
(
request
):
# This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there.
# TODO: write a test that creates two courses, one with the factory and
# the other with this method, then compare them to make sure they are
# equivalent.
template
=
Location
(
request
.
POST
[
'template'
])
org
=
request
.
POST
.
get
(
'org'
)
number
=
request
.
POST
.
get
(
'number'
)
...
...
@@ -1289,8 +1294,11 @@ def initialize_course_tabs(course):
# at least a list populated with the minimal times
# @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better
# place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here
course
.
tabs
=
[{
"type"
:
"courseware"
},
{
"type"
:
"course_info"
,
"name"
:
"Course Info"
},
# This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there.
course
.
tabs
=
[{
"type"
:
"courseware"
},
{
"type"
:
"course_info"
,
"name"
:
"Course Info"
},
{
"type"
:
"discussion"
,
"name"
:
"Discussion"
},
{
"type"
:
"wiki"
,
"name"
:
"Wiki"
},
{
"type"
:
"progress"
,
"name"
:
"Progress"
}]
...
...
cms/envs/test.py
View file @
e8d01a08
...
...
@@ -11,7 +11,6 @@ from .common import *
import
os
from
path
import
path
# Nose Test Runner
INSTALLED_APPS
+=
(
'django_nose'
,)
NOSE_ARGS
=
[
'--with-xunit'
]
...
...
@@ -72,17 +71,6 @@ DATABASES = {
'ENGINE'
:
'django.db.backends.sqlite3'
,
'NAME'
:
ENV_ROOT
/
"db"
/
"cms.db"
,
},
# The following are for testing purposes...
'edX/toy/2012_Fall'
:
{
'ENGINE'
:
'django.db.backends.sqlite3'
,
'NAME'
:
ENV_ROOT
/
"db"
/
"course1.db"
,
},
'edx/full/6.002_Spring_2012'
:
{
'ENGINE'
:
'django.db.backends.sqlite3'
,
'NAME'
:
ENV_ROOT
/
"db"
/
"course2.db"
,
}
}
LMS_BASE
=
"localhost:8000"
...
...
common/lib/xmodule/xmodule/modulestore/tests/factories.py
0 → 100644
View file @
e8d01a08
from
factory
import
Factory
from
time
import
gmtime
from
uuid
import
uuid4
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.timeparse
import
stringify_time
def
XMODULE_COURSE_CREATION
(
class_to_create
,
**
kwargs
):
return
XModuleCourseFactory
.
_create
(
class_to_create
,
**
kwargs
)
def
XMODULE_ITEM_CREATION
(
class_to_create
,
**
kwargs
):
return
XModuleItemFactory
.
_create
(
class_to_create
,
**
kwargs
)
class
XModuleCourseFactory
(
Factory
):
"""
Factory for XModule courses.
"""
ABSTRACT_FACTORY
=
True
_creation_function
=
(
XMODULE_COURSE_CREATION
,)
@classmethod
def
_create
(
cls
,
target_class
,
*
args
,
**
kwargs
):
# This logic was taken from the create_new_course method in
# cms/djangoapps/contentstore/views.py
template
=
Location
(
'i4x'
,
'edx'
,
'templates'
,
'course'
,
'Empty'
)
org
=
kwargs
.
get
(
'org'
)
number
=
kwargs
.
get
(
'number'
)
display_name
=
kwargs
.
get
(
'display_name'
)
location
=
Location
(
'i4x'
,
org
,
number
,
'course'
,
Location
.
clean
(
display_name
))
store
=
modulestore
(
'direct'
)
# Write the data to the mongo datastore
new_course
=
store
.
clone_item
(
template
,
location
)
# This metadata code was copied from cms/djangoapps/contentstore/views.py
if
display_name
is
not
None
:
new_course
.
metadata
[
'display_name'
]
=
display_name
new_course
.
metadata
[
'data_dir'
]
=
uuid4
()
.
hex
new_course
.
metadata
[
'start'
]
=
stringify_time
(
gmtime
())
new_course
.
tabs
=
[{
"type"
:
"courseware"
},
{
"type"
:
"course_info"
,
"name"
:
"Course Info"
},
{
"type"
:
"discussion"
,
"name"
:
"Discussion"
},
{
"type"
:
"wiki"
,
"name"
:
"Wiki"
},
{
"type"
:
"progress"
,
"name"
:
"Progress"
}]
# Update the data in the mongo datastore
store
.
update_metadata
(
new_course
.
location
.
url
(),
new_course
.
own_metadata
)
return
new_course
class
Course
:
pass
class
CourseFactory
(
XModuleCourseFactory
):
FACTORY_FOR
=
Course
template
=
'i4x://edx/templates/course/Empty'
org
=
'MITx'
number
=
'999'
display_name
=
'Robot Super Course'
class
XModuleItemFactory
(
Factory
):
"""
Factory for XModule items.
"""
ABSTRACT_FACTORY
=
True
_creation_function
=
(
XMODULE_ITEM_CREATION
,)
@classmethod
def
_create
(
cls
,
target_class
,
*
args
,
**
kwargs
):
"""
kwargs must include parent_location, template. Can contain display_name
target_class is ignored
"""
DETACHED_CATEGORIES
=
[
'about'
,
'static_tab'
,
'course_info'
]
parent_location
=
Location
(
kwargs
.
get
(
'parent_location'
))
template
=
Location
(
kwargs
.
get
(
'template'
))
display_name
=
kwargs
.
get
(
'display_name'
)
store
=
modulestore
(
'direct'
)
# This code was based off that in cms/djangoapps/contentstore/views.py
parent
=
store
.
get_item
(
parent_location
)
dest_location
=
parent_location
.
_replace
(
category
=
template
.
category
,
name
=
uuid4
()
.
hex
)
new_item
=
store
.
clone_item
(
template
,
dest_location
)
# TODO: This needs to be deleted when we have proper storage for static content
new_item
.
metadata
[
'data_dir'
]
=
parent
.
metadata
[
'data_dir'
]
# replace the display name with an optional parameter passed in from the caller
if
display_name
is
not
None
:
new_item
.
metadata
[
'display_name'
]
=
display_name
store
.
update_metadata
(
new_item
.
location
.
url
(),
new_item
.
own_metadata
)
if
new_item
.
location
.
category
not
in
DETACHED_CATEGORIES
:
store
.
update_children
(
parent_location
,
parent
.
definition
.
get
(
'children'
,
[])
+
[
new_item
.
location
.
url
()])
return
new_item
class
Item
:
pass
class
ItemFactory
(
XModuleItemFactory
):
FACTORY_FOR
=
Item
parent_location
=
'i4x://MITx/999/course/Robot_Super_Course'
template
=
'i4x://edx/templates/chapter/Empty'
display_name
=
'Section One'
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment