Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
ecommerce
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
ecommerce
Commits
3844fd40
Commit
3844fd40
authored
May 13, 2016
by
Marko Jevtic
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[SOL-1772] Add course query preview endpoint
parent
b1c8889f
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
145 additions
and
15 deletions
+145
-15
ecommerce/extensions/api/v2/tests/views/test_catalog.py
+60
-1
ecommerce/extensions/api/v2/tests/views/test_coupons.py
+2
-0
ecommerce/extensions/api/v2/views/catalog.py
+34
-0
ecommerce/extensions/api/v2/views/coupons.py
+1
-5
ecommerce/extensions/basket/tests/test_views.py
+6
-9
ecommerce/tests/mixins.py
+42
-0
No files found.
ecommerce/extensions/api/v2/tests/views/test_catalog.py
View file @
3844fd40
import
json
import
mock
import
ddt
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.test
import
RequestFactory
from
edx_rest_api_client.client
import
EdxRestApiClient
import
httpretty
from
requests.exceptions
import
ConnectionError
,
Timeout
from
oscar.core.loading
import
get_model
from
slumber.exceptions
import
SlumberBaseException
from
ecommerce.extensions.api.serializers
import
ProductSerializer
from
ecommerce.extensions.api.v2.views.catalog
import
CatalogViewSet
from
ecommerce.extensions.api.v2.tests.views.mixins
import
CatalogMixin
from
ecommerce.tests.mixins
import
ApiMockMixin
,
CatalogPreviewMockMixin
from
ecommerce.tests.testcases
import
TestCase
...
...
@@ -12,7 +22,9 @@ Catalog = get_model('catalogue', 'Catalog')
StockRecord
=
get_model
(
'partner'
,
'StockRecord'
)
class
CatalogViewSetTest
(
CatalogMixin
,
TestCase
):
@httpretty.activate
@ddt.ddt
class
CatalogViewSetTest
(
CatalogMixin
,
CatalogPreviewMockMixin
,
ApiMockMixin
,
TestCase
):
"""Test the Catalog and related products APIs."""
catalog_list_path
=
reverse
(
'api:v2:catalog-list'
)
...
...
@@ -22,6 +34,12 @@ class CatalogViewSetTest(CatalogMixin, TestCase):
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
password
)
def
prepare_request
(
self
,
url
):
factory
=
RequestFactory
()
request
=
factory
.
get
(
url
)
request
.
site
=
self
.
site
return
request
def
test_staff_authorization_required
(
self
):
"""Verify that only users with staff permissions can access the API. """
response
=
self
.
client
.
get
(
self
.
catalog_list_path
)
...
...
@@ -83,6 +101,47 @@ class CatalogViewSetTest(CatalogMixin, TestCase):
expected_data
=
ProductSerializer
(
self
.
stock_record
.
product
,
context
=
{
'request'
:
response
.
wsgi_request
})
.
data
self
.
assertListEqual
(
response_data
[
'results'
],
[
expected_data
])
@ddt.data
(
(
'/api/v2/coupons/preview/'
,
400
),
(
'/api/v2/coupons/preview/?query='
,
400
),
(
'/api/v2/coupons/preview/?wrong=parameter'
,
400
),
(
'/api/v2/coupons/preview/?query=id:course*'
,
200
)
)
@ddt.unpack
@mock.patch
(
'ecommerce.extensions.api.v2.views.catalog.get_course_catalog_api_client'
,
mock
.
Mock
(
return_value
=
EdxRestApiClient
(
settings
.
COURSE_CATALOG_API_URL
,
jwt
=
'auth-token'
))
)
def
test_preview_catalog_query_results
(
self
,
url
,
status_code
):
"""Test catalog query preview."""
self
.
mock_course_runs_contains_api_response
()
request
=
self
.
prepare_request
(
url
)
response
=
CatalogViewSet
()
.
preview
(
request
)
self
.
assertEqual
(
response
.
status_code
,
status_code
)
@ddt.data
(
ConnectionError
,
SlumberBaseException
,
Timeout
)
@mock.patch
(
'ecommerce.extensions.api.v2.views.catalog.get_course_catalog_api_client'
,
mock
.
Mock
(
return_value
=
EdxRestApiClient
(
settings
.
COURSE_CATALOG_API_URL
,
jwt
=
'auth-token'
))
)
def
test_preview_catalog_course_discovery_service_not_available
(
self
,
error
):
"""Test catalog query preview when course discovery is not available."""
url
=
'/api/v2/coupons/preview/?query=id:course*'
request
=
self
.
prepare_request
(
url
)
self
.
mock_api_error
(
error
=
error
,
url
=
'{}course_runs/?q=id:course*'
.
format
(
settings
.
COURSE_CATALOG_API_URL
))
response
=
CatalogViewSet
()
.
preview
(
request
)
self
.
assertEqual
(
response
.
status_code
,
400
)
class
PartnerCatalogViewSetTest
(
CatalogMixin
,
TestCase
):
...
...
ecommerce/extensions/api/v2/tests/views/test_coupons.py
View file @
3844fd40
...
...
@@ -8,6 +8,7 @@ import ddt
from
django.core.urlresolvers
import
reverse
from
django.db.utils
import
IntegrityError
from
django.test
import
RequestFactory
import
httpretty
from
oscar.apps.catalogue.categories
import
create_from_breadcrumbs
from
oscar.core.loading
import
get_class
,
get_model
from
oscar.test
import
factories
...
...
@@ -39,6 +40,7 @@ Voucher = get_model('voucher', 'Voucher')
COUPONS_LINK
=
reverse
(
'api:v2:coupons-list'
)
@httpretty.activate
@ddt.ddt
class
CouponViewSetTest
(
CouponMixin
,
CourseCatalogTestMixin
,
TestCase
):
"""Unit tests for creating coupon order."""
...
...
ecommerce/extensions/api/v2/views/catalog.py
View file @
3844fd40
import
logging
from
oscar.core.loading
import
get_model
from
requests.exceptions
import
ConnectionError
,
Timeout
from
rest_framework
import
status
from
rest_framework.permissions
import
IsAuthenticated
,
IsAdminUser
from
rest_framework.response
import
Response
from
rest_framework.viewsets
import
ReadOnlyModelViewSet
from
rest_framework_extensions.decorators
import
action
from
rest_framework_extensions.mixins
import
NestedViewSetMixin
from
slumber.exceptions
import
SlumberBaseException
from
ecommerce.extensions.api
import
serializers
from
ecommerce.core.url_utils
import
get_course_catalog_api_client
Catalog
=
get_model
(
'catalogue'
,
'Catalog'
)
logger
=
logging
.
getLogger
(
__name__
)
class
CatalogViewSet
(
NestedViewSetMixin
,
ReadOnlyModelViewSet
):
queryset
=
Catalog
.
objects
.
all
()
serializer_class
=
serializers
.
CatalogSerializer
permission_classes
=
(
IsAuthenticated
,
IsAdminUser
,)
@action
(
is_for_list
=
True
,
methods
=
[
'get'
])
def
preview
(
self
,
request
):
"""
Preview the results of the catalog query.
A list of course runs, indicating a course run presence within the catalog, will be returned.
---
parameters:
- name: query
description: Elasticsearch querystring query
required: true
type: string
paramType: query
multiple: false
"""
query
=
request
.
GET
.
get
(
'query'
,
''
)
if
query
:
try
:
api
=
get_course_catalog_api_client
(
request
.
site
)
response
=
api
.
course_runs
.
get
(
q
=
query
)
return
Response
(
response
)
except
(
ConnectionError
,
SlumberBaseException
,
Timeout
):
logger
.
error
(
'Unable to connect to Course Catalog service.'
)
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
ecommerce/extensions/api/v2/views/coupons.py
View file @
3844fd40
...
...
@@ -41,11 +41,7 @@ Voucher = get_model('voucher', 'Voucher')
class
CouponViewSet
(
EdxOrderPlacementMixin
,
viewsets
.
ModelViewSet
):
"""Endpoint for creating coupons.
Creates a new coupon product, adds it to a basket and creates a
new order from that basket.
"""
""" Coupon resource. """
queryset
=
Product
.
objects
.
filter
(
product_class__name
=
'Coupon'
)
permission_classes
=
(
IsAuthenticated
,
IsAdminUser
)
filter_backends
=
(
filters
.
DjangoFilterBackend
,
)
...
...
ecommerce/extensions/basket/tests/test_views.py
View file @
3844fd40
...
...
@@ -27,7 +27,7 @@ from ecommerce.extensions.offer.utils import format_benefit_value
from
ecommerce.extensions.payment.tests.processors
import
DummyProcessor
from
ecommerce.extensions.test.factories
import
prepare_voucher
from
ecommerce.tests.factories
import
StockRecordFactory
from
ecommerce.tests.mixins
import
CouponMixin
,
LmsApiMockMixin
from
ecommerce.tests.mixins
import
ApiMockMixin
,
CouponMixin
,
LmsApiMockMixin
from
ecommerce.tests.testcases
import
TestCase
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
...
...
@@ -175,7 +175,7 @@ class BasketSingleItemViewTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockM
@httpretty.activate
@ddt.ddt
@override_settings
(
PAYMENT_PROCESSORS
=
[
'ecommerce.extensions.payment.tests.processors.DummyProcessor'
])
class
BasketSummaryViewTests
(
CourseCatalogTestMixin
,
LmsApiMockMixin
,
TestCase
):
class
BasketSummaryViewTests
(
CourseCatalogTestMixin
,
LmsApiMockMixin
,
ApiMockMixin
,
TestCase
):
""" BasketSummaryView basket view tests. """
path
=
reverse
(
'basket:summary'
)
...
...
@@ -199,12 +199,6 @@ class BasketSummaryViewTests(CourseCatalogTestMixin, LmsApiMockMixin, TestCase):
toggle_switch
(
settings
.
PAYMENT_PROCESSOR_SWITCH_PREFIX
+
DummyProcessor
.
NAME
,
True
)
def
mock_course_api_error
(
self
,
error
):
def
callback
(
request
,
uri
,
headers
):
# pylint: disable=unused-argument
raise
error
course_url
=
get_lms_url
(
'api/courses/v1/courses/{}/'
.
format
(
self
.
course
.
id
))
httpretty
.
register_uri
(
httpretty
.
GET
,
course_url
,
body
=
callback
,
content_type
=
'application/json'
)
def
create_basket_and_add_product
(
self
,
product
):
basket
=
factories
.
BasketFactory
(
owner
=
self
.
user
,
site
=
self
.
site
)
basket
.
add_product
(
product
,
1
)
...
...
@@ -227,7 +221,10 @@ class BasketSummaryViewTests(CourseCatalogTestMixin, LmsApiMockMixin, TestCase):
self
.
assertEqual
(
basket
.
lines
.
count
(),
1
)
logger_name
=
'ecommerce.extensions.basket.views'
self
.
mock_course_api_error
(
error
)
self
.
mock_api_error
(
error
=
error
,
url
=
get_lms_url
(
'api/courses/v1/courses/{}/'
.
format
(
self
.
course
.
id
))
)
with
LogCapture
(
logger_name
)
as
l
:
response
=
self
.
client
.
get
(
self
.
path
)
...
...
ecommerce/tests/mixins.py
View file @
3844fd40
...
...
@@ -279,6 +279,18 @@ class TestServerUrlMixin(object):
return
'http://{domain}{path}'
.
format
(
domain
=
site
.
domain
,
path
=
path
)
class
ApiMockMixin
(
object
):
""" Common Mocks for the API responses. """
def
setUp
(
self
):
super
(
ApiMockMixin
,
self
)
.
setUp
()
def
mock_api_error
(
self
,
error
,
url
):
def
callback
(
request
,
uri
,
headers
):
# pylint: disable=unused-argument
raise
error
httpretty
.
register_uri
(
httpretty
.
GET
,
url
,
body
=
callback
,
content_type
=
'application/json'
)
class
LmsApiMockMixin
(
object
):
""" Mocks for the LMS API reponses. """
...
...
@@ -303,6 +315,36 @@ class LmsApiMockMixin(object):
httpretty
.
register_uri
(
httpretty
.
GET
,
course_url
,
body
=
course_info_json
,
content_type
=
'application/json'
)
class
CatalogPreviewMockMixin
(
object
):
""" Mocks for the Course Discovery responses. """
def
setUp
(
self
):
super
(
CatalogPreviewMockMixin
,
self
)
.
setUp
()
def
mock_course_runs_contains_api_response
(
self
,
course_run
=
None
,
query
=
None
):
""" Helper function to register an API endpoint for the course run information. """
course_run_info
=
{
'count'
:
1
,
'results'
:
[{
'key'
:
course_run
.
id
,
'title'
:
course_run
.
name
,
}]
if
course_run
else
[{
'key'
:
'course-v1:test+test+test'
,
'title'
:
'Test course'
,
}],
}
course_run_info_json
=
json
.
dumps
(
course_run_info
)
course_run_url
=
'{}course_runs/?query={}'
.
format
(
settings
.
COURSE_CATALOG_API_URL
,
query
if
query
else
'id:course*'
)
httpretty
.
register_uri
(
httpretty
.
GET
,
course_run_url
,
body
=
course_run_info_json
,
content_type
=
'application/json'
)
class
CouponMixin
(
object
):
""" Mixin for preparing data for coupons and creating coupons. """
...
...
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