test_views.py 5.98 KB
Newer Older
1
""" Tests for commerce views. """
Ned Batchelder committed
2

3
import json
4

5 6
import ddt
import mock
7 8
from django.core.urlresolvers import reverse
from nose.plugins.attrib import attr
9

10
from course_modes.models import CourseMode
11
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
12
from student.models import CourseEnrollment
13 14 15
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
16 17


18 19 20 21 22 23 24 25 26 27 28 29
class UserMixin(object):
    """ Mixin for tests involving users. """

    def setUp(self):
        super(UserMixin, self).setUp()
        self.user = UserFactory()

    def _login(self):
        """ Log into LMS. """
        self.client.login(username=self.user.username, password='test')


30
@attr(shard=1)
31
@ddt.ddt
32
class ReceiptViewTests(UserMixin, ModuleStoreTestCase):
33 34
    """ Tests for the receipt view. """

35 36 37 38 39 40 41 42 43 44 45 46 47
    def setUp(self):
        """
        Add a user and a course
        """
        super(ReceiptViewTests, self).setUp()
        self.user = UserFactory()
        self.client.login(username=self.user.username, password='test')
        self.course = CourseFactory.create(
            org='edX',
            course='900',
            run='test_run'
        )

48 49 50
    def test_login_required(self):
        """ The view should redirect to the login page if the user is not logged in. """
        self.client.logout()
51
        response = self.client.post(reverse('commerce:checkout_receipt'))
52
        self.assertEqual(response.status_code, 302)
53 54 55 56 57 58 59

    def post_to_receipt_page(self, post_data):
        """ DRY helper """
        response = self.client.post(reverse('commerce:checkout_receipt'), params={'basket_id': 1}, data=post_data)
        self.assertEqual(response.status_code, 200)
        return response

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    def test_user_verification_status_success(self):
        """
        Test user verification status. If the user enrollment for the course belongs to verified modes
        e.g. Verified, Professional then verification is required.
        """
        # Enroll as verified in the course with the current user.
        CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.VERIFIED)
        response = self.client.get(reverse('commerce:user_verification_status'), data={'course_id': self.course.id})
        json_data = json.loads(response.content)
        self.assertEqual(json_data['is_verification_required'], True)

        # Enroll as honor in the course with the current user.
        CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.HONOR)
        response = self.client.get(reverse('commerce:user_verification_status'), data={'course_id': self.course.id})
        json_data = json.loads(response.content)
        self.assertEqual(json_data['is_verification_required'], False)

    def test_user_verification_status_failure(self):
        """
        Test user verification status failure. View should required HttpResponseBadRequest 400 if course id is missing.
        """
        response = self.client.get(reverse('commerce:user_verification_status'))
        self.assertEqual(response.status_code, 400)

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    @ddt.data('decision', 'reason_code', 'signed_field_names', None)
    def test_is_cybersource(self, post_key):
        """
        Ensure the view uses three specific POST keys to detect a request initiated by Cybersource.
        """
        self._login()
        post_data = {'decision': 'REJECT', 'reason_code': '200', 'signed_field_names': 'dummy'}
        if post_key is not None:
            # a key will be missing; we will not expect the receipt page to handle a cybersource decision
            del post_data[post_key]
            expected_pattern = r"<title>(\s+)Receipt"
        else:
            expected_pattern = r"<title>(\s+)Payment Failed"
        response = self.post_to_receipt_page(post_data)
        self.assertRegexpMatches(response.content, expected_pattern)

    @ddt.data('ACCEPT', 'REJECT', 'ERROR')
    def test_cybersource_decision(self, decision):
        """
        Ensure the view renders a page appropriately depending on the Cybersource decision.
        """
        self._login()
        post_data = {'decision': decision, 'reason_code': '200', 'signed_field_names': 'dummy'}
        expected_pattern = r"<title>(\s+)Receipt" if decision == 'ACCEPT' else r"<title>(\s+)Payment Failed"
        response = self.post_to_receipt_page(post_data)
        self.assertRegexpMatches(response.content, expected_pattern)

    @ddt.data(True, False)
    @mock.patch('commerce.views.is_user_payment_error')
    def test_cybersource_message(self, is_user_message_expected, mock_is_user_payment_error):
        """
        Ensure that the page displays the right message for the reason_code (it
        may be a user error message or a system error message).
        """
        mock_is_user_payment_error.return_value = is_user_message_expected
        self._login()
        response = self.post_to_receipt_page({'decision': 'REJECT', 'reason_code': '99', 'signed_field_names': 'dummy'})
        self.assertTrue(mock_is_user_payment_error.called)
        self.assertTrue(mock_is_user_payment_error.call_args[0][0], '99')

        user_message = "There was a problem with this transaction"
        system_message = "A system error occurred while processing your payment"
        self.assertRegexpMatches(response.content, user_message if is_user_message_expected else system_message)
        self.assertNotRegexpMatches(response.content, user_message if not is_user_message_expected else system_message)
128

129
    @with_comprehensive_theme("edx.org")
130 131 132 133 134 135 136 137 138
    def test_hide_nav_header(self):
        self._login()
        post_data = {'decision': 'ACCEPT', 'reason_code': '200', 'signed_field_names': 'dummy'}
        response = self.post_to_receipt_page(post_data)

        # Verify that the header navigation links are hidden for the edx.org version
        self.assertNotContains(response, "How it Works")
        self.assertNotContains(response, "Find courses")
        self.assertNotContains(response, "Schools & Partners")