Commit 9a094c9a by Ayub khan

Merge pull request #720 from edx/ECOM-4312

ECOM-4312 Check added to prevent multiple order issue.
parents 96fcf292 4a95a746
......@@ -33,6 +33,11 @@ def get_lms_enrollment_api_url():
return get_lms_url('/api/enrollment/v1/enrollment')
def get_lms_enrollment_base_api_url():
""" Returns the Base lms enrollment api url."""
return get_lms_url('/api/enrollment/v1')
def get_lms_heartbeat_url():
return get_lms_url('/heartbeat')
......
import datetime
import hashlib
import json
import ddt
from django.conf import settings
......@@ -16,6 +17,7 @@ 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_url
......@@ -39,6 +41,7 @@ StockRecord = get_model('partner', 'StockRecord')
COUPON_CODE = 'COUPONTEST'
@ddt.ddt
class BasketSingleItemViewTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockMixin, TestCase):
""" BasketSingleItemView view tests. """
path = reverse('basket:single-item')
......@@ -55,6 +58,29 @@ class BasketSingleItemViewTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockM
self.catalog = Catalog.objects.create(partner=self.partner)
self.catalog.stock_records.add(self.stock_record)
def mock_enrollment_api_success(self, course_id, mode='audit'):
""" Returns a successful response indicating self.user is enrolled in the specified course mode. """
self.assertTrue(httpretty.is_enabled())
url = '{host}/{username},{course_id}'.format(
host=get_lms_enrollment_api_url(),
username=self.user.username,
course_id=course_id
)
httpretty.register_uri(httpretty.GET, url, body=json.dumps({'mode': mode}), content_type='application/json')
def mock_enrollment_api_error(self, error):
""" Mock Enrollment api call which raises error when called """
self.assertTrue(httpretty.is_enabled())
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,
course_id=self.course.id
)
httpretty.register_uri(httpretty.GET, url, body=callback, content_type='application/json')
def test_login_required(self):
""" The view should redirect to login page if the user is not logged in. """
self.client.logout()
......@@ -78,8 +104,10 @@ class BasketSingleItemViewTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockM
self.assertEqual(response.status_code, 400)
self.assertEqual(response.content, expected_content)
@httpretty.activate
def test_unavailable_product(self):
""" The view should return HTTP 400 if the product is not available for purchase. """
self.mock_enrollment_api_success(self.course.id)
product = self.stock_record.product
product.expires = pytz.utc.localize(datetime.datetime.min)
product.save()
......@@ -96,6 +124,7 @@ class BasketSingleItemViewTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockM
"""
Verify the view redirects to the basket summary page, and that the user's basket is prepared for checkout.
"""
self.mock_enrollment_api_success(self.course.id)
self.create_coupon(catalog=self.catalog, code=COUPON_CODE, benefit_value=5)
self.mock_course_api_response(course=self.course)
......@@ -111,6 +140,37 @@ class BasketSingleItemViewTests(CouponMixin, CourseCatalogTestMixin, LmsApiMockM
self.assertTrue(basket.contains_a_voucher)
self.assertEqual(basket.lines.first().product, self.stock_record.product)
@httpretty.activate
@ddt.data(('verified', False), ('professional', True), ('no-id-professional', False))
@ddt.unpack
def test_already_verified_student(self, mode, id_verification):
"""
Verify the view return HTTP 400 if the student is already enrolled as verified student in the course
"""
course = CourseFactory()
self.mock_enrollment_api_success(course.id, mode=mode)
product = course.create_or_update_seat(mode, id_verification, 0, self.partner)
stock_record = StockRecordFactory(product=product, partner=self.partner)
catalog = Catalog.objects.create(partner=self.partner)
catalog.stock_records.add(stock_record)
url = '{path}?sku={sku}'.format(path=self.path, sku=stock_record.partner_sku)
expected_content = 'You are already enrolled in {product}.'.format(product=product.course.name)
response = self.client.get(url)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.content, expected_content)
@httpretty.activate
@ddt.data(ConnectionError, SlumberBaseException, Timeout)
def test_enrollment_api_failure(self, error):
"""
Verify the view returns HTTP status 400 if the Enrollment API is not available.
"""
self.mock_enrollment_api_error(error)
url = '{path}?sku={sku}'.format(path=self.path, sku=self.stock_record.partner_sku)
response = self.client.get(url)
self.assertEqual(response.status_code, 400)
@httpretty.activate
@ddt.ddt
......
......@@ -15,8 +15,9 @@ from edx_rest_api_client.client import EdxRestApiClient
from slumber.exceptions import SlumberBaseException
from ecommerce.core.constants import ENROLLMENT_CODE_PRODUCT_CLASS_NAME, SEAT_PRODUCT_CLASS_NAME
from ecommerce.core.url_utils import get_lms_url
from ecommerce.core.url_utils import get_lms_url, get_lms_enrollment_base_api_url
from ecommerce.coupons.views import get_voucher_from_code
from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.analytics.utils import prepare_analytics_data
from ecommerce.extensions.basket.utils import get_certificate_type_display_value, prepare_basket
from ecommerce.extensions.offer.utils import format_benefit_value
......@@ -48,12 +49,43 @@ class BasketSingleItemView(View):
try:
product = StockRecord.objects.get(partner=partner, partner_sku=sku).product
course_key = product.attr.course_key
api = EdxRestApiClient(
get_lms_enrollment_base_api_url(),
oauth_access_token=request.user.access_token,
append_slash=False
)
logger.debug(
'Getting enrollment information for [%s] in [%s].',
request.user.username,
course_key
)
status = api.enrollment(','.join([request.user.username, course_key])).get()
username = request.user.username
seat_type = mode_for_seat(product)
if status and status.get('mode') == seat_type:
logger.warning(
'User [%s] attempted to repurchase the [%s] seat of course [%s]',
username,
seat_type,
course_key
)
return HttpResponseBadRequest(_('You are already enrolled in {course}.').format(
course=product.course.name))
except StockRecord.DoesNotExist:
return HttpResponseBadRequest(_('SKU [{sku}] does not exist.'.format(sku=sku)))
return HttpResponseBadRequest(_('SKU [{sku}] does not exist.').format(sku=sku))
except (ConnectionError, SlumberBaseException, Timeout) as ex:
logger.exception(
'Failed to retrieve enrollment details for [%s] in course [%s], Because of [%s]',
request.user.username,
course_key,
ex,
)
return HttpResponseBadRequest(_('An error occurred while retrieving enrollment details. Please try again.'))
purchase_info = request.strategy.fetch_for_product(product)
if not purchase_info.availability.is_available_to_buy:
return HttpResponseBadRequest(_('Product [{product}] not available to buy.'.format(product=product.title)))
return HttpResponseBadRequest(_('Product [{product}] not available to buy.').format(product=product.title))
prepare_basket(request, product, voucher)
return HttpResponseRedirect(reverse('basket:summary'), status=303)
......
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