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
e65730b9
Commit
e65730b9
authored
Sep 30, 2015
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rationalize all cases around direct-only categories (both as blocks and parents) and delete
parent
ac507c3d
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
390 additions
and
144 deletions
+390
-144
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+2
-2
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
+21
-22
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
+176
-61
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
+0
-26
common/lib/xmodule/xmodule/modulestore/tests/test_semantics.py
+191
-33
No files found.
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
e65730b9
...
...
@@ -3036,9 +3036,9 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
Delete the orphan and any of its descendants which no longer have parents.
"""
if
len
(
self
.
_get_parents_from_structure
(
orphan
,
structure
))
==
0
:
for
child
in
structure
[
'blocks'
][
orphan
]
.
fields
.
get
(
'children'
,
[]):
orphan_data
=
structure
[
'blocks'
]
.
pop
(
orphan
)
for
child
in
orphan_data
.
fields
.
get
(
'children'
,
[]):
self
.
_delete_if_true_orphan
(
BlockKey
(
*
child
),
structure
)
del
structure
[
'blocks'
][
orphan
]
@contract
(
returns
=
BlockData
)
def
_new_block
(
self
,
user_id
,
category
,
block_fields
,
definition_id
,
new_id
,
raw
=
False
,
block_defaults
=
None
):
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
View file @
e65730b9
...
...
@@ -189,41 +189,40 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli
currently only provided by contentstore.views.item.orphan_handler
Otherwise, raises a ValueError.
"""
allowed_revisions
=
[
None
,
ModuleStoreEnum
.
RevisionOption
.
published_only
,
ModuleStoreEnum
.
RevisionOption
.
all
]
if
revision
not
in
allowed_revisions
:
raise
UnsupportedRevisionError
(
allowed_revisions
)
autopublish_parent
=
False
with
self
.
bulk_operations
(
location
.
course_key
):
if
isinstance
(
location
,
LibraryUsageLocator
):
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
library
]
# Libraries don't yet have draft/publish support
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
]
elif
location
.
category
in
DIRECT_ONLY_CATEGORIES
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
,
ModuleStoreEnum
.
BranchName
.
draft
]
elif
revision
==
ModuleStoreEnum
.
RevisionOption
.
all
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
,
ModuleStoreEnum
.
BranchName
.
draft
]
else
:
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
published
]
elif
revision
is
None
:
branches_to_delete
=
[
ModuleStoreEnum
.
BranchName
.
draft
]
if
location
.
category
in
DIRECT_ONLY_CATEGORIES
:
branches_to_delete
.
append
(
ModuleStoreEnum
.
BranchName
.
published
)
else
:
raise
UnsupportedRevisionError
(
[
None
,
ModuleStoreEnum
.
RevisionOption
.
published_only
,
ModuleStoreEnum
.
RevisionOption
.
all
]
parent_loc
=
self
.
get_parent_location
(
location
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
draft
))
autopublish_parent
=
(
not
skip_auto_publish
and
parent_loc
is
not
None
and
parent_loc
.
block_type
in
DIRECT_ONLY_CATEGORIES
)
self
.
_flag_publish_event
(
location
.
course_key
)
for
branch
in
branches_to_delete
:
branched_location
=
location
.
for_branch
(
branch
)
parent_loc
=
self
.
get_parent_location
(
branched_location
)
super
(
DraftVersioningModuleStore
,
self
)
.
delete_item
(
branched_location
,
user_id
)
# publish parent w/o child if deleted element is direct only (not based on type of parent)
# publish vertical to behave more like the old mongo/draft modulestore - TNL-2593
if
(
branch
==
ModuleStoreEnum
.
BranchName
.
draft
and
branched_location
.
block_type
in
(
DIRECT_ONLY_CATEGORIES
+
[
'vertical'
])
and
parent_loc
and
not
skip_auto_publish
):
# will publish if its not an orphan
if
autopublish_parent
:
self
.
publish
(
parent_loc
.
version_agnostic
(),
user_id
,
blacklist
=
EXCLUDE_ALL
,
**
kwargs
)
def
_map_revision_to_branch
(
self
,
key
,
revision
=
None
):
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
View file @
e65730b9
...
...
@@ -91,19 +91,19 @@ class CommonMixedModuleStoreSetup(CourseComparisonTest):
OPTIONS
=
{
'stores'
:
[
{
'NAME'
:
'draft'
,
'NAME'
:
ModuleStoreEnum
.
Type
.
mongo
,
'ENGINE'
:
'xmodule.modulestore.mongo.draft.DraftModuleStore'
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'OPTIONS'
:
modulestore_options
},
{
'NAME'
:
'split'
,
'NAME'
:
ModuleStoreEnum
.
Type
.
split
,
'ENGINE'
:
'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore'
,
'DOC_STORE_CONFIG'
:
DOC_STORE_CONFIG
,
'OPTIONS'
:
modulestore_options
},
{
'NAME'
:
'xml'
,
'NAME'
:
ModuleStoreEnum
.
Type
.
xml
,
'ENGINE'
:
'xmodule.modulestore.xml.XMLModuleStore'
,
'OPTIONS'
:
{
'data_dir'
:
DATA_DIR
,
...
...
@@ -289,8 +289,11 @@ class CommonMixedModuleStoreSetup(CourseComparisonTest):
self
.
xml_chapter_location
=
self
.
course_locations
[
self
.
XML_COURSEID1
]
.
replace
(
category
=
'chapter'
,
name
=
'Overview'
)
self
.
_create_course
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
self
.
assertEquals
(
default
,
self
.
store
.
get_modulestore_type
(
self
.
course
.
id
))
@ddt.ddt
@attr
(
'mongo'
)
...
...
@@ -298,7 +301,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
"""
Tests of the MixedModulestore interface methods.
"""
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_get_modulestore_type
(
self
,
default_ms
):
"""
Make sure we get back the store type we expect for given mappings
...
...
@@ -310,16 +313,15 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertEqual
(
self
.
store
.
get_modulestore_type
(
self
.
_course_key_from_string
(
self
.
XML_COURSEID2
)),
ModuleStoreEnum
.
Type
.
xml
)
mongo_ms_type
=
ModuleStoreEnum
.
Type
.
mongo
if
default_ms
==
'draft'
else
ModuleStoreEnum
.
Type
.
split
self
.
assertEqual
(
self
.
store
.
get_modulestore_type
(
self
.
_course_key_from_string
(
self
.
MONGO_COURSEID
)),
mongo_ms_type
self
.
_course_key_from_string
(
self
.
MONGO_COURSEID
)),
default_ms
)
# try an unknown mapping, it should be the 'default' store
self
.
assertEqual
(
self
.
store
.
get_modulestore_type
(
SlashSeparatedCourseKey
(
'foo'
,
'bar'
,
'2012_Fall'
)),
mongo_ms_type
SlashSeparatedCourseKey
(
'foo'
,
'bar'
,
'2012_Fall'
)),
default_ms
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_get_modulestore_cache
(
self
,
default_ms
):
"""
Make sure we cache discovered course mappings
...
...
@@ -354,7 +356,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# problem: One lookup to locate an item that exists
# fake: one w/ wildcard version
# split has one lookup for the course and then one for the course items
@ddt.data
((
'draft'
,
[
1
,
1
],
0
),
(
'split'
,
[
2
,
2
],
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
[
1
,
1
],
0
),
(
ModuleStoreEnum
.
Type
.
split
,
[
2
,
2
],
0
))
@ddt.unpack
def
test_has_item
(
self
,
default_ms
,
max_find
,
max_send
):
self
.
initdb
(
default_ms
)
...
...
@@ -382,7 +384,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# split:
# problem: active_versions, structure
# non-existent problem: ditto
@ddt.data
((
'draft'
,
[
3
,
2
],
0
),
(
'split'
,
[
2
,
2
],
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
[
3
,
2
],
0
),
(
ModuleStoreEnum
.
Type
.
split
,
[
2
,
2
],
0
))
@ddt.unpack
def
test_get_item
(
self
,
default_ms
,
max_find
,
max_send
):
self
.
initdb
(
default_ms
)
...
...
@@ -410,7 +412,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# wildcard query, 6! load pertinent items for inheritance calls, load parents, course root fetch (why)
# Split:
# active_versions (with regex), structure, and spurious active_versions refetch
@ddt.data
((
'draft'
,
14
,
0
),
(
'split'
,
3
,
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
14
,
0
),
(
ModuleStoreEnum
.
Type
.
split
,
3
,
0
))
@ddt.unpack
def
test_get_items
(
self
,
default_ms
,
max_find
,
max_send
):
self
.
initdb
(
default_ms
)
...
...
@@ -438,7 +440,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# sends: update problem and then each ancestor up to course (edit info)
# split: active_versions, definitions (calculator field), structures
# 2 sends to update index & structure (note, it would also be definition if a content field changed)
@ddt.data
((
'draft'
,
7
,
5
),
(
'split'
,
3
,
2
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
7
,
5
),
(
ModuleStoreEnum
.
Type
.
split
,
3
,
2
))
@ddt.unpack
def
test_update_item
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -463,7 +465,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertEqual
(
problem
.
max_attempts
,
2
,
"Update didn't persist"
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_has_changes_direct_only
(
self
,
default_ms
):
"""
Tests that has_changes() returns false when a new xblock in a direct only category is checked
...
...
@@ -484,7 +486,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertFalse
(
self
.
store
.
has_changes
(
test_course
))
self
.
assertFalse
(
self
.
store
.
has_changes
(
chapter
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_has_changes
(
self
,
default_ms
):
"""
Tests that has_changes() only returns true when changes are present
...
...
@@ -519,7 +521,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
component
=
self
.
store
.
publish
(
component
.
location
,
self
.
user_id
)
self
.
assertFalse
(
self
.
store
.
has_changes
(
component
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_unit_stuck_in_draft_mode
(
self
,
default_ms
):
"""
After revert_to_published() the has_changes() should return false if draft has no changes
...
...
@@ -551,7 +553,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
component
=
self
.
store
.
publish
(
component
.
location
,
self
.
user_id
)
self
.
assertFalse
(
self
.
store
.
has_changes
(
component
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_unit_stuck_in_published_mode
(
self
,
default_ms
):
"""
After revert_to_published() the has_changes() should return true if draft has changes
...
...
@@ -588,7 +590,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Verify that changes are present
self
.
assertTrue
(
self
.
store
.
has_changes
(
component
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_unit_stuck_in_published_mode_after_delete
(
self
,
default_ms
):
"""
Test that a unit does not get stuck in published mode
...
...
@@ -631,7 +633,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
vertical
=
self
.
store
.
get_item
(
vertical
.
location
)
self
.
assertTrue
(
self
.
_has_changes
(
vertical
.
location
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_publish_automatically_after_delete_unit
(
self
,
default_ms
):
"""
Check that sequential publishes automatically after deleting a unit
...
...
@@ -674,7 +676,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
return
locations
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_has_changes_ancestors
(
self
,
default_ms
):
"""
Tests that has_changes() returns true on ancestors when a child is changed
...
...
@@ -704,7 +706,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
for
key
in
locations
:
self
.
assertFalse
(
self
.
_has_changes
(
locations
[
key
]))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_has_changes_publish_ancestors
(
self
,
default_ms
):
"""
Tests that has_changes() returns false after a child is published only if all children are unchanged
...
...
@@ -741,7 +743,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertFalse
(
self
.
_has_changes
(
locations
[
'grandparent'
]))
self
.
assertFalse
(
self
.
_has_changes
(
locations
[
'parent'
]))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_has_changes_add_remove_child
(
self
,
default_ms
):
"""
Tests that has_changes() returns true for the parent when a child with changes is added
...
...
@@ -774,7 +776,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertFalse
(
self
.
_has_changes
(
locations
[
'grandparent'
]))
self
.
assertFalse
(
self
.
_has_changes
(
locations
[
'parent'
]))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_has_changes_non_direct_only_children
(
self
,
default_ms
):
"""
Tests that has_changes() returns true after editing the child of a vertical (both not direct only categories).
...
...
@@ -808,7 +810,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertTrue
(
self
.
_has_changes
(
child
.
location
))
@ddt.data
(
*
itertools
.
product
(
(
'draft'
,
'split'
),
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
),
(
ModuleStoreEnum
.
Branch
.
draft_preferred
,
ModuleStoreEnum
.
Branch
.
published_only
)
))
@ddt.unpack
...
...
@@ -840,14 +842,14 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Split
# Find: active_versions, 2 structures (published & draft), definition (unnecessary)
# Sends: updated draft and published structures and active_versions
@ddt.data
((
'draft'
,
7
,
2
),
(
'split'
,
4
,
3
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
7
,
2
),
(
ModuleStoreEnum
.
Type
.
split
,
3
,
3
))
@ddt.unpack
def
test_delete_item
(
self
,
default_ms
,
max_find
,
max_send
):
"""
Delete should reject on r/o db and work on r/w one
"""
self
.
initdb
(
default_ms
)
if
default_ms
==
'draft'
and
mongo_uses_error_check
(
self
.
store
):
if
default_ms
==
ModuleStoreEnum
.
Type
.
mongo
and
mongo_uses_error_check
(
self
.
store
):
max_find
+=
1
# r/o try deleting the chapter (is here to ensure it can't be deleted)
...
...
@@ -872,7 +874,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Split:
# queries: active_versions, draft and published structures, definition (unnecessary)
# sends: update published (why?), draft, and active_versions
@ddt.data
((
'draft'
,
9
,
2
),
(
'split'
,
4
,
3
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
9
,
2
),
(
ModuleStoreEnum
.
Type
.
split
,
4
,
3
))
@ddt.unpack
def
test_delete_private_vertical
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -880,7 +882,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
behavioral properties which this deletion test gets at.
"""
self
.
initdb
(
default_ms
)
if
default_ms
==
'draft'
and
mongo_uses_error_check
(
self
.
store
):
if
default_ms
==
ModuleStoreEnum
.
Type
.
mongo
and
mongo_uses_error_check
(
self
.
store
):
max_find
+=
1
# create and delete a private vertical with private children
private_vert
=
self
.
store
.
create_child
(
...
...
@@ -925,7 +927,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Split:
# find: active_version & structure (cached)
# send: update structure and active_versions
@ddt.data
((
'draft'
,
4
,
1
),
(
'split'
,
2
,
2
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
4
,
1
),
(
ModuleStoreEnum
.
Type
.
split
,
2
,
2
))
@ddt.unpack
def
test_delete_draft_vertical
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -955,7 +957,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
private_leaf
.
display_name
=
'change me'
private_leaf
=
self
.
store
.
update_item
(
private_leaf
,
self
.
user_id
)
# test succeeds if delete succeeds w/o error
if
default_ms
==
'draft'
and
mongo_uses_error_check
(
self
.
store
):
if
default_ms
==
ModuleStoreEnum
.
Type
.
mongo
and
mongo_uses_error_check
(
self
.
store
):
max_find
+=
1
with
check_mongo_calls
(
max_find
,
max_send
):
self
.
store
.
delete_item
(
private_leaf
.
location
,
self
.
user_id
)
...
...
@@ -968,7 +970,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# 1) wildcard split search,
# 2-4) active_versions, structure, definition (s/b lazy; so, unnecessary)
# 5) wildcard draft mongo which has none
@ddt.data
((
'draft'
,
3
,
0
),
(
'split'
,
5
,
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
3
,
0
),
(
ModuleStoreEnum
.
Type
.
split
,
5
,
0
))
@ddt.unpack
def
test_get_courses
(
self
,
default_ms
,
max_find
,
max_send
):
self
.
initdb
(
default_ms
)
...
...
@@ -987,7 +989,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
published_courses
=
self
.
store
.
get_courses
(
remove_branch
=
True
)
self
.
assertEquals
([
c
.
id
for
c
in
draft_courses
],
[
c
.
id
for
c
in
published_courses
])
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_create_child_detached_tabs
(
self
,
default_ms
):
"""
test 'create_child' method with a detached category ('static_tab')
...
...
@@ -1012,7 +1014,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
"""
Test that the xml modulestore only loaded the courses from the maps.
"""
self
.
initdb
(
'draft'
)
self
.
initdb
(
ModuleStoreEnum
.
Type
.
mongo
)
xml_store
=
self
.
store
.
_get_modulestore_by_type
(
ModuleStoreEnum
.
Type
.
xml
)
# pylint: disable=protected-access
courses
=
xml_store
.
get_courses
()
self
.
assertEqual
(
len
(
courses
),
2
)
...
...
@@ -1026,7 +1028,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
"""
Test that the xml modulestore doesn't allow write ops.
"""
self
.
initdb
(
'draft'
)
self
.
initdb
(
ModuleStoreEnum
.
Type
.
mongo
)
xml_store
=
self
.
store
.
_get_modulestore_by_type
(
ModuleStoreEnum
.
Type
.
xml
)
# pylint: disable=protected-access
# the important thing is not which exception it raises but that it raises an exception
with
self
.
assertRaises
(
AttributeError
):
...
...
@@ -1034,7 +1036,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# draft is 2: find out which ms owns course, get item
# split: active_versions, structure, definition (to load course wiki string)
@ddt.data
((
'draft'
,
2
,
0
),
(
'split'
,
3
,
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
2
,
0
),
(
ModuleStoreEnum
.
Type
.
split
,
3
,
0
))
@ddt.unpack
def
test_get_course
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -1049,7 +1051,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
course
=
self
.
store
.
get_item
(
self
.
course_locations
[
self
.
XML_COURSEID1
])
self
.
assertEqual
(
course
.
id
,
self
.
course_locations
[
self
.
XML_COURSEID1
]
.
course_key
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_get_library
(
self
,
default_ms
):
"""
Test that create_library and get_library work regardless of the default modulestore.
...
...
@@ -1074,7 +1076,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# still only 2)
# Draft: get_parent
# Split: active_versions, structure
@ddt.data
((
'draft'
,
1
,
0
),
(
'split'
,
2
,
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
1
,
0
),
(
ModuleStoreEnum
.
Type
.
split
,
2
,
0
))
@ddt.unpack
def
test_get_parent_locations
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -1100,7 +1102,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
store
.
get_parent_location
(
child_location
,
revision
=
revision
)
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_get_parent_locations_moved_child
(
self
,
default_ms
):
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
...
...
@@ -1151,7 +1153,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
(
child_to_move_location
,
new_parent_published_location
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
])
@ddt.data
(
'draft'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
)
def
test_get_parent_locations_deleted_child
(
self
,
default_ms
):
self
.
initdb
(
default_ms
)
self
.
_create_block_hierarchy
()
...
...
@@ -1182,7 +1184,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
(
child_to_delete_location
,
None
,
ModuleStoreEnum
.
RevisionOption
.
published_only
),
])
@ddt.data
(
'draft'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
)
def
test_get_parent_location_draft
(
self
,
default_ms
):
"""
Test that "get_parent_location" method returns first published parent
...
...
@@ -1225,7 +1227,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# 8-9. get vertical, compute inheritance
# 10-11. get other vertical_x1b (why?) and compute inheritance
# Split: active_versions & structure
@ddt.data
((
'draft'
,
[
12
,
3
],
0
),
(
'split'
,
[
2
,
2
],
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
[
12
,
3
],
0
),
(
ModuleStoreEnum
.
Type
.
split
,
[
2
,
2
],
0
))
@ddt.unpack
def
test_path_to_location
(
self
,
default_ms
,
num_finds
,
num_sends
):
"""
...
...
@@ -1276,7 +1278,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
with the toy and simple courses loaded.
"""
# only needs course_locations set
self
.
initdb
(
'draft'
)
self
.
initdb
(
ModuleStoreEnum
.
Type
.
mongo
)
course_key
=
self
.
course_locations
[
self
.
XML_COURSEID1
]
.
course_key
video_key
=
course_key
.
make_usage_key
(
'video'
,
'Welcome'
)
chapter_key
=
course_key
.
make_usage_key
(
'chapter'
,
'Overview'
)
...
...
@@ -1310,7 +1312,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertEqual
(
5
,
navigation_index
(
"5_2"
))
self
.
assertEqual
(
7
,
navigation_index
(
"7_3_5_6_"
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_revert_to_published_root_draft
(
self
,
default_ms
):
"""
Test calling revert_to_published on draft vertical.
...
...
@@ -1342,7 +1344,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertBlocksEqualByFields
(
reverted_parent
,
published_parent
)
self
.
assertFalse
(
self
.
_has_changes
(
self
.
vertical_x1a
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_revert_to_published_root_published
(
self
,
default_ms
):
"""
Test calling revert_to_published on a published vertical with a draft child.
...
...
@@ -1362,7 +1364,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
reverted_problem
=
self
.
store
.
get_item
(
self
.
problem_x1a_1
)
self
.
assertEqual
(
orig_display_name
,
reverted_problem
.
display_name
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_revert_to_published_no_draft
(
self
,
default_ms
):
"""
Test calling revert_to_published on vertical with no draft content does nothing.
...
...
@@ -1377,7 +1379,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertBlocksEqualByFields
(
orig_vertical
,
reverted_vertical
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_revert_to_published_no_published
(
self
,
default_ms
):
"""
Test calling revert_to_published on vertical with no published version errors.
...
...
@@ -1387,7 +1389,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
with
self
.
assertRaises
(
InvalidVersionError
):
self
.
store
.
revert_to_published
(
self
.
vertical_x1a
,
self
.
user_id
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_revert_to_published_direct_only
(
self
,
default_ms
):
"""
Test calling revert_to_published on a direct-only item is a no-op.
...
...
@@ -1402,7 +1404,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Draft: get all items which can be or should have parents
# Split: active_versions, structure
@ddt.data
((
'draft'
,
1
,
0
),
(
'split'
,
2
,
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
1
,
0
),
(
ModuleStoreEnum
.
Type
.
split
,
2
,
0
))
@ddt.unpack
def
test_get_orphans
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -1440,7 +1442,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
found_orphans
=
self
.
store
.
get_orphans
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
self
.
assertItemsEqual
(
found_orphans
,
orphan_locations
)
@ddt.data
(
'draft'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
)
def
test_get_non_orphan_parents
(
self
,
default_ms
):
"""
Test finding non orphan parents from many possible parents.
...
...
@@ -1502,7 +1504,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
with
self
.
assertRaises
(
ReferentialIntegrityError
):
self
.
store
.
get_parent_location
(
self
.
problem_x1a_1
)
@ddt.data
(
'draft'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
)
def
test_create_item_from_parent_location
(
self
,
default_ms
):
"""
Test a code path missed by the above: passing an old-style location as parent but no
...
...
@@ -1518,7 +1520,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
orphans
=
self
.
store
.
get_orphans
(
self
.
course_locations
[
self
.
MONGO_COURSEID
]
.
course_key
)
self
.
assertEqual
(
len
(
orphans
),
0
,
"unexpected orphans: {}"
.
format
(
orphans
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_create_item_populates_edited_info
(
self
,
default_ms
):
self
.
initdb
(
default_ms
)
block
=
self
.
store
.
create_item
(
...
...
@@ -1529,7 +1531,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertEqual
(
self
.
user_id
,
block
.
edited_by
)
self
.
assertGreater
(
datetime
.
datetime
.
now
(
UTC
),
block
.
edited_on
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_create_item_populates_subtree_edited_info
(
self
,
default_ms
):
self
.
initdb
(
default_ms
)
block
=
self
.
store
.
create_item
(
...
...
@@ -1542,7 +1544,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Draft: wildcard search of draft and split
# Split: wildcard search of draft and split
@ddt.data
((
'draft'
,
2
,
0
),
(
'split'
,
2
,
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
2
,
0
),
(
ModuleStoreEnum
.
Type
.
split
,
2
,
0
))
@ddt.unpack
def
test_get_courses_for_wiki
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -1580,14 +1582,14 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Sends:
# - insert structure
# - write index entry
@ddt.data
((
'draft'
,
2
,
6
),
(
'split'
,
3
,
2
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
2
,
6
),
(
ModuleStoreEnum
.
Type
.
split
,
3
,
2
))
@ddt.unpack
def
test_unpublish
(
self
,
default_ms
,
max_find
,
max_send
):
"""
Test calling unpublish
"""
self
.
initdb
(
default_ms
)
if
default_ms
==
'draft'
and
mongo_uses_error_check
(
self
.
store
):
if
default_ms
==
ModuleStoreEnum
.
Type
.
mongo
and
mongo_uses_error_check
(
self
.
store
):
max_find
+=
1
self
.
_create_block_hierarchy
()
...
...
@@ -1618,7 +1620,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Draft: specific query for revision None
# Split: active_versions, structure
@ddt.data
((
'draft'
,
1
,
0
),
(
'split'
,
2
,
0
))
@ddt.data
((
ModuleStoreEnum
.
Type
.
mongo
,
1
,
0
),
(
ModuleStoreEnum
.
Type
.
split
,
2
,
0
))
@ddt.unpack
def
test_has_published_version
(
self
,
default_ms
,
max_find
,
max_send
):
"""
...
...
@@ -1659,7 +1661,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertTrue
(
self
.
store
.
has_changes
(
item
))
self
.
assertTrue
(
self
.
store
.
has_published_version
(
item
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_update_edit_info_ancestors
(
self
,
default_ms
):
"""
Tests that edited_on, edited_by, subtree_edited_on, and subtree_edited_by are set correctly during update
...
...
@@ -1735,7 +1737,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Verify that others have unchanged edit info
check_node
(
sibling
.
location
,
None
,
after_create
,
self
.
user_id
,
None
,
after_create
,
self
.
user_id
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_update_edit_info
(
self
,
default_ms
):
"""
Tests that edited_on and edited_by are set correctly during an update
...
...
@@ -1765,7 +1767,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertLess
(
old_edited_on
,
updated_component
.
edited_on
)
self
.
assertEqual
(
updated_component
.
edited_by
,
edit_user
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_update_published_info
(
self
,
default_ms
):
"""
Tests that published_on and published_by are set correctly
...
...
@@ -1799,7 +1801,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
self
.
assertLessEqual
(
old_time
,
updated_component
.
published_on
)
self
.
assertEqual
(
updated_component
.
published_by
,
publish_user
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_auto_publish
(
self
,
default_ms
):
"""
Test that the correct things have been published automatically
...
...
@@ -1869,7 +1871,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
chapter
=
self
.
store
.
get_item
(
chapter
.
location
.
for_branch
(
None
))
self
.
assertTrue
(
self
.
store
.
has_published_version
(
chapter
))
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_get_courses_for_wiki_shared
(
self
,
default_ms
):
"""
Test two courses sharing the same wiki
...
...
@@ -1924,7 +1926,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
wiki_courses
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_branch_setting
(
self
,
default_ms
):
"""
Test the branch_setting context manager
...
...
@@ -2473,6 +2475,119 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
# Verify that the signal was emitted
signal_handler
.
send
.
assert_called_with
(
'course_deleted'
,
course_key
=
course_key
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_delete_published_item_orphans
(
self
,
default_store
):
"""
Tests delete published item dont create any oprhans in course
"""
self
.
initdb
(
default_store
)
course_locator
=
self
.
course
.
id
chapter
=
self
.
store
.
create_child
(
self
.
user_id
,
self
.
course
.
location
,
'chapter'
,
block_id
=
'section_one'
)
sequential
=
self
.
store
.
create_child
(
self
.
user_id
,
chapter
.
location
,
'sequential'
,
block_id
=
'subsection_one'
)
vertical
=
self
.
store
.
create_child
(
self
.
user_id
,
sequential
.
location
,
'vertical'
,
block_id
=
'moon_unit'
)
problem
=
self
.
store
.
create_child
(
self
.
user_id
,
vertical
.
location
,
'problem'
,
block_id
=
'problem'
)
self
.
store
.
publish
(
chapter
.
location
,
self
.
user_id
)
# Verify that there are no changes
self
.
assertFalse
(
self
.
_has_changes
(
chapter
.
location
))
self
.
assertFalse
(
self
.
_has_changes
(
sequential
.
location
))
self
.
assertFalse
(
self
.
_has_changes
(
vertical
.
location
))
self
.
assertFalse
(
self
.
_has_changes
(
problem
.
location
))
# No orphans in course
course_orphans
=
self
.
store
.
get_orphans
(
course_locator
)
self
.
assertEqual
(
len
(
course_orphans
),
0
)
self
.
store
.
delete_item
(
vertical
.
location
,
self
.
user_id
)
# No orphans in course after delete, except
# in old mongo, which still creates orphans
course_orphans
=
self
.
store
.
get_orphans
(
course_locator
)
if
default_store
==
ModuleStoreEnum
.
Type
.
mongo
:
self
.
assertEqual
(
len
(
course_orphans
),
1
)
else
:
self
.
assertEqual
(
len
(
course_orphans
),
0
)
course_locator_publish
=
course_locator
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
)
# No published oprhans after delete, except
# in old mongo, which still creates orphans
course_publish_orphans
=
self
.
store
.
get_orphans
(
course_locator_publish
)
if
default_store
==
ModuleStoreEnum
.
Type
.
mongo
:
self
.
assertEqual
(
len
(
course_publish_orphans
),
1
)
else
:
self
.
assertEqual
(
len
(
course_publish_orphans
),
0
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_delete_draft_item_orphans
(
self
,
default_store
):
"""
Tests delete draft item create no orphans in course
"""
self
.
initdb
(
default_store
)
course_locator
=
self
.
course
.
id
chapter
=
self
.
store
.
create_child
(
self
.
user_id
,
self
.
course
.
location
,
'chapter'
,
block_id
=
'section_one'
)
sequential
=
self
.
store
.
create_child
(
self
.
user_id
,
chapter
.
location
,
'sequential'
,
block_id
=
'subsection_one'
)
vertical
=
self
.
store
.
create_child
(
self
.
user_id
,
sequential
.
location
,
'vertical'
,
block_id
=
'moon_unit'
)
problem
=
self
.
store
.
create_child
(
self
.
user_id
,
vertical
.
location
,
'problem'
,
block_id
=
'problem'
)
self
.
store
.
publish
(
chapter
.
location
,
self
.
user_id
)
# Verify that there are no changes
self
.
assertFalse
(
self
.
_has_changes
(
chapter
.
location
))
self
.
assertFalse
(
self
.
_has_changes
(
sequential
.
location
))
self
.
assertFalse
(
self
.
_has_changes
(
vertical
.
location
))
self
.
assertFalse
(
self
.
_has_changes
(
problem
.
location
))
# No orphans in course
course_orphans
=
self
.
store
.
get_orphans
(
course_locator
)
self
.
assertEqual
(
len
(
course_orphans
),
0
)
problem
.
display_name
=
'changed'
problem
=
self
.
store
.
update_item
(
problem
,
self
.
user_id
)
self
.
assertTrue
(
self
.
_has_changes
(
vertical
.
location
))
self
.
assertTrue
(
self
.
_has_changes
(
problem
.
location
))
self
.
store
.
delete_item
(
vertical
.
location
,
self
.
user_id
)
# No orphans in course after delete, except
# in old mongo, which still creates them
course_orphans
=
self
.
store
.
get_orphans
(
course_locator
)
if
default_store
==
ModuleStoreEnum
.
Type
.
mongo
:
self
.
assertEqual
(
len
(
course_orphans
),
1
)
else
:
self
.
assertEqual
(
len
(
course_orphans
),
0
)
course_locator_publish
=
course_locator
.
for_branch
(
ModuleStoreEnum
.
BranchName
.
published
)
# No published orphans after delete, except
# in old mongo, which still creates them
course_publish_orphans
=
self
.
store
.
get_orphans
(
course_locator_publish
)
if
default_store
==
ModuleStoreEnum
.
Type
.
mongo
:
self
.
assertEqual
(
len
(
course_publish_orphans
),
1
)
else
:
self
.
assertEqual
(
len
(
course_publish_orphans
),
0
)
@ddt.ddt
@attr
(
'mongo'
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_publish.py
View file @
e65730b9
...
...
@@ -1122,20 +1122,7 @@ class ElementalDeleteItemTests(DraftPublishedOpBaseTestSetup):
self
.
assertOLXIsPublishedOnly
(
block_list_to_delete
)
self
.
delete_item
(
block_list_to_delete
,
revision
=
revision
)
self
.
_check_for_item_deletion
(
block_list_to_delete
,
result
)
# MODULESTORE_DIFFERENCE
if
self
.
is_split_modulestore
:
# Split:
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
# If deleting published_only items, the children that are drafts remain.
self
.
assertOLXIsDraftOnly
(
block_list_children
)
else
:
self
.
assertOLXIsDeleted
(
block_list_children
)
elif
self
.
is_old_mongo_modulestore
:
# Old Mongo:
# If deleting draft_only or both items, the drafts will be deleted.
self
.
assertOLXIsDeleted
(
block_list_children
)
else
:
raise
Exception
(
"Must test either Old Mongo or Split modulestore!"
)
@ddt.data
(
*
itertools
.
product
(
MODULESTORE_SETUPS
,
...
...
@@ -1176,20 +1163,7 @@ class ElementalDeleteItemTests(DraftPublishedOpBaseTestSetup):
self
.
delete_item
(
block_list_to_delete
,
revision
=
revision
)
self
.
_check_for_item_deletion
(
block_list_to_delete
,
result
)
self
.
assertOLXIsDeleted
(
autopublished_children
)
# MODULESTORE_DIFFERENCE
if
self
.
is_split_modulestore
:
# Split:
if
revision
==
ModuleStoreEnum
.
RevisionOption
.
published_only
:
# If deleting published_only items, the children that are drafts remain.
self
.
assertOLXIsDraftOnly
(
block_list_draft_children
)
else
:
self
.
assertOLXIsDeleted
(
block_list_draft_children
)
elif
self
.
is_old_mongo_modulestore
:
# Old Mongo:
# If deleting draft_only or both items, the drafts will be deleted.
self
.
assertOLXIsDeleted
(
block_list_draft_children
)
else
:
raise
Exception
(
"Must test either Old Mongo or Split modulestore!"
)
@ddt.ddt
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_semantics.py
View file @
e65730b9
...
...
@@ -3,6 +3,7 @@ Tests of modulestore semantics: How do the interfaces methods of ModuleStore rel
"""
import
ddt
import
itertools
from
collections
import
namedtuple
from
xmodule.modulestore.tests.utils
import
(
...
...
@@ -51,45 +52,130 @@ class DirectOnlyCategorySemantics(PureModulestoreTestCase):
modulestore
=
self
.
store
)
def
assertBlockDoesntExist
(
self
):
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
store
.
get_item
(
self
.
block_usage_key
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
store
.
get_item
(
self
.
block_usage_key
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
draft_only
)
def
assertBlockDoesntExist
(
self
,
block_usage_key
,
draft
=
None
):
"""
Verify that loading ``block_usage_key`` raises an ItemNotFoundError.
Arguments:
block_usage_key: The xblock to check.
draft (optional): If omitted, verify both published and draft branches.
If True, verify only the draft branch. If False, verify only the
published branch.
"""
if
draft
is
None
or
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
store
.
get_item
(
self
.
block_usage_key
)
self
.
store
.
get_item
(
block_usage_key
)
if
draft
is
None
or
not
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
published_only
):
with
self
.
assertRaises
(
ItemNotFoundError
):
self
.
store
.
get_item
(
self
.
block_usage_key
)
self
.
store
.
get_item
(
block_usage_key
)
def
assertBlockHasContent
(
self
,
test_data
,
content
):
def
assertBlockHasContent
(
self
,
block_usage_key
,
field_name
,
content
,
draft
=
None
):
"""
Assert that the block ``block_usage_key`` has the value ``content`` for ``field_name``
when it is loaded.
Arguments:
block_usage_key: The xblock to check.
field_name (string): The name of the field to check.
content: The value to assert is in the field.
draft (optional): If omitted, verify both published and draft branches.
If True, verify only the draft branch. If False, verify only the
published branch.
"""
if
draft
is
None
or
not
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
published_only
):
target_block
=
self
.
store
.
get_item
(
self
.
block_usage_key
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
block_usage_key
,
)
self
.
assertEquals
(
content
,
target_block
.
fields
[
field_name
]
.
read_from
(
target_block
))
self
.
assertEquals
(
content
,
target_block
.
fields
[
test_data
.
field_name
]
.
read_from
(
target_block
))
if
draft
is
None
or
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
target_block
=
self
.
store
.
get_item
(
self
.
block_usage_key
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
draft_only
block_usage_key
,
)
self
.
assertEquals
(
content
,
target_block
.
fields
[
test_data
.
field_name
]
.
read_from
(
target_block
))
self
.
assertEquals
(
content
,
target_block
.
fields
[
field_name
]
.
read_from
(
target_block
))
def
assertParentOf
(
self
,
parent_usage_key
,
child_usage_key
,
draft
=
None
):
"""
Assert that the block ``parent_usage_key`` has ``child_usage_key`` listed
as one of its ``.children``.
Arguments:
parent_usage_key: The xblock to check as a parent.
child_usage_key: The xblock to check as a child.
draft (optional): If omitted, verify both published and draft branches.
If True, verify only the draft branch. If False, verify only the
published branch.
"""
if
draft
is
None
or
not
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
published_only
):
parent_block
=
self
.
store
.
get_item
(
parent_usage_key
,
)
self
.
assertIn
(
child_usage_key
,
parent_block
.
children
)
if
draft
is
None
or
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
targe
t_block
=
self
.
store
.
get_item
(
self
.
block
_usage_key
,
paren
t_block
=
self
.
store
.
get_item
(
parent
_usage_key
,
)
self
.
assertEquals
(
content
,
target_block
.
fields
[
test_data
.
field_name
]
.
read_from
(
target_block
)
)
self
.
assertIn
(
child_usage_key
,
parent_block
.
children
)
def
assertNotParentOf
(
self
,
parent_usage_key
,
child_usage_key
,
draft
=
None
):
"""
Assert that the block ``parent_usage_key`` does not have ``child_usage_key`` listed
as one of its ``.children``.
Arguments:
parent_usage_key: The xblock to check as a parent.
child_usage_key: The xblock to check as a child.
draft (optional): If omitted, verify both published and draft branches.
If True, verify only the draft branch. If False, verify only the
published branch.
"""
if
draft
is
None
or
not
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
published_only
):
targe
t_block
=
self
.
store
.
get_item
(
self
.
block
_usage_key
,
paren
t_block
=
self
.
store
.
get_item
(
parent
_usage_key
,
)
self
.
assertEquals
(
content
,
target_block
.
fields
[
test_data
.
field_name
]
.
read_from
(
target_block
))
self
.
assertNotIn
(
child_usage_key
,
parent_block
.
children
)
if
draft
is
None
or
draft
:
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
parent_block
=
self
.
store
.
get_item
(
parent_usage_key
,
)
self
.
assertNotIn
(
child_usage_key
,
parent_block
.
children
)
def
assertCoursePointsToBlock
(
self
,
block_usage_key
,
draft
=
None
):
"""
Assert that the context course for the test has ``block_usage_key`` listed
as one of its ``.children``.
Arguments:
block_usage_key: The xblock to check.
draft (optional): If omitted, verify both published and draft branches.
If True, verify only the draft branch. If False, verify only the
published branch.
"""
self
.
assertParentOf
(
self
.
course
.
scope_ids
.
usage_id
,
block_usage_key
,
draft
=
draft
)
def
assertCourseDoesntPointToBlock
(
self
,
block_usage_key
,
draft
=
None
):
"""
Assert that the context course for the test does not have ``block_usage_key`` listed
as one of its ``.children``.
Arguments:
block_usage_key: The xblock to check.
draft (optional): If omitted, verify both published and draft branches.
If True, verify only the draft branch. If False, verify only the
published branch.
"""
self
.
assertNotParentOf
(
self
.
course
.
scope_ids
.
usage_id
,
block_usage_key
,
draft
=
draft
)
def
is_detached
(
self
,
block_type
):
"""
...
...
@@ -109,9 +195,10 @@ class DirectOnlyCategorySemantics(PureModulestoreTestCase):
and then verify that it was created successfully, and is visible in
both published and draft branches.
"""
self
.
block_usage_key
=
self
.
course
.
id
.
make_usage_key
(
block_type
,
'test_block'
)
block_usage_key
=
self
.
course
.
id
.
make_usage_key
(
block_type
,
'test_block'
)
self
.
assertBlockDoesntExist
()
self
.
assertBlockDoesntExist
(
block_usage_key
)
self
.
assertCourseDoesntPointToBlock
(
block_usage_key
)
test_data
=
self
.
DATA_FIELDS
[
block_type
]
...
...
@@ -122,8 +209,8 @@ class DirectOnlyCategorySemantics(PureModulestoreTestCase):
block
=
self
.
store
.
create_xblock
(
self
.
course
.
runtime
,
self
.
course
.
id
,
self
.
block_usage_key
.
block_type
,
block_id
=
self
.
block_usage_key
.
block_id
block_usage_key
.
block_type
,
block_id
=
block_usage_key
.
block_id
)
block
.
fields
[
test_data
.
field_name
]
.
write_to
(
block
,
initial_field_value
)
self
.
store
.
update_item
(
block
,
ModuleStoreEnum
.
UserID
.
test
,
allow_not_found
=
True
)
...
...
@@ -132,18 +219,24 @@ class DirectOnlyCategorySemantics(PureModulestoreTestCase):
user_id
=
ModuleStoreEnum
.
UserID
.
test
,
parent_usage_key
=
self
.
course
.
scope_ids
.
usage_id
,
block_type
=
block_type
,
block_id
=
self
.
block_usage_key
.
block_id
,
block_id
=
block_usage_key
.
block_id
,
fields
=
{
test_data
.
field_name
:
initial_field_value
},
)
self
.
assertBlockHasContent
(
test_data
,
initial_field_value
)
if
self
.
is_detached
(
block_type
):
self
.
assertCourseDoesntPointToBlock
(
block_usage_key
)
else
:
self
.
assertCoursePointsToBlock
(
block_usage_key
)
self
.
assertBlockHasContent
(
block_usage_key
,
test_data
.
field_name
,
initial_field_value
)
return
block_usage_key
@ddt.data
(
*
TESTABLE_BLOCK_TYPES
)
def
test_update
(
self
,
block_type
):
self
.
_do_create
(
block_type
)
block_usage_key
=
self
.
_do_create
(
block_type
)
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
block
=
self
.
store
.
get_item
(
self
.
block_usage_key
)
block
=
self
.
store
.
get_item
(
block_usage_key
)
test_data
=
self
.
DATA_FIELDS
[
block_type
]
...
...
@@ -154,16 +247,81 @@ class DirectOnlyCategorySemantics(PureModulestoreTestCase):
self
.
store
.
update_item
(
block
,
ModuleStoreEnum
.
UserID
.
test
,
allow_not_found
=
True
)
self
.
assertBlockHasContent
(
test_data
,
updated_field_value
)
self
.
assertBlockHasContent
(
block_usage_key
,
test_data
.
field_name
,
updated_field_value
)
@ddt.data
(
*
TESTABLE_BLOCK_TYPES
)
def
test_delete
(
self
,
block_type
):
self
.
_do_create
(
block_type
)
block_usage_key
=
self
.
_do_create
(
block_type
)
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
self
.
store
.
delete_item
(
block_usage_key
,
ModuleStoreEnum
.
UserID
.
test
)
self
.
assertCourseDoesntPointToBlock
(
block_usage_key
)
self
.
assertBlockDoesntExist
(
block_usage_key
)
@ddt.data
(
*
itertools
.
product
([
'chapter'
,
'sequential'
],
[
True
,
False
]))
@ddt.unpack
def
test_delete_child
(
self
,
block_type
,
child_published
):
block_usage_key
=
self
.
course
.
id
.
make_usage_key
(
block_type
,
'test_block'
)
child_usage_key
=
self
.
course
.
id
.
make_usage_key
(
'html'
,
'test_child'
)
self
.
assertCourseDoesntPointToBlock
(
block_usage_key
)
self
.
assertBlockDoesntExist
(
block_usage_key
)
self
.
assertBlockDoesntExist
(
child_usage_key
)
test_data
=
self
.
DATA_FIELDS
[
block_type
]
child_data
=
'<div>child value</div>'
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
self
.
store
.
create_child
(
user_id
=
ModuleStoreEnum
.
UserID
.
test
,
parent_usage_key
=
self
.
course
.
scope_ids
.
usage_id
,
block_type
=
block_type
,
block_id
=
block_usage_key
.
block_id
,
fields
=
{
test_data
.
field_name
:
test_data
.
initial
},
)
self
.
store
.
create_child
(
user_id
=
ModuleStoreEnum
.
UserID
.
test
,
parent_usage_key
=
block_usage_key
,
block_type
=
child_usage_key
.
block_type
,
block_id
=
child_usage_key
.
block_id
,
fields
=
{
'data'
:
child_data
},
)
if
child_published
:
self
.
store
.
publish
(
child_usage_key
,
ModuleStoreEnum
.
UserID
.
test
)
self
.
assertCoursePointsToBlock
(
block_usage_key
)
if
child_published
:
self
.
assertParentOf
(
block_usage_key
,
child_usage_key
)
else
:
self
.
assertParentOf
(
block_usage_key
,
child_usage_key
,
draft
=
True
)
# N.B. whether the direct-only parent block points to the child in the publish branch
# is left as undefined behavior
self
.
assertBlockHasContent
(
block_usage_key
,
test_data
.
field_name
,
test_data
.
initial
)
if
child_published
:
self
.
assertBlockHasContent
(
child_usage_key
,
'data'
,
child_data
)
else
:
self
.
assertBlockHasContent
(
child_usage_key
,
'data'
,
child_data
,
draft
=
True
)
self
.
assertBlockDoesntExist
(
child_usage_key
,
draft
=
False
)
with
self
.
store
.
branch_setting
(
ModuleStoreEnum
.
Branch
.
draft_preferred
):
self
.
store
.
delete_item
(
self
.
block_usage_key
,
ModuleStoreEnum
.
UserID
.
test
)
self
.
store
.
delete_item
(
child_usage_key
,
ModuleStoreEnum
.
UserID
.
test
)
self
.
assertCoursePointsToBlock
(
block_usage_key
)
self
.
assertNotParentOf
(
block_usage_key
,
child_usage_key
)
self
.
assertBlockDoesntExist
()
if
child_published
and
self
.
store
.
get_modulestore_type
(
self
.
course
.
id
)
==
ModuleStoreEnum
.
Type
.
mongo
:
# N.B. This block is being left as an orphan in old-mongo. This test will
# fail when that is fixed. At that time, this condition should just be removed,
# as SplitMongo and OldMongo will have the same semantics.
self
.
assertBlockHasContent
(
child_usage_key
,
'data'
,
child_data
)
else
:
self
.
assertBlockDoesntExist
(
child_usage_key
)
class
TestSplitDirectOnlyCategorySemantics
(
DirectOnlyCategorySemantics
):
...
...
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