Commit 3ce7d773 by Vedran Karacic

Omit unavailable seats in offer landing page.

parent af166e02
...@@ -19,22 +19,23 @@ class CatalogPreviewMockMixin(object): ...@@ -19,22 +19,23 @@ class CatalogPreviewMockMixin(object):
def setUp(self): def setUp(self):
super(CatalogPreviewMockMixin, self).setUp() super(CatalogPreviewMockMixin, self).setUp()
def mock_dynamic_catalog_course_runs_api(self, course_run=None, query=None): def mock_dynamic_catalog_course_runs_api(self, course_run=None, query=None, course_run_info=None):
""" Helper function to register a dynamic course catalog API endpoint for the course run information. """ """ Helper function to register a dynamic course catalog API endpoint for the course run information. """
course_run_info = { if not course_run_info:
'count': 1, course_run_info = {
'results': [{ 'count': 1,
'key': course_run.id, 'results': [{
'title': course_run.name, 'key': course_run.id,
'start': '2016-05-01T00:00:00Z', 'title': course_run.name,
'image': { 'start': '2016-05-01T00:00:00Z',
'src': 'path/to/the/course/image' 'image': {
} 'src': 'path/to/the/course/image'
}] if course_run else [{ }
'key': 'test', }] if course_run else [{
'title': 'Test course', 'key': 'test',
}], 'title': 'Test course',
} }],
}
course_run_info_json = json.dumps(course_run_info) course_run_info_json = json.dumps(course_run_info)
course_run_url = '{}course_runs/?q={}'.format( course_run_url = '{}course_runs/?q={}'.format(
settings.COURSE_CATALOG_API_URL, settings.COURSE_CATALOG_API_URL,
......
...@@ -64,7 +64,7 @@ class CouponAppViewTests(TestCase): ...@@ -64,7 +64,7 @@ class CouponAppViewTests(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
class GetVoucherTests(TestCase): class GetVoucherTests(CourseCatalogTestMixin, TestCase):
def test_get_voucher_and_products_from_code(self): def test_get_voucher_and_products_from_code(self):
""" Verify that get_voucher_and_products_from_code() returns products and voucher. """ """ Verify that get_voucher_and_products_from_code() returns products and voucher. """
original_voucher, original_product = prepare_voucher(code=COUPON_CODE) original_voucher, original_product = prepare_voucher(code=COUPON_CODE)
...@@ -130,6 +130,15 @@ class GetVoucherTests(TestCase): ...@@ -130,6 +130,15 @@ class GetVoucherTests(TestCase):
valid, __ = voucher_is_valid(voucher=voucher, products=[product], request=request) valid, __ = voucher_is_valid(voucher=voucher, products=[product], request=request)
self.assertFalse(valid) self.assertFalse(valid)
def test_omitting_unavailable_voucher(self):
""" Verify if there are more than one product, that availability check is omitted. """
request = RequestFactory().request()
voucher, product = prepare_voucher(code=COUPON_CODE)
product.expires = pytz.utc.localize(datetime.datetime.min)
__, seat = self.create_course_and_seat()
valid, __ = voucher_is_valid(voucher=voucher, products=[product, seat], request=request)
self.assertTrue(valid)
def assert_error_messages(self, voucher, product, user, error_msg): def assert_error_messages(self, voucher, product, user, error_msg):
""" Assert the proper error message is returned. """ """ Assert the proper error message is returned. """
voucher.offers.first().record_usage(discount={'freq': 1, 'discount': 1}) voucher.offers.first().record_usage(discount={'freq': 1, 'discount': 1})
......
...@@ -90,10 +90,10 @@ def voucher_is_valid(voucher, products, request): ...@@ -90,10 +90,10 @@ def voucher_is_valid(voucher, products, request):
voucher_msg = msg.replace('voucher', 'coupon') voucher_msg = msg.replace('voucher', 'coupon')
return False, voucher_msg return False, voucher_msg
for product in products: if len(products) == 1:
purchase_info = request.strategy.fetch_for_product(product) purchase_info = request.strategy.fetch_for_product(products[0])
if not purchase_info.availability.is_available_to_buy: if not purchase_info.availability.is_available_to_buy:
return False, _('Product [{product}] not available for purchase.'.format(product=product)) return False, _('Product [{product}] not available for purchase.'.format(product=products[0]))
# If the voucher's number of applications exceeds it's limit. # If the voucher's number of applications exceeds it's limit.
offer = voucher.offers.first() offer = voucher.offers.first()
......
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
import json import json
import mock import mock
import ddt import ddt
import httpretty import httpretty
import pytz
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import Http404 from django.http import Http404
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
...@@ -22,6 +24,7 @@ from ecommerce.courses.models import Course ...@@ -22,6 +24,7 @@ from ecommerce.courses.models import Course
from ecommerce.extensions.api import serializers from ecommerce.extensions.api import serializers
from ecommerce.extensions.api.v2.views.vouchers import VoucherViewSet from ecommerce.extensions.api.v2.views.vouchers import VoucherViewSet
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.partner.strategy import DefaultStrategy
from ecommerce.extensions.test.factories import prepare_voucher from ecommerce.extensions.test.factories import prepare_voucher
from ecommerce.tests.mixins import Catalog, LmsApiMockMixin from ecommerce.tests.mixins import Catalog, LmsApiMockMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
...@@ -33,7 +36,7 @@ Range = get_model('offer', 'Range') ...@@ -33,7 +36,7 @@ Range = get_model('offer', 'Range')
StockRecord = get_model('partner', 'StockRecord') StockRecord = get_model('partner', 'StockRecord')
class VoucherViewSetTests(TestCase): class VoucherViewSetTests(CatalogPreviewMockMixin, CourseCatalogTestMixin, TestCase):
""" Tests for the VoucherViewSet view set. """ """ Tests for the VoucherViewSet view set. """
path = reverse('api:v2:vouchers-list') path = reverse('api:v2:vouchers-list')
...@@ -63,6 +66,50 @@ class VoucherViewSetTests(TestCase): ...@@ -63,6 +66,50 @@ class VoucherViewSetTests(TestCase):
self.assertEqual(response_data['count'], 1) self.assertEqual(response_data['count'], 1)
self.assertEqual(response_data['results'][0]['code'], COUPON_CODE) self.assertEqual(response_data['results'][0]['code'], COUPON_CODE)
# NOTE (VK): This unit test is added here because it results in a segmentation fault if
# added to the test class below.
@httpretty.activate
@mock_course_catalog_api_client
def test_omitting_unavailable_seats(self):
""" Verify an unavailable seat is omitted from offer page results. """
course1, seat1 = self.create_course_and_seat()
course2, seat2 = self.create_course_and_seat()
course_run_info = {
'count': 2,
'results': [{
'key': course1.id,
'title': course1.name,
'start': '2016-05-01T00:00:00Z',
'image': {
'src': 'path/to/the/course/image'
}
}, {
'key': course2.id,
'title': course2.name,
'start': '2016-05-01T00:00:00Z',
'image': {
'src': 'path/to/the/course/image'
}
}]
}
self.mock_dynamic_catalog_course_runs_api(query='*:*', course_run_info=course_run_info)
new_range, __ = Range.objects.get_or_create(catalog_query='*:*')
new_range.add_product(seat1)
new_range.add_product(seat2)
voucher, __ = prepare_voucher(_range=new_range)
voucher, products = get_voucher_and_products_from_code(voucher.code)
factory = APIRequestFactory()
request = factory.get('/?code={}&page_size=6'.format(voucher.code))
request.site = self.site
request.strategy = DefaultStrategy()
offers = VoucherViewSet().get_offers(products=products, request=request, voucher=voucher)
self.assertEqual(len(offers), 2)
products[1].expires = pytz.utc.localize(datetime.datetime.min)
offers = VoucherViewSet().get_offers(products=products, request=request, voucher=voucher)
self.assertEqual(len(offers), 1)
@ddt.ddt @ddt.ddt
@httpretty.activate @httpretty.activate
...@@ -86,6 +133,7 @@ class VoucherViewOffersEndpointTests( ...@@ -86,6 +133,7 @@ class VoucherViewOffersEndpointTests(
factory = APIRequestFactory() factory = APIRequestFactory()
request = factory.get('/?code={}&page_size=6'.format(code)) request = factory.get('/?code={}&page_size=6'.format(code))
request.site = self.site request.site = self.site
request.strategy = DefaultStrategy()
return request return request
@ddt.data(('COUPONCODE',), ('NOT_FOUND_CODE',)) @ddt.data(('COUPONCODE',), ('NOT_FOUND_CODE',))
......
...@@ -110,7 +110,6 @@ class VoucherViewSet(NonDestroyableModelViewSet): ...@@ -110,7 +110,6 @@ class VoucherViewSet(NonDestroyableModelViewSet):
benefit = voucher.offers.first().benefit benefit = voucher.offers.first().benefit
catalog_query = benefit.range.catalog_query catalog_query = benefit.range.catalog_query
offers = [] offers = []
if catalog_query: if catalog_query:
query_results = request.site.siteconfiguration.course_catalog_api_client.course_runs.get( query_results = request.site.siteconfiguration.course_catalog_api_client.course_runs.get(
q=catalog_query, q=catalog_query,
...@@ -124,6 +123,11 @@ class VoucherViewSet(NonDestroyableModelViewSet): ...@@ -124,6 +123,11 @@ class VoucherViewSet(NonDestroyableModelViewSet):
contains_verified_course = (benefit.range.course_seat_types == 'verified') contains_verified_course = (benefit.range.course_seat_types == 'verified')
for product in products: for product in products:
# Omit unavailable seats from the offer results so that one seat does not cause an
# error message for every seat in the query result.
if not request.strategy.fetch_for_product(product).availability.is_available_to_buy:
logger.info('%s is unavailable to buy. Omitting it from the results.', product)
continue
course_id = product.course_id course_id = product.course_id
course_catalog_data = next((result for result in query_results if result['key'] == course_id), None) course_catalog_data = next((result for result in query_results if result['key'] == course_id), None)
......
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