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
9fd6f149
Commit
9fd6f149
authored
May 30, 2016
by
Clinton Blackburn
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moved coupons-related mixins to appropriate module (#764)
parent
79ad83c9
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
250 additions
and
267 deletions
+250
-267
ecommerce/coupons/tests/mixins.py
+168
-0
ecommerce/coupons/tests/test_utils.py
+2
-4
ecommerce/coupons/tests/test_views.py
+4
-4
ecommerce/extensions/api/v2/tests/views/test_catalog.py
+6
-7
ecommerce/extensions/api/v2/tests/views/test_checkout.py
+3
-3
ecommerce/extensions/api/v2/tests/views/test_coupons.py
+13
-19
ecommerce/extensions/api/v2/tests/views/test_products.py
+10
-11
ecommerce/extensions/api/v2/views/coupons.py
+12
-15
ecommerce/extensions/basket/tests/test_views.py
+6
-4
ecommerce/extensions/catalogue/tests/test_utils.py
+8
-7
ecommerce/extensions/fulfillment/tests/test_modules.py
+3
-3
ecommerce/extensions/offer/tests/test_models.py
+2
-4
ecommerce/extensions/voucher/tests/test_utils.py
+5
-8
ecommerce/extensions/voucher/tests/test_views.py
+2
-1
ecommerce/tests/mixins.py
+6
-177
No files found.
ecommerce/coupons/tests/mixins.py
0 → 100644
View file @
9fd6f149
import
datetime
import
json
import
httpretty
from
django.conf
import
settings
from
django.test
import
RequestFactory
from
oscar.test
import
factories
from
ecommerce.core.models
import
BusinessClient
from
ecommerce.extensions.api.v2.views.coupons
import
CouponViewSet
from
ecommerce.extensions.basket.utils
import
prepare_basket
from
ecommerce.tests.factories
import
PartnerFactory
from
ecommerce.tests.mixins
import
ProductClass
,
Catalog
,
Benefit
,
Voucher
,
Applicator
class
CatalogPreviewMockMixin
(
object
):
""" Mocks for the Course Discovery responses. """
def
setUp
(
self
):
super
(
CatalogPreviewMockMixin
,
self
)
.
setUp
()
def
mock_dynamic_catalog_course_runs_api
(
self
,
course_run
=
None
,
query
=
None
):
""" Helper function to register a dynamic course catalog 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'
:
'test'
,
'title'
:
'Test course'
,
}],
}
course_run_info_json
=
json
.
dumps
(
course_run_info
)
course_run_url
=
'{}course_runs/?q={}'
.
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'
)
def
mock_dynamic_catalog_contains_api
(
self
,
course_run_ids
,
query
):
""" Helper function to register a dynamic course catalog API endpoint for the contains information. """
course_contains_info
=
{
'course_runs'
:
{}
}
for
course_run_id
in
course_run_ids
:
course_contains_info
[
'course_runs'
][
course_run_id
]
=
True
course_run_info_json
=
json
.
dumps
(
course_contains_info
)
course_run_url
=
'{}course_runs/contains/?course_run_ids={}&query={}'
.
format
(
settings
.
COURSE_CATALOG_API_URL
,
(
course_run_id
for
course_run_id
in
course_run_ids
),
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. """
REDEMPTION_URL
=
"/coupons/offer/?code={}"
def
setUp
(
self
):
super
(
CouponMixin
,
self
)
.
setUp
()
self
.
category
=
factories
.
CategoryFactory
()
# Force the creation of a coupon ProductClass
self
.
coupon_product_class
# pylint: disable=pointless-statement
@property
def
coupon_product_class
(
self
):
defaults
=
{
'requires_shipping'
:
False
,
'track_stock'
:
False
,
'name'
:
'Coupon'
}
pc
,
created
=
ProductClass
.
objects
.
get_or_create
(
name
=
'Coupon'
,
slug
=
'coupon'
,
defaults
=
defaults
)
if
created
:
factories
.
ProductAttributeFactory
(
code
=
'coupon_vouchers'
,
name
=
'Coupon vouchers'
,
product_class
=
pc
,
type
=
'entity'
)
factories
.
ProductAttributeFactory
(
code
=
'note'
,
name
=
'Note'
,
product_class
=
pc
,
type
=
'text'
)
return
pc
def
create_coupon
(
self
,
title
=
'Test coupon'
,
price
=
100
,
client
=
None
,
partner
=
None
,
catalog
=
None
,
code
=
''
,
benefit_value
=
100
,
note
=
None
,
max_uses
=
None
,
quantity
=
5
,
catalog_query
=
None
,
course_seat_types
=
None
):
"""Helper method for creating a coupon.
Arguments:
title(str): Title of the coupon
price(int): Price of the coupon
partner(Partner): Partner used for creating a catalog
catalog(Catalog): Catalog of courses for which the coupon applies
code(str): Custom coupon code
benefit_value(int): The voucher benefit value
catalog_query(str): course query string
course_seat_types(JSONField): List of seat types
Returns:
coupon (Coupon)
"""
if
partner
is
None
:
partner
=
PartnerFactory
(
name
=
'Tester'
)
if
client
is
None
:
client
,
__
=
BusinessClient
.
objects
.
get_or_create
(
name
=
'Test Client'
)
if
catalog
is
None
and
not
(
catalog_query
and
course_seat_types
):
catalog
=
Catalog
.
objects
.
create
(
partner
=
partner
)
if
code
is
not
''
:
quantity
=
1
data
=
{
'partner'
:
partner
,
'benefit_type'
:
Benefit
.
PERCENTAGE
,
'benefit_value'
:
benefit_value
,
'catalog'
:
catalog
,
'end_date'
:
datetime
.
date
(
2020
,
1
,
1
),
'code'
:
code
,
'quantity'
:
quantity
,
'start_date'
:
datetime
.
date
(
2015
,
1
,
1
),
'voucher_type'
:
Voucher
.
SINGLE_USE
,
'categories'
:
[
self
.
category
],
'note'
:
note
,
'max_uses'
:
max_uses
,
'catalog_query'
:
catalog_query
,
'course_seat_types'
:
course_seat_types
,
}
coupon
=
CouponViewSet
()
.
create_coupon_product
(
title
=
title
,
price
=
price
,
data
=
data
)
request
=
RequestFactory
()
request
.
site
=
self
.
site
request
.
user
=
factories
.
UserFactory
()
request
.
COOKIES
=
{}
self
.
basket
=
prepare_basket
(
request
,
coupon
)
self
.
response_data
=
CouponViewSet
()
.
create_order_for_invoice
(
self
.
basket
,
coupon_id
=
coupon
.
id
,
client
=
client
)
coupon
.
client
=
client
return
coupon
def
apply_voucher
(
self
,
user
,
site
,
voucher
):
""" Apply the voucher to a basket. """
basket
=
factories
.
BasketFactory
(
owner
=
user
,
site
=
site
)
product
=
voucher
.
offers
.
first
()
.
benefit
.
range
.
all_products
()[
0
]
basket
.
add_product
(
product
)
basket
.
vouchers
.
add
(
voucher
)
Applicator
()
.
apply
(
basket
,
self
.
user
)
return
basket
ecommerce/coupons/tests/test_utils.py
View file @
9fd6f149
import
httpretty
import
mock
from
django.conf
import
settings
from
edx_rest_api_client.client
import
EdxRestApiClient
import
httpretty
from
ecommerce.coupons.tests.mixins
import
CatalogPreviewMockMixin
,
CouponMixin
from
ecommerce.coupons.utils
import
get_seats_from_query
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.tests.mixins
import
CouponMixin
,
CatalogPreviewMockMixin
from
ecommerce.tests.testcases
import
TestCase
...
...
@@ -19,7 +18,6 @@ from ecommerce.tests.testcases import TestCase
))
)
class
CouponUtilsTests
(
CouponMixin
,
CourseCatalogTestMixin
,
CatalogPreviewMockMixin
,
TestCase
):
def
setUp
(
self
):
super
(
CouponUtilsTests
,
self
)
.
setUp
()
self
.
query
=
'key:*'
...
...
ecommerce/coupons/tests/test_views.py
View file @
9fd6f149
import
datetime
import
ddt
import
httpretty
import
pytz
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.utils.timezone
import
now
from
django.utils.translation
import
ugettext_lazy
as
_
import
httpretty
from
oscar.core.loading
import
get_class
,
get_model
from
oscar.test.factories
import
(
ConditionalOfferFactory
,
OrderFactory
,
OrderLineFactory
,
RangeFactory
,
VoucherFactory
)
from
oscar.test.utils
import
RequestFactory
import
pytz
from
ecommerce.core.url_utils
import
get_lms_url
,
get_lms_enrollment_api_url
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.coupons.views
import
get_voucher_from_code
,
voucher_is_valid
from
ecommerce.courses.tests.factories
import
CourseFactory
from
ecommerce.extensions.api
import
exceptions
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.extensions.test.factories
import
prepare_voucher
from
ecommerce.tests.mixins
import
CouponMixin
,
LmsApiMockMixin
from
ecommerce.tests.mixins
import
LmsApiMockMixin
from
ecommerce.tests.testcases
import
TestCase
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
...
...
@@ -64,7 +65,6 @@ class CouponAppViewTests(TestCase):
class
GetVoucherTests
(
TestCase
):
def
test_get_voucher_from_code
(
self
):
""" Verify that get_voucher_from_code() returns product and voucher. """
original_voucher
,
original_product
=
prepare_voucher
(
code
=
COUPON_CODE
)
...
...
ecommerce/extensions/api/v2/tests/views/test_catalog.py
View file @
9fd6f149
import
json
import
mock
import
ddt
import
httpretty
import
mock
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
requests.exceptions
import
ConnectionError
,
Timeout
from
slumber.exceptions
import
SlumberBaseException
from
ecommerce.coupons.tests.mixins
import
CatalogPreviewMockMixin
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.extensions.api.v2.views.catalog
import
CatalogViewSet
from
ecommerce.tests.mixins
import
ApiMockMixin
from
ecommerce.tests.testcases
import
TestCase
Catalog
=
get_model
(
'catalogue'
,
'Catalog'
)
StockRecord
=
get_model
(
'partner'
,
'StockRecord'
)
...
...
@@ -144,7 +144,6 @@ class CatalogViewSetTest(CatalogMixin, CatalogPreviewMockMixin, ApiMockMixin, Te
class
PartnerCatalogViewSetTest
(
CatalogMixin
,
TestCase
):
def
setUp
(
self
):
super
(
PartnerCatalogViewSetTest
,
self
)
.
setUp
()
...
...
ecommerce/extensions/api/v2/tests/views/test_checkout.py
View file @
9fd6f149
...
...
@@ -34,9 +34,9 @@ class CheckoutViewTests(TestCase):
super
(
CheckoutViewTests
,
self
)
.
setUp
()
self
.
user
=
self
.
create_user
()
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
password
)
Basket
.
objects
.
create
(
owner
=
self
.
user
)
self
.
basket
=
Basket
.
objects
.
create
(
owner
=
self
.
user
)
self
.
data
=
{
'basket_id'
:
1
,
'basket_id'
:
self
.
basket
.
id
,
'payment_processor'
:
DummyProcessorWithUrl
.
NAME
}
...
...
@@ -70,7 +70,7 @@ class CheckoutViewTests(TestCase):
response
=
self
.
client
.
post
(
self
.
path
,
data
=
self
.
data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
basket
=
Basket
.
objects
.
get
(
id
=
1
)
basket
=
Basket
.
objects
.
get
(
id
=
self
.
basket
.
id
)
self
.
assertEqual
(
basket
.
status
,
Basket
.
FROZEN
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'payment_form_data'
][
'transaction_param'
],
'test_trans_param'
)
...
...
ecommerce/extensions/api/v2/tests/views/test_coupons.py
View file @
9fd6f149
...
...
@@ -3,20 +3,21 @@ from __future__ import unicode_literals
import
json
from
decimal
import
Decimal
import
mock
import
ddt
import
httpretty
import
mock
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.db.utils
import
IntegrityError
from
django.test
import
RequestFactory
from
edx_rest_api_client.client
import
EdxRestApiClient
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
from
rest_framework
import
status
from
ecommerce.coupons.tests.mixins
import
CatalogPreviewMockMixin
,
CouponMixin
from
ecommerce.courses.tests.factories
import
CourseFactory
from
ecommerce.extensions.api.constants
import
APIConstants
as
AC
from
ecommerce.extensions.api.v2.views.coupons
import
CouponViewSet
...
...
@@ -24,7 +25,7 @@ from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from
ecommerce.extensions.voucher.models
import
CouponVouchers
from
ecommerce.invoice.models
import
Invoice
from
ecommerce.tests.factories
import
ProductFactory
,
SiteConfigurationFactory
,
SiteFactory
from
ecommerce.tests.mixins
import
CouponMixin
,
CatalogPreviewMockMixin
,
ThrottlingMixin
from
ecommerce.tests.mixins
import
ThrottlingMixin
from
ecommerce.tests.testcases
import
TestCase
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
...
...
@@ -46,18 +47,15 @@ COUPONS_LINK = reverse('api:v2:coupons-list')
@httpretty.activate
@ddt.ddt
class
CouponViewSetTest
(
CouponMixin
,
CourseCatalogTestMixin
,
TestCase
):
"""Unit tests for creating coupon order."""
def
setUp
(
self
):
super
(
CouponViewSetTest
,
self
)
.
setUp
()
self
.
user
=
self
.
create_user
(
is_staff
=
True
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
password
)
course
=
CourseFactory
(
id
=
'edx/Demo_Course/DemoX'
)
course
.
create_or_update_seat
(
'verified'
,
True
,
50
,
self
.
partner
)
self
.
seat
=
course
.
create_or_update_seat
(
'verified'
,
True
,
50
,
self
.
partner
)
self
.
catalog
=
Catalog
.
objects
.
create
(
partner
=
self
.
partner
)
self
.
product_class
,
__
=
ProductClass
.
objects
.
get_or_create
(
name
=
'Coupon'
)
self
.
coupon_data
=
{
'title'
:
'Test Coupon'
,
'partner'
:
self
.
partner
,
...
...
@@ -90,10 +88,11 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
def
test_create
(
self
,
voucher_type
,
max_uses
,
expected_max_uses
):
"""Test the create method."""
title
=
'Test coupon'
stock_record
=
self
.
seat
.
stockrecords
.
first
()
self
.
coupon_data
.
update
({
'title'
:
title
,
'client_username'
:
'Client'
,
'stock_record_ids'
:
[
1
],
'stock_record_ids'
:
[
stock_record
.
id
],
'voucher_type'
:
voucher_type
,
'price'
:
100
,
'category_ids'
:
[
self
.
category
.
id
],
...
...
@@ -122,7 +121,7 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
def
test_create_coupon_product
(
self
):
"""Test the created coupon data."""
coupon
=
self
.
create_coupon
()
self
.
assertEqual
(
Product
.
objects
.
filter
(
product_class
=
self
.
product_class
)
.
count
(),
1
)
self
.
assertEqual
(
Product
.
objects
.
filter
(
product_class
=
self
.
coupon_
product_class
)
.
count
(),
1
)
self
.
assertIsInstance
(
coupon
,
Product
)
self
.
assertEqual
(
coupon
.
title
,
'Test coupon'
)
...
...
@@ -239,7 +238,7 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
def
test_delete_coupon
(
self
):
"""Test the coupon deletion."""
coupon
=
self
.
create_coupon
(
partner
=
self
.
partner
)
self
.
assertEqual
(
Product
.
objects
.
filter
(
product_class
=
self
.
product_class
)
.
count
(),
1
)
self
.
assertEqual
(
Product
.
objects
.
filter
(
product_class
=
self
.
coupon_
product_class
)
.
count
(),
1
)
self
.
assertEqual
(
StockRecord
.
objects
.
filter
(
product
=
coupon
)
.
count
(),
1
)
coupon_voucher_qs
=
CouponVouchers
.
objects
.
filter
(
coupon
=
coupon
)
self
.
assertEqual
(
coupon_voucher_qs
.
count
(),
1
)
...
...
@@ -249,7 +248,7 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
request
.
site
=
self
.
setup_site_configuration
()
response
=
CouponViewSet
()
.
destroy
(
request
,
coupon
.
id
)
self
.
assertEqual
(
Product
.
objects
.
filter
(
product_class
=
self
.
product_class
)
.
count
(),
0
)
self
.
assertEqual
(
Product
.
objects
.
filter
(
product_class
=
self
.
coupon_
product_class
)
.
count
(),
0
)
self
.
assertEqual
(
StockRecord
.
objects
.
filter
(
product
=
coupon
)
.
count
(),
0
)
coupon_voucher_qs
=
CouponVouchers
.
objects
.
filter
(
coupon
=
coupon
)
self
.
assertEqual
(
coupon_voucher_qs
.
count
(),
0
)
...
...
@@ -261,13 +260,8 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
@ddt.ddt
class
CouponViewSetFunctionalTest
(
CouponMixin
,
CourseCatalogTestMixin
,
CatalogPreviewMockMixin
,
ThrottlingMixin
,
TestCase
):
class
CouponViewSetFunctionalTest
(
CouponMixin
,
CourseCatalogTestMixin
,
CatalogPreviewMockMixin
,
ThrottlingMixin
,
TestCase
):
"""Test the coupon order creation functionality."""
def
setUp
(
self
):
...
...
@@ -509,7 +503,7 @@ class CouponViewSetFunctionalTest(
# Seats derived from a migrated "audit" mode do not have a certificate_type attribute.
if
mode
==
'audit'
:
seat
=
ProductFactory
()
self
.
data
.
update
({
'stock_record_ids'
:
[
StockRecord
.
objects
.
get
(
product
=
seat
)
.
id
]
,
})
self
.
data
.
update
({
'stock_record_ids'
:
[
StockRecord
.
objects
.
get
(
product
=
seat
)
.
id
]})
response
=
self
.
client
.
post
(
COUPONS_LINK
,
data
=
self
.
data
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
...
...
ecommerce/extensions/api/v2/tests/views/test_products.py
View file @
9fd6f149
from
__future__
import
unicode_literals
import
datetime
import
json
import
pytz
from
django.core.urlresolvers
import
reverse
from
django.test
import
RequestFactory
from
oscar.core.loading
import
get_model
import
pytz
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.courses.models
import
Course
from
ecommerce.extensions.api.serializers
import
ProductSerializer
from
ecommerce.extensions.api.v2.tests.views
import
JSON_CONTENT_TYPE
,
ProductSerializerMixin
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.tests.mixins
import
CouponMixin
from
ecommerce.tests.testcases
import
TestCase
Benefit
=
get_model
(
'offer'
,
'Benefit'
)
...
...
@@ -20,7 +23,6 @@ Voucher = get_model('voucher', 'Voucher')
class
ProductViewSetBase
(
ProductSerializerMixin
,
CourseCatalogTestMixin
,
TestCase
):
def
setUp
(
self
):
super
(
ProductViewSetBase
,
self
)
.
setUp
()
self
.
user
=
self
.
create_user
(
is_staff
=
True
)
...
...
@@ -33,7 +35,6 @@ class ProductViewSetBase(ProductSerializerMixin, CourseCatalogTestMixin, TestCas
class
ProductViewSetTests
(
ProductViewSetBase
):
def
test_list
(
self
):
""" Verify a list of products is returned. """
path
=
reverse
(
'api:v2:product-list'
)
...
...
@@ -129,7 +130,6 @@ class ProductViewSetTests(ProductViewSetBase):
class
ProductViewSetCouponTests
(
CouponMixin
,
ProductViewSetBase
):
def
test_coupon_product_details
(
self
):
"""Verify the endpoint returns all coupon information."""
coupon
=
self
.
create_coupon
()
...
...
@@ -137,12 +137,11 @@ class ProductViewSetCouponTests(CouponMixin, ProductViewSetBase):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'id'
],
3
)
self
.
assertEqual
(
response_data
[
'title'
],
'Test coupon'
)
self
.
assertEqual
(
response_data
[
'price'
],
'100.00'
)
self
.
assertEqual
(
response_data
[
'attribute_values'
][
0
][
'name'
],
'Coupon vouchers'
)
self
.
assertEqual
(
len
(
response_data
[
'attribute_values'
][
0
][
'value'
]),
5
)
request
=
RequestFactory
(
SERVER_NAME
=
self
.
site
.
domain
)
.
get
(
'/'
)
request
.
user
=
self
.
user
request
.
site
=
self
.
site
expected
=
ProductSerializer
(
coupon
,
context
=
{
'request'
:
request
})
.
data
self
.
assertDictEqual
(
response
.
data
,
expected
)
def
test_coupon_voucher_serializer
(
self
):
"""Verify that the vouchers of a coupon are properly serialized."""
...
...
ecommerce/extensions/api/v2/views/coupons.py
View file @
9fd6f149
...
...
@@ -4,7 +4,6 @@ import logging
from
decimal
import
Decimal
import
dateutil.parser
from
django.conf
import
settings
from
django.db
import
transaction
from
django.db.utils
import
IntegrityError
...
...
@@ -209,9 +208,9 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
catalog_query
=
data
[
'catalog_query'
],
course_seat_types
=
data
[
'course_seat_types'
]
)
except
IntegrityError
as
ex
:
except
IntegrityError
:
logger
.
exception
(
'Failed to create vouchers for [
%
s] coupon.'
,
coupon_product
.
title
)
raise
IntegrityError
(
ex
)
# pylint: disable=nonstandard-exception
raise
coupon_vouchers
=
CouponVouchers
.
objects
.
get
(
coupon
=
coupon_product
)
...
...
@@ -219,28 +218,26 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
coupon_product
.
attr
.
note
=
data
[
'note'
]
coupon_product
.
save
()
sku
=
generate_sku
(
sku
=
generate_sku
(
product
=
coupon_product
,
partner
=
data
[
'partner'
])
StockRecord
.
objects
.
update_or_create
(
product
=
coupon_product
,
partner
=
data
[
'partner'
],
partner_sku
=
sku
,
defaults
=
{
'price_currency'
:
'USD'
,
'price_excl_tax'
:
price
}
)
stock_record
,
__
=
StockRecord
.
objects
.
get_or_create
(
product
=
coupon_product
,
partner
=
data
[
'partner'
],
partner_sku
=
sku
)
stock_record
.
price_currency
=
'USD'
stock_record
.
price_excl_tax
=
price
stock_record
.
save
()
return
coupon_product
def
assign_categories_to_coupon
(
self
,
coupon
,
categories
):
"""
Assign categories to a coupon. If a category is already assigned, it will be fetch instead.
Assigns categories to a coupon.
Arguments:
coupon (Product): Coupon product
categories (
List): List of categories to be assigned to a coupon
categories (
list): List of Category instances
"""
for
category
in
categories
:
ProductCategory
.
objects
.
get_or_create
(
product
=
coupon
,
category
=
category
)
...
...
ecommerce/extensions/basket/tests/test_views.py
View file @
9fd6f149
...
...
@@ -3,31 +3,32 @@ import hashlib
import
json
import
ddt
import
httpretty
import
pytz
from
django.conf
import
settings
from
django.core.cache
import
cache
from
django.core.urlresolvers
import
reverse
from
django.test
import
override_settings
from
django.utils.translation
import
ugettext_lazy
as
_
import
httpretty
from
oscar.core.loading
import
get_class
,
get_model
from
oscar.test
import
newfactories
as
factories
import
pytz
from
requests.exceptions
import
ConnectionError
,
Timeout
from
slumber.exceptions
import
SlumberBaseException
from
testfixtures
import
LogCapture
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
ENROLLMENT_CODE_SWITCH
from
ecommerce.core.url_utils
import
get_lms_enrollment_api_url
from
ecommerce.core.models
import
SiteConfiguration
from
ecommerce.core.tests
import
toggle_switch
from
ecommerce.core.url_utils
import
get_lms_enrollment_api_url
from
ecommerce.core.url_utils
import
get_lms_url
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.courses.tests.factories
import
CourseFactory
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
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
ApiMockMixin
,
CouponMixin
,
LmsApiMockMixin
from
ecommerce.tests.mixins
import
ApiMockMixin
,
LmsApiMockMixin
from
ecommerce.tests.testcases
import
TestCase
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
...
...
@@ -90,6 +91,7 @@ class BasketSingleItemViewTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockM
def
callback
(
request
,
uri
,
headers
):
# pylint: disable=unused-argument
raise
error
url
=
'{host}/{username},{course_id}'
.
format
(
host
=
get_lms_enrollment_api_url
(),
username
=
self
.
user
.
username
,
...
...
ecommerce/extensions/catalogue/tests/test_utils.py
View file @
9fd6f149
...
...
@@ -4,10 +4,10 @@ from hashlib import md5
from
oscar.core.loading
import
get_model
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.extensions.catalogue.utils
import
generate_sku
,
get_or_create_catalog
from
ecommerce.tests.factories
import
ProductFactory
from
ecommerce.tests.mixins
import
CouponMixin
from
ecommerce.tests.testcases
import
TestCase
Benefit
=
get_model
(
'offer'
,
'Benefit'
)
...
...
@@ -26,7 +26,7 @@ class UtilsTests(CourseCatalogTestMixin, TestCase):
def
setUp
(
self
):
super
(
UtilsTests
,
self
)
.
setUp
()
self
.
course
=
Course
.
objects
.
create
(
id
=
COURSE_ID
,
name
=
'Test Course'
)
self
.
course
.
create_or_update_seat
(
'verified'
,
False
,
0
,
self
.
partner
)
self
.
seat
=
self
.
course
.
create_or_update_seat
(
'verified'
,
False
,
0
,
self
.
partner
)
self
.
catalog
=
Catalog
.
objects
.
create
(
name
=
'Test'
,
partner_id
=
self
.
partner
.
id
)
def
test_generate_sku_with_missing_product_class
(
self
):
...
...
@@ -56,14 +56,15 @@ class UtilsTests(CourseCatalogTestMixin, TestCase):
def
test_get_or_create_catalog
(
self
):
"""Verify that the proper catalog is fetched."""
self
.
catalog
.
stock_records
.
add
(
StockRecord
.
objects
.
first
())
stock_record
=
self
.
seat
.
stockrecords
.
first
()
self
.
catalog
.
stock_records
.
add
(
stock_record
)
self
.
assertEqual
(
self
.
catalog
.
id
,
1
)
existing_catalog
,
created
=
get_or_create_catalog
(
name
=
'Test'
,
partner
=
self
.
partner
,
stock_record_ids
=
[
1
]
stock_record_ids
=
[
stock_record
.
id
]
)
self
.
assertFalse
(
created
)
self
.
assertEqual
(
self
.
catalog
,
existing_catalog
)
...
...
@@ -71,12 +72,13 @@ class UtilsTests(CourseCatalogTestMixin, TestCase):
course_id
=
'sku/test2/course'
course
=
Course
.
objects
.
create
(
id
=
course_id
,
name
=
'Test Course 2'
)
course
.
create_or_update_seat
(
'verified'
,
False
,
0
,
self
.
partner
)
seat_2
=
course
.
create_or_update_seat
(
'verified'
,
False
,
0
,
self
.
partner
)
stock_record_2
=
seat_2
.
stockrecords
.
first
()
new_catalog
,
created
=
get_or_create_catalog
(
name
=
'Test'
,
partner
=
self
.
partner
,
stock_record_ids
=
[
1
,
2
]
stock_record_ids
=
[
stock_record
.
id
,
stock_record_2
.
id
]
)
self
.
assertTrue
(
created
)
self
.
assertNotEqual
(
self
.
catalog
,
new_catalog
)
...
...
@@ -84,7 +86,6 @@ class UtilsTests(CourseCatalogTestMixin, TestCase):
class
CouponUtilsTests
(
CouponMixin
,
CourseCatalogTestMixin
,
TestCase
):
def
setUp
(
self
):
super
(
CouponUtilsTests
,
self
)
.
setUp
()
self
.
course
=
Course
.
objects
.
create
(
id
=
COURSE_ID
,
name
=
'Test Course'
)
...
...
ecommerce/extensions/fulfillment/tests/test_modules.py
View file @
9fd6f149
"""Tests of the Fulfillment API's fulfillment modules."""
import
datetime
import
json
import
ddt
import
httpretty
import
mock
from
django.test
import
override_settings
from
oscar.core.loading
import
get_class
,
get_model
from
oscar.test
import
factories
...
...
@@ -15,9 +15,10 @@ from testfixtures import LogCapture
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
ENROLLMENT_CODE_SWITCH
from
ecommerce.core.tests
import
toggle_switch
from
ecommerce.core.url_utils
import
get_lms_enrollment_api_url
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.courses.models
import
Course
from
ecommerce.courses.utils
import
mode_for_seat
from
ecommerce.courses.tests.factories
import
CourseFactory
from
ecommerce.courses.utils
import
mode_for_seat
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.extensions.fulfillment.modules
import
(
CouponFulfillmentModule
,
EnrollmentCodeFulfillmentModule
,
EnrollmentFulfillmentModule
...
...
@@ -26,7 +27,6 @@ from ecommerce.extensions.fulfillment.status import LINE
from
ecommerce.extensions.fulfillment.tests.mixins
import
FulfillmentTestMixin
from
ecommerce.extensions.voucher.models
import
OrderLineVouchers
from
ecommerce.extensions.voucher.utils
import
create_vouchers
from
ecommerce.tests.mixins
import
CouponMixin
from
ecommerce.tests.testcases
import
TestCase
JSON
=
'application/json'
...
...
ecommerce/extensions/offer/tests/test_models.py
View file @
9fd6f149
import
httpretty
import
mock
from
django.conf
import
settings
from
django.test
import
RequestFactory
from
edx_rest_api_client.client
import
EdxRestApiClient
import
httpretty
from
oscar.core.loading
import
get_model
from
oscar.test
import
factories
from
ecommerce.coupons.tests.mixins
import
CatalogPreviewMockMixin
,
CouponMixin
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.tests.mixins
import
CouponMixin
,
CatalogPreviewMockMixin
from
ecommerce.tests.testcases
import
TestCase
Catalog
=
get_model
(
'catalogue'
,
'Catalog'
)
...
...
ecommerce/extensions/voucher/tests/test_utils.py
View file @
9fd6f149
import
httpretty
from
django.db
import
IntegrityError
from
django.test
import
override_settings
from
django.utils.translation
import
ugettext_lazy
as
_
import
httpretty
from
oscar.templatetags.currency_filters
import
currency
from
oscar.test.factories
import
*
# pylint:disable=wildcard-import,unused-wildcard-import
from
ecommerce.core.url_utils
import
get_ecommerce_url
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.courses.tests.factories
import
CourseFactory
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.extensions.fulfillment.modules
import
CouponFulfillmentModule
...
...
@@ -13,10 +14,9 @@ from ecommerce.extensions.fulfillment.status import LINE
from
ecommerce.extensions.voucher.utils
import
(
create_vouchers
,
generate_coupon_report
,
get_voucher_discount_info
,
update_voucher_offer
)
from
ecommerce.tests.mixins
import
CouponMixin
,
LmsApiMockMixin
from
ecommerce.tests.mixins
import
LmsApiMockMixin
from
ecommerce.tests.testcases
import
TestCase
Basket
=
get_model
(
'basket'
,
'Basket'
)
Benefit
=
get_model
(
'offer'
,
'Benefit'
)
Catalog
=
get_model
(
'catalogue'
,
'Catalog'
)
...
...
@@ -33,7 +33,6 @@ VOUCHER_CODE_LENGTH = 1
class
UtilTests
(
CouponMixin
,
CourseCatalogTestMixin
,
LmsApiMockMixin
,
TestCase
):
course_id
=
'edX/DemoX/Demo_Course'
certificate_type
=
'test-certificate-type'
provider
=
None
...
...
@@ -382,10 +381,8 @@ class UtilTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockMixin, TestCase):
for
benefit
in
benefits
:
discount_info
=
get_voucher_discount_info
(
benefit
,
self
.
seat_price
)
if
(
benefit
.
type
==
"Percentage"
and
benefit
.
value
==
100.00
or
benefit
.
type
==
"Absolute"
and
benefit
.
value
==
self
.
seat_price
):
if
(
benefit
.
type
==
"Percentage"
and
benefit
.
value
==
100.00
)
or
\
(
benefit
.
type
==
"Absolute"
and
benefit
.
value
==
self
.
seat_price
):
self
.
assertEqual
(
discount_info
[
'discount_percentage'
],
100.00
)
self
.
assertEqual
(
discount_info
[
'discount_value'
],
100.00
)
self
.
assertFalse
(
discount_info
[
'is_discounted'
])
...
...
ecommerce/extensions/voucher/tests/test_views.py
View file @
9fd6f149
...
...
@@ -3,11 +3,12 @@ from django.test import RequestFactory
from
oscar.core.loading
import
get_model
from
oscar.test
import
factories
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.courses.tests.factories
import
CourseFactory
from
ecommerce.extensions.catalogue.tests.mixins
import
CourseCatalogTestMixin
from
ecommerce.extensions.voucher.views
import
CouponReportCSVView
from
ecommerce.tests.factories
import
PartnerFactory
from
ecommerce.tests.mixins
import
CouponMixin
,
LmsApiMockMixin
from
ecommerce.tests.mixins
import
LmsApiMockMixin
from
ecommerce.tests.testcases
import
TestCase
Basket
=
get_model
(
'basket'
,
'Basket'
)
...
...
ecommerce/tests/mixins.py
View file @
9fd6f149
# -*- coding: utf-8 -*-
"""Broadly-useful mixins for use in automated tests."""
import
datetime
from
decimal
import
Decimal
import
json
from
decimal
import
Decimal
import
httpretty
import
jwt
from
django.conf
import
settings
from
django.contrib.auth
import
get_user_model
from
django.contrib.sites.models
import
Site
from
django.core.cache
import
cache
from
django.core.urlresolvers
import
reverse
from
django.test.client
import
RequestFactory
import
httpretty
import
jwt
from
mock
import
patch
from
oscar.core.loading
import
get_class
,
get_model
from
oscar.test
import
factories
from
threadlocals.threadlocals
import
set_thread_variable
from
social.apps.django_app.default.models
import
UserSocialAuth
from
threadlocals.threadlocals
import
set_thread_variable
from
ecommerce.core.models
import
BusinessClient
from
ecommerce.core.url_utils
import
get_lms_url
from
ecommerce.courses.utils
import
mode_for_seat
from
ecommerce.extensions.api.constants
import
APIConstants
as
AC
from
ecommerce.extensions.api.v2.views.coupons
import
CouponViewSet
from
ecommerce.extensions.basket.utils
import
prepare_basket
from
ecommerce.extensions.fulfillment.signals
import
SHIPPING_EVENT_NAME
from
ecommerce.tests.factories
import
PartnerFactory
,
SiteConfigurationFactory
from
ecommerce.tests.factories
import
SiteConfigurationFactory
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
Basket
=
get_model
(
'basket'
,
'Basket'
)
...
...
@@ -252,7 +248,7 @@ class SiteMixin(object):
domain
=
'testserver.fake'
self
.
client
=
self
.
client_class
(
SERVER_NAME
=
domain
)
Site
.
objects
.
get_current
()
.
delete
()
Site
.
objects
.
all
()
.
delete
()
site_configuration
=
SiteConfigurationFactory
(
partner__name
=
'edX'
,
site__id
=
settings
.
SITE_ID
,
...
...
@@ -313,170 +309,3 @@ class LmsApiMockMixin(object):
course_id
=
course
.
id
if
course
else
'course-v1:test+test+test'
course_url
=
get_lms_url
(
'api/courses/v1/courses/{}/'
.
format
(
course_id
))
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_dynamic_catalog_course_runs_api
(
self
,
course_run
=
None
,
query
=
None
):
""" Helper function to register a dynamic course catalog 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'
:
'test'
,
'title'
:
'Test course'
,
}],
}
course_run_info_json
=
json
.
dumps
(
course_run_info
)
course_run_url
=
'{}course_runs/?q={}'
.
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'
)
def
mock_dynamic_catalog_contains_api
(
self
,
course_run_ids
,
query
):
""" Helper function to register a dynamic course catalog API endpoint for the contains information. """
course_contains_info
=
{
'course_runs'
:
{}
}
for
course_run_id
in
course_run_ids
:
course_contains_info
[
'course_runs'
][
course_run_id
]
=
True
course_run_info_json
=
json
.
dumps
(
course_contains_info
)
course_run_url
=
'{}course_runs/contains/?course_run_ids={}&query={}'
.
format
(
settings
.
COURSE_CATALOG_API_URL
,
(
course_run_id
for
course_run_id
in
course_run_ids
),
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. """
REDEMPTION_URL
=
"/coupons/offer/?code={}"
def
setUp
(
self
):
super
(
CouponMixin
,
self
)
.
setUp
()
self
.
category
=
factories
.
CategoryFactory
()
# Force the creation of a coupon ProductClass
self
.
coupon_product_class
# pylint: disable=pointless-statement
@property
def
coupon_product_class
(
self
):
defaults
=
{
'requires_shipping'
:
False
,
'track_stock'
:
False
,
'name'
:
'Coupon'
}
pc
,
created
=
ProductClass
.
objects
.
get_or_create
(
slug
=
'coupon'
,
defaults
=
defaults
)
if
created
:
factories
.
ProductAttributeFactory
(
code
=
'coupon_vouchers'
,
name
=
'Coupon vouchers'
,
product_class
=
pc
,
type
=
'entity'
)
factories
.
ProductAttributeFactory
(
code
=
'note'
,
name
=
'Note'
,
product_class
=
pc
,
type
=
'text'
)
return
pc
def
create_coupon
(
self
,
title
=
'Test coupon'
,
price
=
100
,
client
=
None
,
partner
=
None
,
catalog
=
None
,
code
=
''
,
benefit_value
=
100
,
note
=
None
,
max_uses
=
None
,
quantity
=
5
,
catalog_query
=
None
,
course_seat_types
=
None
):
"""Helper method for creating a coupon.
Arguments:
title(str): Title of the coupon
price(int): Price of the coupon
partner(Partner): Partner used for creating a catalog
catalog(Catalog): Catalog of courses for which the coupon applies
code(str): Custom coupon code
benefit_value(int): The voucher benefit value
catalog_query(str): course query string
course_seat_types(JSONField): List of seat types
Returns:
coupon (Coupon)
"""
if
partner
is
None
:
partner
=
PartnerFactory
(
name
=
'Tester'
)
if
client
is
None
:
client
,
__
=
BusinessClient
.
objects
.
get_or_create
(
name
=
'Test Client'
)
if
catalog
is
None
and
not
(
catalog_query
and
course_seat_types
):
catalog
=
Catalog
.
objects
.
create
(
partner
=
partner
)
if
code
is
not
''
:
quantity
=
1
data
=
{
'partner'
:
partner
,
'benefit_type'
:
Benefit
.
PERCENTAGE
,
'benefit_value'
:
benefit_value
,
'catalog'
:
catalog
,
'end_date'
:
datetime
.
date
(
2020
,
1
,
1
),
'code'
:
code
,
'quantity'
:
quantity
,
'start_date'
:
datetime
.
date
(
2015
,
1
,
1
),
'voucher_type'
:
Voucher
.
SINGLE_USE
,
'categories'
:
[
self
.
category
],
'note'
:
note
,
'max_uses'
:
max_uses
,
'catalog_query'
:
catalog_query
,
'course_seat_types'
:
course_seat_types
,
}
coupon
=
CouponViewSet
()
.
create_coupon_product
(
title
=
title
,
price
=
price
,
data
=
data
)
request
=
RequestFactory
()
request
.
site
=
self
.
site
request
.
user
=
factories
.
UserFactory
()
request
.
COOKIES
=
{}
self
.
basket
=
prepare_basket
(
request
,
coupon
)
self
.
response_data
=
CouponViewSet
()
.
create_order_for_invoice
(
self
.
basket
,
coupon_id
=
coupon
.
id
,
client
=
client
)
coupon
.
client
=
client
return
coupon
def
apply_voucher
(
self
,
user
,
site
,
voucher
):
""" Apply the voucher to a basket. """
basket
=
factories
.
BasketFactory
(
owner
=
user
,
site
=
site
)
product
=
voucher
.
offers
.
first
()
.
benefit
.
range
.
all_products
()[
0
]
basket
.
add_product
(
product
)
basket
.
vouchers
.
add
(
voucher
)
Applicator
()
.
apply
(
basket
,
self
.
user
)
return
basket
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