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
cc1c7d50
Commit
cc1c7d50
authored
May 05, 2015
by
jsa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix ecommerce api calls in verify_student and update tests.
XCOM-287
parent
4bd15cf2
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
217 additions
and
126 deletions
+217
-126
lms/djangoapps/commerce/tests/__init__.py
+14
-83
lms/djangoapps/commerce/tests/mocks.py
+95
-0
lms/djangoapps/commerce/tests/test_views.py
+20
-28
lms/djangoapps/verify_student/tests/test_views.py
+81
-12
lms/djangoapps/verify_student/views.py
+7
-3
No files found.
lms/djangoapps/commerce/tests/__init__.py
View file @
cc1c7d50
...
...
@@ -3,7 +3,6 @@ import json
from
django.test
import
TestCase
from
django.test.utils
import
override_settings
from
ecommerce_api_client.client
import
EcommerceApiClient
import
httpretty
import
jwt
import
mock
...
...
@@ -12,15 +11,24 @@ from commerce import ecommerce_api_client
from
student.tests.factories
import
UserFactory
TEST_API_URL
=
'http://example.com/api'
TEST_API_SIGNING_KEY
=
'edx'
TEST_BASKET_ID
=
7
TEST_ORDER_NUMBER
=
'100004'
TEST_PAYMENT_DATA
=
{
'payment_processor_name'
:
'test-processor'
,
'payment_form_data'
:
{},
'payment_page_url'
:
'http://example.com/pay'
,
}
class
EcommerceApiClientTest
(
TestCase
):
""" Tests to ensure the client is initialized properly. """
TEST_SIGNING_KEY
=
'edx'
TEST_API_URL
=
'http://example.com/api'
TEST_USER_EMAIL
=
'test@example.com'
TEST_CLIENT_ID
=
'test-client-id'
@override_settings
(
ECOMMERCE_API_SIGNING_KEY
=
TEST_SIGNING_KEY
,
ECOMMERCE_API_URL
=
TEST_API_URL
)
@override_settings
(
ECOMMERCE_API_SIGNING_KEY
=
TEST_
API_
SIGNING_KEY
,
ECOMMERCE_API_URL
=
TEST_API_URL
)
@httpretty.activate
def
test_tracking_context
(
self
):
""" Ensure the tracking context is set up in the api client correctly
...
...
@@ -32,7 +40,7 @@ class EcommerceApiClientTest(TestCase):
# fake an ecommerce api request.
httpretty
.
register_uri
(
httpretty
.
POST
,
'{}/baskets/1/'
.
format
(
self
.
TEST_API_URL
),
'{}/baskets/1/'
.
format
(
TEST_API_URL
),
status
=
200
,
body
=
'{}'
,
adding_headers
=
{
'Content-Type'
:
'application/json'
}
)
...
...
@@ -51,82 +59,5 @@ class EcommerceApiClientTest(TestCase):
'lms_client_id'
:
self
.
TEST_CLIENT_ID
,
},
}
expected_header
=
'JWT {}'
.
format
(
jwt
.
encode
(
expected_payload
,
self
.
TEST
_SIGNING_KEY
))
expected_header
=
'JWT {}'
.
format
(
jwt
.
encode
(
expected_payload
,
TEST_API
_SIGNING_KEY
))
self
.
assertEqual
(
actual_header
,
expected_header
)
class
EcommerceApiTestMixin
(
object
):
""" Mixin for tests utilizing the E-Commerce API. """
ECOMMERCE_API_URL
=
'http://example.com/api'
ECOMMERCE_API_SIGNING_KEY
=
'edx'
BASKET_ID
=
7
ORDER_NUMBER
=
'100004'
PROCESSOR
=
'test-processor'
PAYMENT_DATA
=
{
'payment_processor_name'
:
PROCESSOR
,
'payment_form_data'
:
{},
'payment_page_url'
:
'http://example.com/pay'
,
}
ORDER_DATA
=
{
'number'
:
ORDER_NUMBER
}
ECOMMERCE_API_SUCCESSFUL_BODY
=
{
'id'
:
BASKET_ID
,
'order'
:
{
'number'
:
ORDER_NUMBER
},
# never both None.
'payment_data'
:
PAYMENT_DATA
,
}
ECOMMERCE_API_SUCCESSFUL_BODY_JSON
=
json
.
dumps
(
ECOMMERCE_API_SUCCESSFUL_BODY
)
# pylint: disable=invalid-name
def
_mock_ecommerce_api
(
self
,
status
=
200
,
body
=
None
,
is_payment_required
=
False
):
"""
Mock calls to the E-Commerce API.
The calling test should be decorated with @httpretty.activate.
"""
self
.
assertTrue
(
httpretty
.
is_enabled
(),
'Test is missing @httpretty.activate decorator.'
)
url
=
self
.
ECOMMERCE_API_URL
+
'/baskets/'
if
body
is
None
:
response_data
=
{
'id'
:
self
.
BASKET_ID
,
'payment_data'
:
None
,
'order'
:
None
}
if
is_payment_required
:
response_data
[
'payment_data'
]
=
self
.
PAYMENT_DATA
else
:
response_data
[
'order'
]
=
{
'number'
:
self
.
ORDER_NUMBER
}
body
=
json
.
dumps
(
response_data
)
httpretty
.
register_uri
(
httpretty
.
POST
,
url
,
status
=
status
,
body
=
body
,
adding_headers
=
{
'Content-Type'
:
'application/json'
})
class
mock_create_basket
(
object
):
# pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket creation method. """
patch
=
None
def
__init__
(
self
,
**
kwargs
):
default_kwargs
=
{
'return_value'
:
EcommerceApiTestMixin
.
ECOMMERCE_API_SUCCESSFUL_BODY
}
default_kwargs
.
update
(
kwargs
)
_mock
=
mock
.
Mock
()
_mock
.
post
=
mock
.
Mock
(
**
default_kwargs
)
EcommerceApiClient
.
baskets
=
_mock
self
.
patch
=
_mock
def
__enter__
(
self
):
return
self
.
patch
def
__exit__
(
self
,
exc_type
,
exc_val
,
exc_tb
):
# pylint: disable=unused-argument
pass
class
mock_basket_order
(
object
):
# pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket order method. """
patch
=
None
def
__init__
(
self
,
**
kwargs
):
_mock
=
mock
.
Mock
()
_mock
.
order
.
get
=
mock
.
Mock
(
**
kwargs
)
EcommerceApiClient
.
baskets
=
lambda
client
,
basket_id
:
_mock
self
.
patch
=
_mock
def
__enter__
(
self
):
return
self
.
patch
def
__exit__
(
self
,
exc_type
,
exc_val
,
exc_tb
):
# pylint: disable=unused-argument
pass
lms/djangoapps/commerce/tests/mocks.py
0 → 100644
View file @
cc1c7d50
""" Commerce app tests package. """
import
json
import
httpretty
from
commerce.tests
import
TEST_API_URL
class
mock_ecommerce_api_endpoint
(
object
):
# pylint: disable=invalid-name
"""
Base class for contextmanagers used to mock calls to api endpoints.
The contextmanager internally activates and deactivates httpretty as
required, therefore it is not advised to use this mock endpoint in
test cases where httpretty is being used directly.
"""
# override this in subclasses.
default_response
=
None
# override this in subclasses, using one of httpretty's method constants
method
=
None
def
__init__
(
self
,
response
=
None
,
status
=
200
,
expect_called
=
True
,
exception
=
None
):
"""
Keyword Arguments:
response: a JSON-serializable Python type representing the desired response body.
status: desired HTTP status for the response.
expect_called: a boolean indicating whether an API request was expected; set
to False if we should ensure that no request arrived.
exception: raise this exception instead of returning an HTTP response when called.
"""
self
.
response
=
response
or
self
.
default_response
self
.
status
=
status
self
.
expect_called
=
expect_called
self
.
exception
=
exception
def
get_uri
(
self
):
"""
Return the uri to register with httpretty for this contextmanager.
Subclasses must override this method.
"""
raise
NotImplementedError
def
_exception_body
(
self
,
request
,
uri
,
headers
):
# pylint: disable=unused-argument
"""Helper used to create callbacks in order to have httpretty raise Exceptions."""
raise
self
.
exception
# pylint: disable=raising-bad-type
def
__enter__
(
self
):
httpretty
.
reset
()
httpretty
.
enable
()
httpretty
.
register_uri
(
self
.
method
,
self
.
get_uri
(),
status
=
self
.
status
,
body
=
self
.
_exception_body
if
self
.
exception
is
not
None
else
json
.
dumps
(
self
.
response
),
adding_headers
=
{
'Content-Type'
:
'application/json'
},
)
def
__exit__
(
self
,
exc_type
,
exc_val
,
exc_tb
):
# pylint: disable=unused-argument
assert
self
.
expect_called
==
(
httpretty
.
last_request
()
.
headers
!=
{})
httpretty
.
disable
()
class
mock_create_basket
(
mock_ecommerce_api_endpoint
):
# pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket creation method. """
default_response
=
{
'id'
:
7
,
'order'
:
{
'number'
:
'100004'
},
# never both None.
'payment_data'
:
{
'payment_processor_name'
:
'test-processor'
,
'payment_form_data'
:
{},
'payment_page_url'
:
'http://example.com/pay'
,
},
}
method
=
httpretty
.
POST
def
get_uri
(
self
):
return
TEST_API_URL
+
'/baskets/'
class
mock_basket_order
(
mock_ecommerce_api_endpoint
):
# pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket order method. """
default_response
=
{
'number'
:
1
}
method
=
httpretty
.
GET
def
__init__
(
self
,
basket_id
,
**
kwargs
):
super
(
mock_basket_order
,
self
)
.
__init__
(
**
kwargs
)
self
.
basket_id
=
basket_id
def
get_uri
(
self
):
return
TEST_API_URL
+
'/baskets/{}/order/'
.
format
(
self
.
basket_id
)
lms/djangoapps/commerce/tests/test_views.py
View file @
cc1c7d50
...
...
@@ -12,7 +12,8 @@ from xmodule.modulestore.tests.factories import CourseFactory
from
ecommerce_api_client
import
exceptions
from
commerce.constants
import
Messages
from
commerce.tests
import
EcommerceApiTestMixin
from
commerce.tests
import
TEST_BASKET_ID
,
TEST_ORDER_NUMBER
,
TEST_PAYMENT_DATA
,
TEST_API_URL
,
TEST_API_SIGNING_KEY
from
commerce.tests.mocks
import
mock_basket_order
,
mock_create_basket
from
course_modes.models
import
CourseMode
from
enrollment.api
import
get_enrollment
from
student.models
import
CourseEnrollment
...
...
@@ -33,9 +34,8 @@ class UserMixin(object):
@ddt
@override_settings
(
ECOMMERCE_API_URL
=
EcommerceApiTestMixin
.
ECOMMERCE_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
EcommerceApiTestMixin
.
ECOMMERCE_API_SIGNING_KEY
)
class
BasketsViewTests
(
EnrollmentEventTestMixin
,
EcommerceApiTestMixin
,
UserMixin
,
ModuleStoreTestCase
):
@override_settings
(
ECOMMERCE_API_URL
=
TEST_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
TEST_API_SIGNING_KEY
)
class
BasketsViewTests
(
EnrollmentEventTestMixin
,
UserMixin
,
ModuleStoreTestCase
):
"""
Tests for the commerce orders view.
"""
...
...
@@ -60,7 +60,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
def
assertResponsePaymentData
(
self
,
response
):
""" Asserts correctness of a JSON body containing payment information. """
actual_response
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
actual_response
,
self
.
PAYMENT_DATA
)
self
.
assertEqual
(
actual_response
,
TEST_
PAYMENT_DATA
)
def
assertValidEcommerceInternalRequestErrorResponse
(
self
,
response
):
""" Asserts the response is a valid response sent when the E-Commerce API is unavailable. """
...
...
@@ -126,7 +126,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
"""
If the call to the E-Commerce API times out, the view should log an error and return an HTTP 503 status.
"""
with
self
.
mock_create_basket
(
side_effect
=
exceptions
.
Timeout
):
with
mock_create_basket
(
exception
=
exceptions
.
Timeout
):
response
=
self
.
_post_to_view
()
self
.
assertValidEcommerceInternalRequestErrorResponse
(
response
)
...
...
@@ -136,7 +136,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
"""
If the E-Commerce API raises an error, the view should return an HTTP 503 status.
"""
with
self
.
mock_create_basket
(
side_effect
=
exceptions
.
SlumberBaseException
):
with
mock_create_basket
(
exception
=
exceptions
.
SlumberBaseException
):
response
=
self
.
_post_to_view
()
self
.
assertValidEcommerceInternalRequestErrorResponse
(
response
)
...
...
@@ -150,7 +150,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
# Validate the response content
if
is_completed
:
msg
=
Messages
.
ORDER_COMPLETED
.
format
(
order_number
=
self
.
ORDER_NUMBER
)
msg
=
Messages
.
ORDER_COMPLETED
.
format
(
order_number
=
TEST_
ORDER_NUMBER
)
self
.
assertResponseMessage
(
response
,
msg
)
else
:
self
.
assertResponsePaymentData
(
response
)
...
...
@@ -166,8 +166,8 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
self
.
user
.
is_active
=
user_is_active
self
.
user
.
save
()
# pylint: disable=no-member
return_value
=
{
'id'
:
self
.
BASKET_ID
,
'payment_data'
:
None
,
'order'
:
{
'number'
:
self
.
ORDER_NUMBER
}}
with
self
.
mock_create_basket
(
return_valu
e
=
return_value
):
return_value
=
{
'id'
:
TEST_BASKET_ID
,
'payment_data'
:
None
,
'order'
:
{
'number'
:
TEST_
ORDER_NUMBER
}}
with
mock_create_basket
(
respons
e
=
return_value
):
self
.
_test_successful_ecommerce_api_call
()
@data
(
True
,
False
)
...
...
@@ -180,8 +180,8 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
self
.
user
.
is_active
=
user_is_active
self
.
user
.
save
()
# pylint: disable=no-member
return_value
=
{
'id'
:
self
.
BASKET_ID
,
'payment_data'
:
self
.
PAYMENT_DATA
,
'order'
:
None
}
with
self
.
mock_create_basket
(
return_valu
e
=
return_value
):
return_value
=
{
'id'
:
TEST_BASKET_ID
,
'payment_data'
:
TEST_
PAYMENT_DATA
,
'order'
:
None
}
with
mock_create_basket
(
respons
e
=
return_value
):
self
.
_test_successful_ecommerce_api_call
(
False
)
def
_test_course_without_sku
(
self
):
...
...
@@ -189,7 +189,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
Validates the view bypasses the E-Commerce API when the course has no CourseModes with SKUs.
"""
# Place an order
with
self
.
mock_create_basket
()
as
api_mock
:
with
mock_create_basket
(
expect_called
=
False
)
:
response
=
self
.
_post_to_view
()
# Validate the response content
...
...
@@ -198,9 +198,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
username
=
self
.
user
.
username
)
self
.
assertResponseMessage
(
response
,
msg
)
# No calls made to the E-Commerce API
self
.
assertFalse
(
api_mock
.
called
)
def
test_course_without_sku
(
self
):
"""
If the course does NOT have a SKU, the user should be enrolled in the course (under the honor mode) and
...
...
@@ -218,7 +215,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
"""
If the E-Commerce Service is not configured, the view should enroll the user.
"""
with
self
.
mock_create_basket
()
as
api_mock
:
with
mock_create_basket
(
expect_called
=
False
)
:
response
=
self
.
_post_to_view
()
# Validate the response
...
...
@@ -228,7 +225,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
# Ensure that the user is not enrolled and that no calls were made to the E-Commerce API
self
.
assertTrue
(
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course
.
id
))
self
.
assertFalse
(
api_mock
.
called
)
def
assertProfessionalModeBypassed
(
self
):
""" Verifies that the view returns HTTP 406 when a course with no honor mode is encountered. """
...
...
@@ -238,7 +234,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
CourseModeFactory
.
create
(
course_id
=
self
.
course
.
id
,
mode_slug
=
mode
,
mode_display_name
=
mode
,
sku
=
uuid4
()
.
hex
.
decode
(
'ascii'
))
with
self
.
mock_create_basket
()
as
api_mock
:
with
mock_create_basket
(
expect_called
=
False
)
:
response
=
self
.
_post_to_view
()
# The view should return an error status code
...
...
@@ -246,9 +242,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
msg
=
Messages
.
NO_HONOR_MODE
.
format
(
course_id
=
self
.
course
.
id
)
self
.
assertResponseMessage
(
response
,
msg
)
# No calls should be made to the E-Commerce API.
self
.
assertFalse
(
api_mock
.
called
)
def
test_course_with_professional_mode_only
(
self
):
""" Verifies that the view behaves appropriately when the course only has a professional mode. """
self
.
assertProfessionalModeBypassed
()
...
...
@@ -293,7 +286,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
self
.
assertFalse
(
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course
.
id
))
self
.
assertIsNotNone
(
get_enrollment
(
self
.
user
.
username
,
unicode
(
self
.
course
.
id
)))
with
self
.
mock_create_basket
():
with
mock_create_basket
():
self
.
_test_successful_ecommerce_api_call
(
False
)
...
...
@@ -310,9 +303,8 @@ class OrdersViewTests(BasketsViewTests):
self
.
url
=
reverse
(
'commerce:orders'
)
@override_settings
(
ECOMMERCE_API_URL
=
EcommerceApiTestMixin
.
ECOMMERCE_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
EcommerceApiTestMixin
.
ECOMMERCE_API_SIGNING_KEY
)
class
BasketOrderViewTests
(
UserMixin
,
EcommerceApiTestMixin
,
TestCase
):
@override_settings
(
ECOMMERCE_API_URL
=
TEST_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
TEST_API_SIGNING_KEY
)
class
BasketOrderViewTests
(
UserMixin
,
TestCase
):
""" Tests for the basket order view. """
view_name
=
'commerce:basket_order'
MOCK_ORDER
=
{
'number'
:
1
}
...
...
@@ -325,7 +317,7 @@ class BasketOrderViewTests(UserMixin, EcommerceApiTestMixin, TestCase):
def
test_order_found
(
self
):
""" If the order is located, the view should pass the data from the API. """
with
self
.
mock_basket_order
(
return_valu
e
=
self
.
MOCK_ORDER
):
with
mock_basket_order
(
basket_id
=
1
,
respons
e
=
self
.
MOCK_ORDER
):
response
=
self
.
client
.
get
(
self
.
path
)
self
.
assertEqual
(
response
.
status_code
,
200
)
...
...
@@ -334,7 +326,7 @@ class BasketOrderViewTests(UserMixin, EcommerceApiTestMixin, TestCase):
def
test_order_not_found
(
self
):
""" If the order is not found, the view should return a 404. """
with
self
.
mock_basket_order
(
side_effect
=
exceptions
.
HttpNotFoundError
):
with
mock_basket_order
(
basket_id
=
1
,
exception
=
exceptions
.
HttpNotFoundError
):
response
=
self
.
client
.
get
(
self
.
path
)
self
.
assertEqual
(
response
.
status_code
,
404
)
...
...
lms/djangoapps/verify_student/tests/test_views.py
View file @
cc1c7d50
...
...
@@ -18,6 +18,7 @@ from django.conf import settings
from
django.core.urlresolvers
import
reverse
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.core
import
mail
import
httpretty
from
bs4
import
BeautifulSoup
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
...
...
@@ -27,7 +28,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from
opaque_keys.edx.locator
import
CourseLocator
from
openedx.core.djangoapps.user_api.accounts.api
import
get_account_settings
from
commerce.tests
import
EcommerceApiTestMixin
from
commerce.tests
import
TEST_PAYMENT_DATA
,
TEST_API_URL
,
TEST_API_SIGNING_KEY
from
student.tests.factories
import
UserFactory
,
CourseEnrollmentFactory
from
student.models
import
CourseEnrollment
from
course_modes.tests.factories
import
CourseModeFactory
...
...
@@ -36,8 +37,11 @@ from shoppingcart.models import Order, CertificateItem
from
embargo.test_utils
import
restrict_course
from
util.testing
import
UrlResetMixin
from
verify_student.views
import
(
render_to_response
,
PayAndVerifyView
,
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
checkout_with_ecommerce_service
,
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW
,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
,
PayAndVerifyView
,
render_to_response
,
)
from
verify_student.models
import
(
SoftwareSecurePhotoVerification
,
VerificationCheckpoint
,
...
...
@@ -650,13 +654,18 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
course
.
start
=
kwargs
.
get
(
'course_start'
)
modulestore
()
.
update_item
(
course
,
ModuleStoreEnum
.
UserID
.
test
)
mode_kwargs
=
{}
if
kwargs
.
get
(
'sku'
):
mode_kwargs
[
'sku'
]
=
kwargs
[
'sku'
]
for
course_mode
in
course_modes
:
min_price
=
(
0
if
course_mode
in
[
"honor"
,
"audit"
]
else
self
.
MIN_PRICE
)
CourseModeFactory
(
course_id
=
course
.
id
,
mode_slug
=
course_mode
,
mode_display_name
=
course_mode
,
min_price
=
min_price
min_price
=
min_price
,
**
mode_kwargs
)
return
course
...
...
@@ -819,6 +828,35 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
self
.
assertEqual
(
response_dict
[
'course_name'
],
mode_display_name
)
@httpretty.activate
@override_settings
(
ECOMMERCE_API_URL
=
TEST_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
TEST_API_SIGNING_KEY
)
def
test_processors_api
(
self
):
"""
Check that when working with a product being processed by the
ecommerce api, we correctly call to that api for the list of
available payment processors.
"""
# setting a nonempty sku on the course will a trigger calls to
# the ecommerce api to get payment processors.
course
=
self
.
_create_course
(
"verified"
,
sku
=
'nonempty-sku'
)
self
.
_enroll
(
course
.
id
,
"honor"
)
# mock out the payment processors endpoint
httpretty
.
register_uri
(
httpretty
.
GET
,
"{}/payment/processors/"
.
format
(
TEST_API_URL
),
body
=
json
.
dumps
([
'foo'
,
'bar'
]),
content_type
=
"application/json"
,
)
# make the server request
response
=
self
.
_get_page
(
'verify_student_start_flow'
,
course
.
id
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# ensure the mock api call was made. NOTE: the following line
# approximates the check - if the headers were empty it means
# there was no last request.
self
.
assertNotEqual
(
httpretty
.
last_request
()
.
headers
,
{})
class
CheckoutTestMixin
(
object
):
"""
...
...
@@ -927,7 +965,7 @@ class CheckoutTestMixin(object):
# ensure the response to a request from a stale js client is modified so as
# not to break behavior in the browser.
# (XCOM-214) remove after release.
expected_payment_data
=
EcommerceApiTestMixin
.
PAYMENT_DATA
.
copy
()
expected_payment_data
=
TEST_
PAYMENT_DATA
.
copy
()
expected_payment_data
[
'payment_form_data'
]
.
update
({
'foo'
:
'bar'
})
patched_create_order
.
return_value
=
expected_payment_data
# there is no 'processor' parameter in the post payload, so the response should only contain payment form data.
...
...
@@ -945,7 +983,7 @@ class CheckoutTestMixin(object):
self
.
assertEqual
(
data
,
{
'foo'
:
'bar'
})
@patch
(
'verify_student.views.checkout_with_shoppingcart'
,
return_value
=
EcommerceApiTestMixin
.
PAYMENT_DATA
)
@patch
(
'verify_student.views.checkout_with_shoppingcart'
,
return_value
=
TEST_
PAYMENT_DATA
)
class
TestCreateOrderShoppingCart
(
CheckoutTestMixin
,
ModuleStoreTestCase
):
""" Test view behavior when the shoppingcart is used. """
...
...
@@ -958,12 +996,9 @@ class TestCreateOrderShoppingCart(CheckoutTestMixin, ModuleStoreTestCase):
return
dict
(
zip
((
'request'
,
'user'
,
'course_key'
,
'course_mode'
,
'amount'
),
patched_create_order
.
call_args
[
0
]))
@override_settings
(
ECOMMERCE_API_URL
=
EcommerceApiTestMixin
.
ECOMMERCE_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
EcommerceApiTestMixin
.
ECOMMERCE_API_SIGNING_KEY
)
@patch
(
'verify_student.views.checkout_with_ecommerce_service'
,
return_value
=
EcommerceApiTestMixin
.
PAYMENT_DATA
)
class
TestCreateOrderEcommerceService
(
CheckoutTestMixin
,
EcommerceApiTestMixin
,
ModuleStoreTestCase
):
@override_settings
(
ECOMMERCE_API_URL
=
TEST_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
TEST_API_SIGNING_KEY
)
@patch
(
'verify_student.views.checkout_with_ecommerce_service'
,
return_value
=
TEST_PAYMENT_DATA
)
class
TestCreateOrderEcommerceService
(
CheckoutTestMixin
,
ModuleStoreTestCase
):
""" Test view behavior when the ecommerce service is used. """
def
make_sku
(
self
):
...
...
@@ -975,6 +1010,40 @@ class TestCreateOrderEcommerceService(CheckoutTestMixin, EcommerceApiTestMixin,
return
dict
(
zip
((
'user'
,
'course_key'
,
'course_mode'
,
'processor'
),
patched_create_order
.
call_args
[
0
]))
class
TestCheckoutWithEcommerceService
(
ModuleStoreTestCase
):
"""
Ensures correct behavior in the function `checkout_with_ecommerce_service`.
"""
@httpretty.activate
@override_settings
(
ECOMMERCE_API_URL
=
TEST_API_URL
,
ECOMMERCE_API_SIGNING_KEY
=
TEST_API_SIGNING_KEY
)
def
test_create_basket
(
self
):
"""
Check that when working with a product being processed by the
ecommerce api, we correctly call to that api to create a basket.
"""
user
=
UserFactory
.
create
(
username
=
"test-username"
)
course_mode
=
CourseModeFactory
(
sku
=
"test-sku"
)
expected_payment_data
=
{
'foo'
:
'bar'
}
# mock out the payment processors endpoint
httpretty
.
register_uri
(
httpretty
.
POST
,
"{}/baskets/"
.
format
(
TEST_API_URL
),
body
=
json
.
dumps
({
'payment_data'
:
expected_payment_data
}),
content_type
=
"application/json"
,
)
# call the function
actual_payment_data
=
checkout_with_ecommerce_service
(
user
,
'dummy-course-key'
,
course_mode
,
'test-processor'
)
# check the api call
self
.
assertEqual
(
json
.
loads
(
httpretty
.
last_request
()
.
body
),
{
'products'
:
[{
'sku'
:
'test-sku'
}],
'checkout'
:
True
,
'payment_processor_name'
:
'test-processor'
,
})
# check the response
self
.
assertEqual
(
actual_payment_data
,
expected_payment_data
)
class
TestCreateOrderView
(
ModuleStoreTestCase
):
"""
Tests for the create_order view of verified course enrollment process.
...
...
lms/djangoapps/verify_student/views.py
View file @
cc1c7d50
...
...
@@ -384,7 +384,7 @@ class PayAndVerifyView(View):
# get available payment processors
if
unexpired_paid_course_mode
.
sku
:
# transaction will be conducted via ecommerce service
processors
=
ecommerce_api_client
(
request
.
user
)
.
get_processors
()
processors
=
ecommerce_api_client
(
request
.
user
)
.
payment
.
processors
.
get
()
else
:
# transaction will be conducted using legacy shopping cart
processors
=
[
settings
.
CC_PROCESSOR_NAME
]
...
...
@@ -657,9 +657,13 @@ def checkout_with_ecommerce_service(user, course_key, course_mode, processor):
try
:
api
=
ecommerce_api_client
(
user
)
# Make an API call to create the order and retrieve the results
response_data
=
api
.
create_basket
(
course_mode
.
sku
,
processor
)
result
=
api
.
baskets
.
post
({
'products'
:
[{
'sku'
:
course_mode
.
sku
}],
'checkout'
:
True
,
'payment_processor_name'
:
processor
})
# Pass the payment parameters directly from the API response.
return
res
ponse_data
.
get
(
'payment_data'
)
return
res
ult
.
get
(
'payment_data'
)
except
SlumberBaseException
:
params
=
{
'username'
:
user
.
username
,
'mode'
:
course_mode
.
slug
,
'course_id'
:
unicode
(
course_key
)}
log
.
exception
(
'Failed to create order for
%(username)
s
%(mode)
s mode of
%(course_id)
s'
,
params
)
...
...
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