Commit e208386e by zubair-arbi Committed by Zubair Afzal

Update offer's Range model to use course catalog query for applying enterprise coupon

ENT-169
parent ca89f929
...@@ -89,8 +89,8 @@ class CourseCatalogMockMixin(object): ...@@ -89,8 +89,8 @@ class CourseCatalogMockMixin(object):
course_run_url_with_query_and_partner_code = '{}course_runs/?q={}&partner={}'.format( course_run_url_with_query_and_partner_code = '{}course_runs/?q={}&partner={}'.format(
settings.COURSE_CATALOG_API_URL, settings.COURSE_CATALOG_API_URL,
partner_code if partner_code else 'edx', query if query else 'id:course*',
query if query else 'id:course*' partner_code if partner_code else 'edx'
) )
httpretty.register_uri( httpretty.register_uri(
httpretty.GET, httpretty.GET,
...@@ -129,6 +129,49 @@ class CourseCatalogMockMixin(object): ...@@ -129,6 +129,49 @@ class CourseCatalogMockMixin(object):
content_type='application/json' content_type='application/json'
) )
def mock_get_catalog_contains_api_for_failure(self, partner_code, course_run_ids, query, error):
"""
Helper function to register a course catalog API endpoint with failure
for getting course runs information.
"""
def callback(request, uri, headers): # pylint: disable=unused-argument
raise error
catalog_contains_course_run_url = '{}course_runs/contains/?course_run_ids={}&query={}&partner={}'.format(
settings.COURSE_CATALOG_API_URL,
(course_run_id for course_run_id in course_run_ids),
query,
partner_code,
)
httpretty.register_uri(
method=httpretty.GET,
uri=catalog_contains_course_run_url,
responses=[
httpretty.Response(body=callback, content_type='application/json', status_code=500)
]
)
def mock_get_catalog_course_runs_for_failure(self, partner_code, query, error):
"""
Helper function to register a course catalog API endpoint with failure
for getting course runs information.
"""
def callback(request, uri, headers): # pylint: disable=unused-argument
raise error
course_run_url_with_query_and_partner_code = '{}course_runs/?q={}&partner={}'.format(
settings.COURSE_CATALOG_API_URL,
query,
partner_code,
)
httpretty.register_uri(
method=httpretty.GET,
uri=course_run_url_with_query_and_partner_code,
responses=[
httpretty.Response(body=callback, content_type='application/json', status_code=500)
]
)
class CouponMixin(object): class CouponMixin(object):
""" Mixin for preparing data for coupons and creating coupons. """ """ Mixin for preparing data for coupons and creating coupons. """
......
...@@ -17,12 +17,11 @@ class CourseCatalogServiceMockMixin(object): ...@@ -17,12 +17,11 @@ class CourseCatalogServiceMockMixin(object):
super(CourseCatalogServiceMockMixin, self).setUp() super(CourseCatalogServiceMockMixin, self).setUp()
cache.clear() cache.clear()
def mock_course_discovery_api_for_catalog_by_resource_id(self, catalog_query='title: *'): def mock_course_discovery_api_for_catalog_by_resource_id(self, catalog_id=1, catalog_query='title: *'):
""" """
Helper function to register course catalog API endpoint for a Helper function to register course catalog API endpoint for a
single catalog with its resource id. single catalog with its resource id.
""" """
catalog_id = 1
course_discovery_api_response = { course_discovery_api_response = {
'id': catalog_id, 'id': catalog_id,
'name': 'Catalog {}'.format(catalog_id), 'name': 'Catalog {}'.format(catalog_id),
...@@ -122,15 +121,23 @@ class CourseCatalogServiceMockMixin(object): ...@@ -122,15 +121,23 @@ class CourseCatalogServiceMockMixin(object):
responses=mocked_api_responses responses=mocked_api_responses
) )
def mock_course_discovery_api_for_failure(self): def mock_course_discovery_api_for_catalogs_with_failure(self, error, catalog_id=None):
""" """
Helper function to register course catalog API endpoint for a Helper function to register course catalog API endpoint for catalogs
failure. with failure.
""" """
def callback(request, uri, headers): # pylint: disable=unused-argument
raise error
if catalog_id:
course_catalog_uri = '{}{}/'.format(self.COURSE_DISCOVERY_CATALOGS_URL, catalog_id)
else:
course_catalog_uri = self.COURSE_DISCOVERY_CATALOGS_URL
httpretty.register_uri( httpretty.register_uri(
method=httpretty.GET, method=httpretty.GET,
uri=self.COURSE_DISCOVERY_CATALOGS_URL, uri=course_catalog_uri,
responses=[ responses=[
httpretty.Response(body='Clunk', content_type='application/json', status_code=500) httpretty.Response(body=callback, content_type='application/json', status_code=500)
] ]
) )
...@@ -3,6 +3,7 @@ import hashlib ...@@ -3,6 +3,7 @@ import hashlib
import ddt import ddt
from django.core.cache import cache from django.core.cache import cache
import httpretty import httpretty
from requests.exceptions import ConnectionError
from ecommerce.core.constants import ENROLLMENT_CODE_SWITCH from ecommerce.core.constants import ENROLLMENT_CODE_SWITCH
from ecommerce.core.tests import toggle_switch from ecommerce.core.tests import toggle_switch
...@@ -175,7 +176,8 @@ class GetCourseCatalogUtilTests(CourseCatalogServiceMockMixin, TestCase): ...@@ -175,7 +176,8 @@ class GetCourseCatalogUtilTests(CourseCatalogServiceMockMixin, TestCase):
Verify that method "get_course_catalogs" raises exception in case Verify that method "get_course_catalogs" raises exception in case
the Course Discovery API fails to return data. the Course Discovery API fails to return data.
""" """
self.mock_course_discovery_api_for_failure() exception = ConnectionError
self.mock_course_discovery_api_for_catalogs_with_failure(exception)
with self.assertRaises(Exception): with self.assertRaises(exception):
get_course_catalogs(self.request.site) get_course_catalogs(self.request.site)
...@@ -16,11 +16,10 @@ from oscar.core.loading import get_model ...@@ -16,11 +16,10 @@ from oscar.core.loading import get_model
from requests.exceptions import ConnectionError, Timeout from requests.exceptions import ConnectionError, Timeout
from slumber.exceptions import SlumberBaseException from slumber.exceptions import SlumberBaseException
from ecommerce.coupons.utils import get_catalog_course_runs
from ecommerce.coupons.views import voucher_is_valid from ecommerce.coupons.views import voucher_is_valid
from ecommerce.courses.utils import get_course_catalogs from ecommerce.courses.utils import get_course_catalogs
from ecommerce.enterprise.utils import is_enterprise_feature_enabled from ecommerce.enterprise.utils import is_enterprise_feature_enabled
from ecommerce.extensions.api.serializers import retrieve_voucher from ecommerce.extensions.api.serializers import retrieve_all_vouchers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -34,28 +33,29 @@ def get_entitlement_voucher(request, product): ...@@ -34,28 +33,29 @@ def get_entitlement_voucher(request, product):
learner. learner.
Arguments: Arguments:
request (HttpRequest): request with voucher data product (Product): A product that has course_id as attribute (seat or
product (Product): A product that has course_key as attribute (seat or
bulk enrollment coupon) bulk enrollment coupon)
request (HttpRequest): request with voucher data
""" """
if not is_enterprise_feature_enabled(): if not is_enterprise_feature_enabled():
return None return None
vouchers = get_vouchers_for_learner(request.site, request.user) vouchers = get_course_vouchers_for_learner(request.site, request.user, product.course_id)
if vouchers: if not vouchers:
entitlement_voucher = get_available_voucher_for_product(request, product, vouchers) return None
return entitlement_voucher
return None entitlement_voucher = get_available_voucher_for_product(request, product, vouchers)
return entitlement_voucher
def get_vouchers_for_learner(site, user): def get_course_vouchers_for_learner(site, user, course_id):
""" """
Get vouchers against the list of all enterprise entitlements for the Get vouchers against the list of all enterprise entitlements for the
provided learner. provided learner and course id.
Arguments: Arguments:
course_id (str): The course ID.
site: (django.contrib.sites.Site) site instance site: (django.contrib.sites.Site) site instance
user: (django.contrib.auth.User) django auth user user: (django.contrib.auth.User) django auth user
...@@ -63,7 +63,7 @@ def get_vouchers_for_learner(site, user): ...@@ -63,7 +63,7 @@ def get_vouchers_for_learner(site, user):
list of Voucher class objects list of Voucher class objects
""" """
entitlements = get_entitlements_for_learner(site, user) entitlements = get_course_entitlements_for_learner(site, user, course_id)
if not entitlements: if not entitlements:
return None return None
...@@ -78,18 +78,19 @@ def get_vouchers_for_learner(site, user): ...@@ -78,18 +78,19 @@ def get_vouchers_for_learner(site, user):
) )
return None return None
entitlement_voucher = retrieve_voucher(coupon_product) entitlement_voucher = retrieve_all_vouchers(coupon_product)
vouchers.append(entitlement_voucher) vouchers.extend(entitlement_voucher)
return vouchers return vouchers
def get_entitlements_for_learner(site, user): def get_course_entitlements_for_learner(site, user, course_id):
""" """
Get entitlements for the provided learner if the provided learner is Get entitlements for the provided learner against the provided course id
affiliated with an enterprise. if the provided learner is affiliated with an enterprise.
Arguments: Arguments:
course_id (str): The course ID.
site: (django.contrib.sites.Site) site instance site: (django.contrib.sites.Site) site instance
user: (django.contrib.auth.User) django auth user user: (django.contrib.auth.User) django auth user
...@@ -108,14 +109,90 @@ def get_entitlements_for_learner(site, user): ...@@ -108,14 +109,90 @@ def get_entitlements_for_learner(site, user):
return None return None
try: try:
enterprise_catalog_id = enterprise_learner_data[0]['enterprise_customer']['catalog']
entitlements = enterprise_learner_data[0]['enterprise_customer']['enterprise_customer_entitlements'] entitlements = enterprise_learner_data[0]['enterprise_customer']['enterprise_customer_entitlements']
except KeyError: except KeyError:
logger.error('Invalid structure for enterprise learner API response for the learner [%s]', user.username) logger.exception('Invalid structure for enterprise learner API response for the learner [%s]', user.username)
return None
# Before returning entitlements verify that the provided course exists in
# the enterprise course catalog
if not is_course_in_enterprise_catalog(site, course_id, enterprise_catalog_id):
return None return None
return entitlements return entitlements
def is_course_in_enterprise_catalog(site, course_id, enterprise_catalog_id):
"""
Verify that the provided course id exists in the site base list of course
run keys from the provided enterprise course catalog.
Arguments:
course_id (str): The course ID.
site: (django.contrib.sites.Site) site instance
enterprise_catalog_id (Int): Course catalog id of enterprise
Returns:
Boolean
"""
try:
enterprise_course_catalog = get_course_catalogs(site=site, resource_id=enterprise_catalog_id)
except (ConnectionError, SlumberBaseException, Timeout):
logger.exception('Unable to connect to Course Catalog service for course catalogs.')
return None
if is_course_in_catalog_query(site, course_id, enterprise_course_catalog.get('query')):
return True
return False
def is_course_in_catalog_query(site, course_id, enterprise_catalog_query):
"""
Find out if the provided course exists in list of courses against the
enterprise course catalog query.
Arguments:
site: (django.contrib.sites.Site) site instance
course_id (Int): Course catalog id of enterprise
enterprise_catalog_query (Str): Enterprise course catalog query
Returns:
Boolean
"""
partner_code = site.siteconfiguration.partner.short_code
cache_key = hashlib.md5(
'{site_domain}_{partner_code}_catalog_query_contains_{course_id}_{query}'.format(
site_domain=site.domain,
partner_code=partner_code,
course_id=course_id,
query=enterprise_catalog_query
)
).hexdigest()
response = cache.get(cache_key)
if not response:
try:
response = site.siteconfiguration.course_catalog_api_client.course_runs.contains.get(
query=enterprise_catalog_query,
course_run_ids=course_id,
partner=partner_code
)
cache.set(cache_key, response, settings.COURSES_API_CACHE_TIMEOUT)
except (ConnectionError, SlumberBaseException, Timeout):
logger.exception('Unable to connect to Course Catalog service for course runs.')
return False
try:
is_course_in_course_runs = response['course_runs'][course_id]
except KeyError:
return False
return is_course_in_course_runs
def get_enterprise_learner_data(site, user): def get_enterprise_learner_data(site, user):
""" """
Fetch information related to enterprise and its entitlements according to Fetch information related to enterprise and its entitlements according to
...@@ -220,7 +297,7 @@ def get_available_voucher_for_product(request, product, vouchers): ...@@ -220,7 +297,7 @@ def get_available_voucher_for_product(request, product, vouchers):
product. product.
Arguments: Arguments:
product (Product): A product that has course_key as attribute (seat or product (Product): A product that has course_id as attribute (seat or
bulk enrollment coupon) bulk enrollment coupon)
request (HttpRequest): request with voucher data request (HttpRequest): request with voucher data
vouchers: (List) List of voucher class objects for an enterprise vouchers: (List) List of voucher class objects for an enterprise
...@@ -229,50 +306,10 @@ def get_available_voucher_for_product(request, product, vouchers): ...@@ -229,50 +306,10 @@ def get_available_voucher_for_product(request, product, vouchers):
for voucher in vouchers: for voucher in vouchers:
is_valid_voucher, __ = voucher_is_valid(voucher, [product], request) is_valid_voucher, __ = voucher_is_valid(voucher, [product], request)
if is_valid_voucher: if is_valid_voucher:
voucher_course_ids = get_course_ids_from_voucher(request.site, voucher) voucher_offer = voucher.offers.first()
if product.course_id in voucher_course_ids: offer_range = voucher_offer.condition.range
if offer_range.contains_product(product):
return voucher return voucher
# Explicitly return None in case product has no valid voucher
def get_course_ids_from_voucher(site, voucher): return None
"""
Get site base list of course run keys from the provided voucher object.
Arguments:
site: (django.contrib.sites.Site) site instance
voucher (Voucher): voucher class object
Returns:
list of course ids
"""
voucher_offer = voucher.offers.first()
offer_range = voucher_offer.condition.range
if offer_range.course_catalog:
try:
course_catalog = get_course_catalogs(site=site, resource_id=offer_range.course_catalog)
except (ConnectionError, SlumberBaseException, Timeout):
logger.error('Unable to connect to Course Catalog service for course catalogs.')
return None
try:
course_runs = get_catalog_course_runs(site, course_catalog.get('query'))['results']
except (ConnectionError, SlumberBaseException, Timeout, KeyError):
logger.error('Unable to get course runs from Course Catalog service.')
return None
voucher_course_ids = [course_run.get('key') for course_run in course_runs if course_run.get('key')]
elif offer_range.catalog_query:
try:
course_runs = get_catalog_course_runs(site, offer_range.catalog_query)['results']
except (ConnectionError, SlumberBaseException, Timeout, KeyError):
logger.error('Unable to get course runs from Course Catalog service.')
return None
voucher_course_ids = [course_run.get('key') for course_run in course_runs if course_run.get('key')]
else:
stock_records = offer_range.catalog.stock_records.all()
seats = Product.objects.filter(id__in=[sr.product.id for sr in stock_records])
voucher_course_ids = [seat.course_id for seat in seats]
return voucher_course_ids
...@@ -110,7 +110,7 @@ class EnterpriseServiceMockMixin(object): ...@@ -110,7 +110,7 @@ class EnterpriseServiceMockMixin(object):
def mock_enterprise_learner_api_for_learner_with_invalid_response(self): def mock_enterprise_learner_api_for_learner_with_invalid_response(self):
""" """
Helper function to register enterprise learner API endpoint for a Helper function to register enterprise learner API endpoint for a
learner with invalid API reponse structure. learner with invalid API response structure.
""" """
enterprise_learner_api_response = { enterprise_learner_api_response = {
'count': 0, 'count': 0,
...@@ -151,6 +151,49 @@ class EnterpriseServiceMockMixin(object): ...@@ -151,6 +151,49 @@ class EnterpriseServiceMockMixin(object):
content_type='application/json' content_type='application/json'
) )
def mock_enterprise_learner_api_for_learner_with_invalid_entitlements_response(self):
"""
Helper function to register enterprise learner API endpoint for a
learner with partial invalid API response structure for the enterprise
customer entitlements.
"""
enterprise_learner_api_response = {
'count': 0,
'num_pages': 1,
'current_page': 1,
'results': [
{
'enterprise_customer': {
'uuid': 'cf246b88-d5f6-4908-a522-fc307e0b0c59',
'name': 'TestShib',
'catalog': 1,
'active': True,
'site': {
'domain': 'example.com',
'name': 'example.com'
},
'invalid-unexpected-enterprise_customer_entitlements-key': [
{
'enterprise_customer': 'cf246b88-d5f6-4908-a522-fc307e0b0c59',
'entitlement_id': 1
}
]
}
}
],
'next': None,
'start': 0,
'previous': None
}
enterprise_learner_api_response_json = json.dumps(enterprise_learner_api_response)
httpretty.register_uri(
method=httpretty.GET,
uri=self.ENTERPRISE_LEARNER_URL,
body=enterprise_learner_api_response_json,
content_type='application/json'
)
def mock_enterprise_learner_api_for_failure(self): def mock_enterprise_learner_api_for_failure(self):
""" """
Helper function to register enterprise learner API endpoint for a Helper function to register enterprise learner API endpoint for a
......
...@@ -83,6 +83,11 @@ def retrieve_voucher(obj): ...@@ -83,6 +83,11 @@ def retrieve_voucher(obj):
return obj.attr.coupon_vouchers.vouchers.first() return obj.attr.coupon_vouchers.vouchers.first()
def retrieve_all_vouchers(obj):
"""Helper method to retrieve all vouchers from coupon. """
return obj.attr.coupon_vouchers.vouchers.all()
def retrieve_voucher_usage(obj): def retrieve_voucher_usage(obj):
"""Helper method to retrieve usage from voucher. """ """Helper method to retrieve usage from voucher. """
return retrieve_voucher(obj).usage return retrieve_voucher(obj).usage
......
...@@ -179,7 +179,7 @@ class CatalogViewSetTest(CatalogMixin, CourseCatalogMockMixin, CourseCatalogServ ...@@ -179,7 +179,7 @@ class CatalogViewSetTest(CatalogMixin, CourseCatalogMockMixin, CourseCatalogServ
empty results list in case the Course Discovery API fails to return empty results list in case the Course Discovery API fails to return
data. data.
""" """
self.mock_course_discovery_api_for_failure() self.mock_course_discovery_api_for_catalogs_with_failure(ConnectionError)
request = self.prepare_request('/api/v2/coupons/course_catalogs/') request = self.prepare_request('/api/v2/coupons/course_catalogs/')
response = CatalogViewSet().course_catalogs(request) response = CatalogViewSet().course_catalogs(request)
......
...@@ -7,9 +7,12 @@ from django.core.cache import cache ...@@ -7,9 +7,12 @@ from django.core.cache import cache
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from oscar.apps.offer.abstract_models import AbstractBenefit, AbstractConditionalOffer, AbstractRange from oscar.apps.offer.abstract_models import AbstractBenefit, AbstractConditionalOffer, AbstractRange
from requests.exceptions import ConnectionError, Timeout
from slumber.exceptions import SlumberBaseException
from threadlocals.threadlocals import get_current_request from threadlocals.threadlocals import get_current_request
from ecommerce.core.utils import log_message_and_raise_validation_error from ecommerce.core.utils import log_message_and_raise_validation_error
from ecommerce.courses.utils import get_course_catalogs
class Benefit(AbstractBenefit): class Benefit(AbstractBenefit):
...@@ -220,21 +223,30 @@ class Range(AbstractRange): ...@@ -220,21 +223,30 @@ class Range(AbstractRange):
if self.course_seat_types: if self.course_seat_types:
validate_credit_seat_type(self.course_seat_types) validate_credit_seat_type(self.course_seat_types)
def run_catalog_query(self, product): def run_catalog_query(self, product, query=None):
""" """
Retrieve the results from running the query contained in catalog_query field. Retrieve the results from running the query contained in catalog_query field.
""" """
if not query:
query = self.catalog_query
request = get_current_request()
partner_code = request.site.siteconfiguration.partner.short_code
cache_key = hashlib.md5( cache_key = hashlib.md5(
'catalog_query_contains [{}] [{}]'.format(self.catalog_query, product.course_id) '{site_domain}_{partner_code}_catalog_query_contains_{course_id}_{query}'.format(
site_domain=request.site.domain,
partner_code=partner_code,
course_id=product.course_id,
query=query
)
).hexdigest() ).hexdigest()
response = cache.get(cache_key) response = cache.get(cache_key)
if not response: # pragma: no cover if not response: # pragma: no cover
request = get_current_request()
try: try:
response = request.site.siteconfiguration.course_catalog_api_client.course_runs.contains.get( response = request.site.siteconfiguration.course_catalog_api_client.course_runs.contains.get(
query=self.catalog_query, query=query,
course_run_ids=product.course_id, course_run_ids=product.course_id,
partner=request.site.siteconfiguration.partner.short_code partner=partner_code
) )
cache.set(cache_key, response, settings.COURSES_API_CACHE_TIMEOUT) cache.set(cache_key, response, settings.COURSES_API_CACHE_TIMEOUT)
except: # pylint: disable=bare-except except: # pylint: disable=bare-except
...@@ -246,7 +258,21 @@ class Range(AbstractRange): ...@@ -246,7 +258,21 @@ class Range(AbstractRange):
""" """
Assert if the range contains the product. Assert if the range contains the product.
""" """
if self.catalog_query and self.course_seat_types: if self.course_catalog:
request = get_current_request()
try:
course_catalog = get_course_catalogs(site=request.site, resource_id=self.course_catalog)
except (ConnectionError, SlumberBaseException, Timeout):
raise Exception(
'Unable to connect to Course Catalog service for catalog with id [%s].' % self.course_catalog
)
response = self.run_catalog_query(product, course_catalog.get('query'))
# Range can have a catalog query and 'regular' products in it,
# therefor an OR is used to check for both possibilities.
return ((response['course_runs'][product.course_id]) or
super(Range, self).contains_product(product)) # pylint: disable=bad-super-call
elif self.catalog_query and self.course_seat_types:
if product.attr.certificate_type.lower() in self.course_seat_types: # pylint: disable=unsupported-membership-test if product.attr.certificate_type.lower() in self.course_seat_types: # pylint: disable=unsupported-membership-test
response = self.run_catalog_query(product) response = self.run_catalog_query(product)
# Range can have a catalog query and 'regular' products in it, # Range can have a catalog query and 'regular' products in it,
......
...@@ -13,16 +13,18 @@ from oscar.test import factories ...@@ -13,16 +13,18 @@ from oscar.test import factories
from ecommerce.core.tests.decorators import mock_course_catalog_api_client from ecommerce.core.tests.decorators import mock_course_catalog_api_client
from ecommerce.coupons.tests.mixins import CourseCatalogMockMixin, CouponMixin from ecommerce.coupons.tests.mixins import CourseCatalogMockMixin, CouponMixin
from ecommerce.courses.tests.mixins import CourseCatalogServiceMockMixin
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
Catalog = get_model('catalogue', 'Catalog') Catalog = get_model('catalogue', 'Catalog')
ConditionalOffer = get_model('offer', 'ConditionalOffer') ConditionalOffer = get_model('offer', 'ConditionalOffer')
Range = get_model('offer', 'Range') Range = get_model('offer', 'Range')
@ddt.ddt @ddt.ddt
class RangeTests(CouponMixin, CourseCatalogTestMixin, CourseCatalogMockMixin, TestCase): class RangeTests(CouponMixin, CourseCatalogServiceMockMixin, CourseCatalogTestMixin, CourseCatalogMockMixin, TestCase):
def setUp(self): def setUp(self):
super(RangeTests, self).setUp() super(RangeTests, self).setUp()
...@@ -100,7 +102,15 @@ class RangeTests(CouponMixin, CourseCatalogTestMixin, CourseCatalogMockMixin, Te ...@@ -100,7 +102,15 @@ class RangeTests(CouponMixin, CourseCatalogTestMixin, CourseCatalogMockMixin, Te
request.site = self.site request.site = self.site
self.range.catalog_query = 'key:*' self.range.catalog_query = 'key:*'
cache_key = hashlib.md5('catalog_query_contains [{}] [{}]'.format('key:*', seat.course_id)).hexdigest() partner_code = request.site.siteconfiguration.partner.short_code
cache_key = hashlib.md5(
'{site_domain}_{partner_code}_catalog_query_contains_{course_id}_{query}'.format(
site_domain=request.site.domain,
partner_code=partner_code,
course_id=seat.course_id,
query=self.range.catalog_query
)
).hexdigest()
cached_response = cache.get(cache_key) cached_response = cache.get(cache_key)
self.assertIsNone(cached_response) self.assertIsNone(cached_response)
...@@ -129,6 +139,54 @@ class RangeTests(CouponMixin, CourseCatalogTestMixin, CourseCatalogMockMixin, Te ...@@ -129,6 +139,54 @@ class RangeTests(CouponMixin, CourseCatalogTestMixin, CourseCatalogMockMixin, Te
@httpretty.activate @httpretty.activate
@mock_course_catalog_api_client @mock_course_catalog_api_client
def test_course_catalog_query_range_contains_product(self):
"""
Verify that the method "contains_product" returns True (boolean) if a
product is in it's range for a course catalog Range.
"""
catalog_query = 'key:*'
course, seat = self.create_course_and_seat()
self.mock_dynamic_catalog_contains_api(query=catalog_query, course_run_ids=[course.id])
false_response = self.range.contains_product(seat)
self.assertFalse(false_response)
course_catalog_id = 1
self.mock_course_discovery_api_for_catalog_by_resource_id(
catalog_id=course_catalog_id, catalog_query=catalog_query
)
self.range.catalog_query = None
self.range.course_seat_types = None
self.range.course_catalog = course_catalog_id
self.range.save()
response = self.range.contains_product(seat)
self.assertTrue(response)
@httpretty.activate
@mock_course_catalog_api_client
def test_course_catalog_query_range_contains_product_for_failure(self):
"""
Verify that the method "contains_product" raises exception if the
method "get_course_catalogs" is unable to get the catalog from course
catalog service for a course catalog Range.
"""
__, seat = self.create_course_and_seat()
course_catalog_id = 1
self.range.catalog_query = None
self.range.course_seat_types = None
self.range.course_catalog = course_catalog_id
self.range.save()
with self.assertRaises(Exception) as error:
self.range.contains_product(seat)
expected_exception_message = 'Unable to connect to Course Catalog service for catalog with id [%s].' %\
self.range.course_catalog
self.assertEqual(error.exception.message, expected_exception_message)
@httpretty.activate
@mock_course_catalog_api_client
def test_query_range_all_products(self): def test_query_range_all_products(self):
""" """
all_products() should return seats from the query. all_products() should return seats from the query.
......
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