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
920dfb52
Commit
920dfb52
authored
Jan 13, 2015
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MA-216 Mobile API: display unreleased courses in enrollment list.
parent
c11a9f05
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
92 additions
and
16 deletions
+92
-16
lms/djangoapps/mobile_api/tests.py
+46
-7
lms/djangoapps/mobile_api/testutils.py
+7
-2
lms/djangoapps/mobile_api/users/tests.py
+1
-0
lms/djangoapps/mobile_api/users/views.py
+3
-2
lms/djangoapps/mobile_api/utils.py
+35
-5
No files found.
lms/djangoapps/mobile_api/tests.py
View file @
920dfb52
...
...
@@ -8,12 +8,12 @@ from django.test import TestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
.utils
import
mobile_
access_when_enrolled
,
mobile_course_access
,
mobile_view
from
.utils
import
mobile_
course_listing_access
,
mobile_course_access
,
mobile_view
,
dict_value
from
.testutils
import
MobileAPITestCase
,
ROLE_CASES
@ddt.ddt
class
TestMobile
AccessWhenEnrolled
(
MobileAPITestCase
):
class
TestMobile
CourseListingAccess
(
MobileAPITestCase
):
"""
Tests for mobile_access_when_enrolled utility function.
"""
...
...
@@ -26,26 +26,26 @@ class TestMobileAccessWhenEnrolled(MobileAPITestCase):
non_mobile_course
=
CourseFactory
.
create
(
mobile_available
=
False
)
if
role
:
role
(
non_mobile_course
.
id
)
.
add_users
(
self
.
user
)
self
.
assertEqual
(
should_have_access
,
mobile_
access_when_enrolled
(
non_mobile_course
,
self
.
user
))
self
.
assertEqual
(
should_have_access
,
mobile_
course_listing_access
(
non_mobile_course
,
self
.
user
))
def
test_mobile_explicit_access
(
self
):
"""
Verifies that our mobile access function listens to the mobile_available flag as it should
"""
self
.
assertTrue
(
mobile_
access_when_enrolled
(
self
.
course
,
self
.
user
))
self
.
assertTrue
(
mobile_
course_listing_access
(
self
.
course
,
self
.
user
))
def
test_missing_course
(
self
):
"""
Verifies that we handle the case where a course doesn't exist
"""
self
.
assertFalse
(
mobile_
access_when_enrolled
(
None
,
self
.
user
))
self
.
assertFalse
(
mobile_
course_listing_access
(
None
,
self
.
user
))
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'DISABLE_START_DATES'
:
False
})
def
test_unreleased_course
(
self
):
"""
Verifies that we
handle
the case where a course hasn't started
Verifies that we
allow
the case where a course hasn't started
"""
self
.
assert
False
(
mobile_access_when_enrolled
(
self
.
course
,
self
.
user
))
self
.
assert
True
(
mobile_course_listing_access
(
self
.
course
,
self
.
user
))
@ddt.ddt
...
...
@@ -65,3 +65,42 @@ class TestMobileAPIDecorators(TestCase):
self
.
assertIn
(
"Test docstring of decorated function."
,
decorated_func
.
__doc__
)
self
.
assertEquals
(
decorated_func
.
__name__
,
"decorated_func"
)
self
.
assertTrue
(
decorated_func
.
__module__
.
endswith
(
"tests"
))
@ddt.ddt
class
TestDictContextManager
(
TestCase
):
"""
Tests for dict contextmanager.
"""
def
setUp
(
self
):
super
(
TestDictContextManager
,
self
)
.
setUp
()
self
.
test_dict
=
{}
self
.
test_key
=
'test key'
def
call_context_manager
(
self
,
raise_exception
):
"""Helper method that calls the context manager."""
new_value
=
"new value"
try
:
with
dict_value
(
self
.
test_dict
,
self
.
test_key
,
new_value
):
# verify test_key is assigned to new_value within the context.
self
.
assertEquals
(
self
.
test_dict
[
self
.
test_key
],
new_value
)
if
raise_exception
:
raise
StandardError
except
StandardError
:
pass
@ddt.data
(
True
,
False
)
def
test_no_previous_value
(
self
,
raise_exception
):
self
.
call_context_manager
(
raise_exception
)
# verify test_key no longer exists in the dict.
self
.
assertNotIn
(
self
.
test_key
,
self
.
test_dict
)
@ddt.data
(
True
,
False
)
def
test_has_previous_value
(
self
,
raise_exception
):
old_value
=
"old value"
self
.
test_dict
[
self
.
test_key
]
=
old_value
self
.
call_context_manager
(
raise_exception
)
# verify test_key's value is reverted back to old_value.
self
.
assertEquals
(
self
.
test_dict
[
self
.
test_key
],
old_value
)
lms/djangoapps/mobile_api/testutils.py
View file @
920dfb52
...
...
@@ -10,7 +10,7 @@ Test utilities for mobile API tests:
MobileCourseAccessTestMixin - tests for APIs with mobile_course_access and verify_enrolled=False.
MobileEnrolledCourseAccessTestMixin - tests for APIs with mobile_course_access and verify_enrolled=True.
"""
# pylint: disable=no-member
# pylint: disable=no-member
, invalid-name
import
ddt
from
mock
import
patch
from
rest_framework.test
import
APITestCase
...
...
@@ -140,6 +140,8 @@ class MobileCourseAccessTestMixin(object):
Subclasses are expected to inherit from MobileAPITestCase.
Subclasses can override verify_success, verify_failure, and init_course_access methods.
"""
ALLOW_ACCESS_TO_UNRELEASED_COURSE
=
False
def
verify_success
(
self
,
response
):
"""Base implementation of verifying a successful response."""
self
.
assertEqual
(
response
.
status_code
,
200
)
...
...
@@ -170,7 +172,10 @@ class MobileCourseAccessTestMixin(object):
self
.
init_course_access
()
response
=
self
.
api_response
(
expected_response_code
=
None
)
self
.
verify_failure
(
response
)
# allow subclasses to override verification
if
self
.
ALLOW_ACCESS_TO_UNRELEASED_COURSE
:
self
.
verify_success
(
response
)
else
:
self
.
verify_failure
(
response
)
@ddt.data
(
*
ROLE_CASES
)
@ddt.unpack
...
...
lms/djangoapps/mobile_api/users/tests.py
View file @
920dfb52
...
...
@@ -48,6 +48,7 @@ class TestUserEnrollmentApi(MobileAPITestCase, MobileAuthUserTestMixin, MobileEn
Tests for /api/mobile/v0.5/users/<user_name>/course_enrollments/
"""
REVERSE_INFO
=
{
'name'
:
'courseenrollment-detail'
,
'params'
:
[
'username'
]}
ALLOW_ACCESS_TO_UNRELEASED_COURSE
=
True
def
verify_success
(
self
,
response
):
super
(
TestUserEnrollmentApi
,
self
)
.
verify_success
(
response
)
...
...
lms/djangoapps/mobile_api/users/views.py
View file @
920dfb52
...
...
@@ -26,7 +26,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from
.serializers
import
CourseEnrollmentSerializer
,
UserSerializer
from
mobile_api
import
errors
from
mobile_api.utils
import
mobile_
access_when_enrolled
,
mobile_view
,
mobile_course_access
from
mobile_api.utils
import
mobile_
course_listing_access
,
mobile_view
,
mobile_course_access
@mobile_view
(
is_user
=
True
)
...
...
@@ -46,6 +46,7 @@ class UserDetail(generics.RetrieveAPIView):
GET /api/mobile/v0.5/users/{username}
**Response Values**
* id: The ID of the user.
...
...
@@ -244,7 +245,7 @@ class UserCourseEnrollmentsList(generics.ListAPIView):
enrollments
=
self
.
queryset
.
filter
(
user__username
=
self
.
kwargs
[
'username'
],
is_active
=
True
)
.
order_by
(
'created'
)
return
[
enrollment
for
enrollment
in
enrollments
if
mobile_
access_when_enrolled
(
enrollment
.
course
,
self
.
request
.
user
)
if
mobile_
course_listing_access
(
enrollment
.
course
,
self
.
request
.
user
)
]
...
...
lms/djangoapps/mobile_api/utils.py
View file @
920dfb52
...
...
@@ -4,7 +4,9 @@ Common utility methods and decorators for Mobile APIs.
import
functools
from
contextlib
import
contextmanager
from
django.http
import
Http404
from
django.conf
import
settings
from
opaque_keys.edx.keys
import
CourseKey
from
courseware.courses
import
get_course_with_access
...
...
@@ -12,6 +14,30 @@ from rest_framework import permissions
from
rest_framework.authentication
import
OAuth2Authentication
,
SessionAuthentication
# TODO This contextmanager should be moved to a common utility library.
@contextmanager
def
dict_value
(
dictionary
,
key
,
value
):
"""
A context manager that assigns 'value' to the 'key' in the 'dictionary' when entering the context,
and then resets the key upon exiting the context.
"""
# cache previous values
has_previous_value
=
key
in
dictionary
previous_value
=
dictionary
[
key
]
if
has_previous_value
else
None
try
:
# temporarily set to new value
dictionary
[
key
]
=
value
yield
finally
:
# reset to previous values
if
has_previous_value
:
dictionary
[
key
]
=
previous_value
else
:
dictionary
.
pop
(
key
,
None
)
def
mobile_course_access
(
depth
=
0
,
verify_enrolled
=
True
):
"""
Method decorator for a mobile API endpoint that verifies the user has access to the course in a mobile context.
...
...
@@ -37,18 +63,22 @@ def mobile_course_access(depth=0, verify_enrolled=True):
return
_decorator
def
mobile_
access_when_enrolled
(
course
,
user
):
def
mobile_
course_listing_access
(
course
,
user
):
"""
Determines whether a user has access to a course in a mobile context.
Checks the mobile_available flag and the start_date.
Note: Does not check if the user is actually enrolled in the course.
Determines whether a user has access to a course' listing in a mobile context.
Checks the mobile_available flag.
Checks roles including Beta Tester and staff roles.
Note:
Does not check if the user is actually enrolled in the course.
Does not check the start_date.
"""
# The course doesn't always really exist -- we can have bad data in the enrollments
# pointing to non-existent (or removed) courses, in which case `course` is None.
if
not
course
:
return
False
try
:
return
get_course_with_access
(
user
,
'load_mobile_no_enrollment_check'
,
course
.
id
)
is
not
None
with
dict_value
(
settings
.
FEATURES
,
'DISABLE_START_DATES'
,
True
):
return
get_course_with_access
(
user
,
'load_mobile_no_enrollment_check'
,
course
.
id
)
is
not
None
except
Http404
:
return
False
...
...
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