diff --git a/common/djangoapps/course_modes/tests/test_views.py b/common/djangoapps/course_modes/tests/test_views.py index 16868037f4..07bbeb7218 100644 --- a/common/djangoapps/course_modes/tests/test_views.py +++ b/common/djangoapps/course_modes/tests/test_views.py @@ -1,5 +1,6 @@ -import ddt import unittest +import decimal +import ddt from django.conf import settings from django.test.utils import override_settings from django.core.urlresolvers import reverse @@ -162,7 +163,7 @@ class CourseModeViewTest(ModuleStoreTestCase): POST_PARAMS_FOR_COURSE_MODE = { 'audit': {'audit_mode': True}, 'honor': {'honor-code': True}, - 'verified': {'certificate_mode': True} + 'verified': {'certificate_mode': True, 'contribution': '1.23'} } # TODO (ECOM-16): Remove the auto-register flag once the AB-test completes @@ -203,3 +204,20 @@ class CourseModeViewTest(ModuleStoreTestCase): self.fail("Must provide a valid redirect URL name") self.assertRedirects(resp, redirect_url) + + def test_remember_donation_for_course(self): + # Create the course modes + for mode in ('audit', 'honor', 'verified'): + CourseModeFactory(mode_slug=mode, course_id=self.course.id) + + # Choose the mode (POST request) + choose_track_url = reverse('course_modes_choose', args=[unicode(self.course.id)]) + self.client.post(choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE['verified']) + + # Expect that the contribution amount is stored in the user's session + self.assertIn('donation_for_course', self.client.session) + self.assertIn(unicode(self.course.id), self.client.session['donation_for_course']) + + actual_amount = self.client.session['donation_for_course'][unicode(self.course.id)] + expected_amount = decimal.Decimal(self.POST_PARAMS_FOR_COURSE_MODE['verified']['contribution']) + self.assertEqual(actual_amount, expected_amount) diff --git a/common/djangoapps/course_modes/views.py b/common/djangoapps/course_modes/views.py index d5b9932842..38525e48cf 100644 --- a/common/djangoapps/course_modes/views.py +++ b/common/djangoapps/course_modes/views.py @@ -16,7 +16,6 @@ from edxmako.shortcuts import render_to_response from course_modes.models import CourseMode from courseware.access import has_access from student.models import CourseEnrollment -from verify_student.models import SoftwareSecurePhotoVerification from opaque_keys.edx.locations import SlashSeparatedCourseKey from xmodule.modulestore.django import modulestore @@ -92,7 +91,7 @@ class ChooseModeView(View): ) donation_for_course = request.session.get("donation_for_course", {}) - chosen_price = donation_for_course.get(course_key, None) + chosen_price = donation_for_course.get(unicode(course_key), None) course = modulestore().get_course(course_key) context = { @@ -164,7 +163,7 @@ class ChooseModeView(View): return self.get(request, course_id, error=error_msg) donation_for_course = request.session.get("donation_for_course", {}) - donation_for_course[course_key] = amount_value + donation_for_course[unicode(course_key)] = amount_value request.session["donation_for_course"] = donation_for_course return redirect( diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 4c02899c8c..d5a7c9233e 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -13,6 +13,7 @@ verify_student/start?course_id=MITx/6.002x/2013_Spring # create import json import mock import urllib +import decimal from mock import patch, Mock import pytz from datetime import timedelta, datetime @@ -98,6 +99,21 @@ class TestVerifyView(ModuleStoreTestCase): response = self.client.get(url, {'upgrade': "True"}) self.assertIn("You are upgrading your registration for", response.content) + def test_show_selected_contribution_amount(self): + # Set the donation amount in the client's session + session = self.client.session + session['donation_for_course'] = { + unicode(self.course_key): decimal.Decimal('1.23') + } + session.save() + + # Retrieve the page + url = reverse('verify_student_verify', kwargs={"course_id": unicode(self.course_key)}) + response = self.client.get(url) + + # Expect that the user's contribution amount is shown on the page + self.assertContains(response, '1.23') + @override_settings(MODULESTORE=MODULESTORE_CONFIG) class TestVerifiedView(ModuleStoreTestCase): @@ -126,6 +142,25 @@ class TestVerifiedView(ModuleStoreTestCase): # Location should contains dashboard. self.assertIn('dashboard', response._headers.get('location')[1]) + def test_show_selected_contribution_amount(self): + # Configure the course to have a verified mode + for mode in ('audit', 'honor', 'verified'): + CourseModeFactory(mode_slug=mode, course_id=self.course.id) + + # Set the donation amount in the client's session + session = self.client.session + session['donation_for_course'] = { + unicode(self.course_id): decimal.Decimal('1.23') + } + session.save() + + # Retrieve the page + url = reverse('verify_student_verified', kwargs={"course_id": unicode(self.course_id)}) + response = self.client.get(url) + + # Expect that the user's contribution amount is shown on the page + self.assertContains(response, '1.23') + @override_settings(MODULESTORE=MODULESTORE_CONFIG) class TestReverifyView(ModuleStoreTestCase): @@ -548,6 +583,26 @@ class TestCreateOrder(ModuleStoreTestCase): data = json.loads(response.content) self.assertEqual(data['override_custom_receipt_page'], "http://testserver/shoppingcart/postpay_callback/") + def test_create_order_set_donation_amount(self): + # Verify the student so we don't need to submit photos + self._verify_student() + + # Create an order + url = reverse('verify_student_create_order') + params = { + 'course_id': unicode(self.course.id), + 'contribution': '1.23' + } + self.client.post(url, params) + + # Verify that the client's session contains the new donation amount + self.assertIn('donation_for_course', self.client.session) + self.assertIn(unicode(self.course.id), self.client.session['donation_for_course']) + + actual_amount = self.client.session['donation_for_course'][unicode(self.course.id)] + expected_amount = decimal.Decimal('1.23') + self.assertEqual(actual_amount, expected_amount) + def _verify_student(self): """ Simulate that the student's identity has already been verified. """ attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user) diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index 0ed9566ee9..6377d8f271 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -82,7 +82,7 @@ class VerifyView(View): if not current_mode: return redirect(reverse('dashboard')) if course_id.to_deprecated_string() in request.session.get("donation_for_course", {}): - chosen_price = request.session["donation_for_course"][course_id.to_deprecated_string()] + chosen_price = request.session["donation_for_course"][unicode(course_id)] else: chosen_price = current_mode.min_price @@ -145,7 +145,7 @@ class VerifiedView(View): if not current_mode: return redirect(reverse('dashboard')) if course_id.to_deprecated_string() in request.session.get("donation_for_course", {}): - chosen_price = request.session["donation_for_course"][course_id.to_deprecated_string()] + chosen_price = request.session["donation_for_course"][unicode(course_id)] else: chosen_price = current_mode.min_price @@ -189,15 +189,15 @@ def create_order(request): course_id = request.POST['course_id'] course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) donation_for_course = request.session.get('donation_for_course', {}) - current_donation = donation_for_course.get(course_id, decimal.Decimal(0)) - contribution = request.POST.get("contribution", donation_for_course.get(course_id, 0)) + current_donation = donation_for_course.get(unicode(course_id), decimal.Decimal(0)) + contribution = request.POST.get("contribution", donation_for_course.get(unicode(course_id), 0)) try: amount = decimal.Decimal(contribution).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN) except decimal.InvalidOperation: return HttpResponseBadRequest(_("Selected price is not valid number.")) if amount != current_donation: - donation_for_course[course_id] = amount + donation_for_course[unicode(course_id)] = amount request.session['donation_for_course'] = donation_for_course # prefer professional mode over verified_mode diff --git a/lms/envs/devstack.py b/lms/envs/devstack.py index dd70d63c67..84229bd609 100644 --- a/lms/envs/devstack.py +++ b/lms/envs/devstack.py @@ -84,8 +84,15 @@ PIPELINE_SASS_ARGUMENTS = '--debug-info --require {proj_dir}/static/sass/bourbon FEATURES['AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'] = True FEATURES['ENABLE_PAYMENT_FAKE'] = True -for processor in CC_PROCESSOR.values(): - processor['PURCHASE_ENDPOINT'] = '/shoppingcart/payment_fake/' +CC_PROCESSOR_NAME = 'CyberSource2' +CC_PROCESSOR = { + 'CyberSource2': { + "PURCHASE_ENDPOINT": '/shoppingcart/payment_fake/', + "SECRET_KEY": 'abcd123', + "ACCESS_KEY": 'abcd123', + "PROFILE_ID": 'edx', + } +} ##################################################################### # See if the developer has any local overrides.