Commit 9fd6f149 by Clinton Blackburn

Moved coupons-related mixins to appropriate module (#764)

parent 79ad83c9
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
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:*'
......
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)
......
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()
......
......@@ -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')
......
......@@ -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)
......
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."""
......
......@@ -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)
......
......@@ -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,
......
......@@ -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')
......
"""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'
......
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')
......
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'])
......
......@@ -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')
......
# -*- 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment