Beispiel #1
0
 def test_enrollment_module_fulfill_bad_attributes(self):
     """Test that use of the Fulfillment Module fails when the product does not have attributes."""
     ProductAttribute.objects.get(code='course_key').delete()
     EnrollmentFulfillmentModule().fulfill_product(
         self.order, list(self.order.lines.all()))
     self.assertEqual(LINE.FULFILLMENT_CONFIGURATION_ERROR,
                      self.order.lines.all()[0].status)
Beispiel #2
0
 def test_enrollment_module_server_error(self, body):
     """Test that lines receive a server-side error status if a server-side error occurs during fulfillment."""
     # NOTE: We are testing for cases where the response does and does NOT have data. The module should be able
     # to handle both cases.
     httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=500, body=body, content_type=JSON)
     EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
     self.assertEqual(LINE.FULFILLMENT_SERVER_ERROR, self.order.lines.all()[0].status)
Beispiel #3
0
    def test_credit_enrollment_module_fulfill(self):
        """Happy path test to ensure we can properly fulfill enrollments."""
        # Create the credit certificate type and order for the credit certificate type.
        self.create_seat_and_order(certificate_type='credit', provider='MIT')
        httpretty.register_uri(httpretty.POST,
                               get_lms_enrollment_api_url(),
                               status=200,
                               body='{}',
                               content_type=JSON)

        # Attempt to enroll.
        with LogCapture(LOGGER_NAME) as logger:
            EnrollmentFulfillmentModule().fulfill_product(
                self.order, list(self.order.lines.all()))

            line = self.order.lines.get()
            logger.check_present((
                LOGGER_NAME, 'INFO',
                'line_fulfilled: course_id="{}", credit_provider="{}", mode="{}", order_line_id="{}", '
                'order_number="{}", product_class="{}", user_id="{}"'.format(
                    line.product.attr.course_key,
                    line.product.attr.credit_provider,
                    mode_for_product(line.product),
                    line.id,
                    line.order.number,
                    line.product.get_product_class().name,
                    line.order.user.id,
                )))

        self.assertEqual(LINE.COMPLETE, line.status)

        actual = json.loads(httpretty.last_request().body.decode('utf-8'))
        expected = {
            'user':
            self.order.user.username,
            'is_active':
            True,
            'mode':
            self.certificate_type,
            'course_details': {
                'course_id': self.course_id,
            },
            'enrollment_attributes': [{
                'namespace': 'order',
                'name': 'order_number',
                'value': self.order.number
            }, {
                'namespace':
                'order',
                'name':
                'date_placed',
                'value':
                self.order.date_placed.strftime(ISO_8601_FORMAT)
            }, {
                'namespace': 'credit',
                'name': 'provider_id',
                'value': self.provider
            }]
        }
        self.assertEqual(actual, expected)
Beispiel #4
0
    def test_enrollment_headers(self):
        """ Test that the enrollment module 'EnrollmentFulfillmentModule' is
        sending enrollment request over to the LMS with proper headers.
        """
        # Create a dummy data for the enrollment request.
        data = {
            'user': '******',
            'is_active': True,
            'mode': 'honor',
            'course_details': {
                'course_id': self.course_id
            },
            'enrollment_attributes': []
        }

        # Now call the enrollment api to send POST request to LMS and verify
        # that the header of the request being sent contains the analytics
        # header 'x-edx-ga-client-id'.
        # This will raise the exception 'ConnectionError' because the LMS is
        # not available for ecommerce tests.
        try:
            # pylint: disable=protected-access
            EnrollmentFulfillmentModule()._post_to_enrollment_api(
                data=data, user=self.user)
        except ConnectionError as exp:
            # Check that the enrollment request object has the analytics header
            # 'x-edx-ga-client-id' and 'x-forwarded-for'.
            self.assertEqual(exp.request.headers.get('x-edx-ga-client-id'),
                             self.user.tracking_context['ga_client_id'])
            self.assertEqual(exp.request.headers.get('x-forwarded-for'),
                             self.user.tracking_context['lms_ip'])
Beispiel #5
0
    def test_enrollment_module_fulfill(self, parse_tracking_context):
        """Happy path test to ensure we can properly fulfill enrollments."""
        httpretty.register_uri(httpretty.POST,
                               get_lms_enrollment_api_url(),
                               status=200,
                               body='{}',
                               content_type=JSON)
        parse_tracking_context.return_value = ('user_123', 'GA-123456789',
                                               '11.22.33.44')
        # Attempt to enroll.
        with LogCapture(LOGGER_NAME) as l:
            EnrollmentFulfillmentModule().fulfill_product(
                self.order, list(self.order.lines.all()))

            line = self.order.lines.get()
            l.check((
                LOGGER_NAME, 'INFO',
                'line_fulfilled: course_id="{}", credit_provider="{}", mode="{}", order_line_id="{}", '
                'order_number="{}", product_class="{}", user_id="{}"'.format(
                    line.product.attr.course_key,
                    None,
                    mode_for_seat(line.product),
                    line.id,
                    line.order.number,
                    line.product.get_product_class().name,
                    line.order.user.id,
                )))

        self.assertEqual(LINE.COMPLETE, line.status)

        last_request = httpretty.last_request()
        actual_body = json.loads(last_request.body)
        actual_headers = last_request.headers

        expected_body = {
            'user':
            self.order.user.username,
            'is_active':
            True,
            'mode':
            self.certificate_type,
            'course_details': {
                'course_id': self.course_id,
            },
            'enrollment_attributes': [{
                'namespace': 'order',
                'name': 'order_number',
                'value': self.order.number
            }]
        }

        expected_headers = {
            'X-Edx-Ga-Client-Id': 'GA-123456789',
            'X-Forwarded-For': '11.22.33.44',
        }

        self.assertDictContainsSubset(expected_headers, actual_headers)
        self.assertEqual(expected_body, actual_body)
Beispiel #6
0
    def test_enrollment_module_fulfill(self, mock_post_request):
        """Happy path test to ensure we can properly fulfill enrollments."""
        fake_enrollment_api_response = Response()
        fake_enrollment_api_response.status_code = status.HTTP_200_OK
        mock_post_request.return_value = fake_enrollment_api_response

        self._create_attributes()

        # Attempt to enroll.
        EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
        self.assertEqual(LINE.COMPLETE, self.order.lines.all()[0].status)
Beispiel #7
0
 def test_enrollment_module_fulfill_order_with_discount_no_voucher(self):
     """
     Test that components of the Fulfillment Module which trigger on the presence of a voucher do
     not cause failures in cases where a discount does not have a voucher included
     (such as with a Conditional Offer)
     """
     httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON)
     self.create_seat_and_order(certificate_type='credit', provider='MIT')
     self.order.discounts.create()
     __, lines = EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
     # No exceptions should be raised and the order should be fulfilled
     self.assertEqual(lines[0].status, 'Complete')
Beispiel #8
0
 def test_voucher_usage_with_program(self):
     """
     Test that using a voucher with a program basket results in a fulfilled order.
     """
     httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON)
     self.create_seat_and_order(certificate_type='credit', provider='MIT')
     program_uuid = uuid.uuid4()
     self.mock_program_detail_endpoint(program_uuid, self.site_configuration.discovery_api_url)
     self.mock_user_data(self.user.username)
     self.prepare_basket_with_voucher(program_uuid=program_uuid)
     __, lines = EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
     # No exceptions should be raised and the order should be fulfilled
     self.assertEqual(lines[0].status, 'Complete')
Beispiel #9
0
    def test_enrollment_module_server_error(self, response_content):
        """Test that lines receive a server-side error status if a server-side error occurs during fulfillment."""
        # NOTE: We are testing for cases where the response does and does NOT have data. The module should be able
        # to handle both cases.
        fake_error_response = Response()
        fake_error_response._content = response_content  # pylint: disable=protected-access
        fake_error_response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR

        with mock.patch('requests.post', return_value=fake_error_response):
            self._create_attributes()

            # Attempt to enroll
            EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
            self.assertEqual(LINE.FULFILLMENT_SERVER_ERROR, self.order.lines.all()[0].status)
Beispiel #10
0
    def test_revoke_product_unexpected_error(self):
        """ If the Enrollment API responds with a non-200 status, the method should log an error and return False. """
        message = 'Meh.'
        body = '{{"message": "{}"}}'.format(message)
        httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=500, body=body, content_type=JSON)

        line = self.order.lines.first()
        logger_name = 'ecommerce.extensions.fulfillment.modules'
        with LogCapture(logger_name) as logger:
            self.assertFalse(EnrollmentFulfillmentModule().revoke_line(line))
            logger.check_present(
                (logger_name, 'INFO', 'Attempting to revoke fulfillment of Line [{}]...'.format(line.id)),
                (logger_name, 'ERROR', 'Failed to revoke fulfillment of Line [%d]: %s' % (line.id, message))
            )
Beispiel #11
0
    def test_revoke_product_expected_error(self):
        """
        If the Enrollment API responds with an expected error, the method should log that revocation was
        bypassed, and return True.
        """
        message = 'Enrollment mode mismatch: active mode=x, requested mode=y. Won\'t deactivate.'
        body = '{{"message": "{}"}}'.format(message)
        httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=400, body=body, content_type=JSON)

        line = self.order.lines.first()
        logger_name = 'ecommerce.extensions.fulfillment.modules'
        with LogCapture(logger_name) as logger:
            self.assertTrue(EnrollmentFulfillmentModule().revoke_line(line))
            logger.check_present(
                (logger_name, 'INFO', 'Attempting to revoke fulfillment of Line [{}]...'.format(line.id)),
                (logger_name, 'INFO', 'Skipping revocation for line [%d]: %s' % (line.id, message))
            )
Beispiel #12
0
    def test_revoke_product(self, parse_tracking_context):
        """ The method should call the Enrollment API to un-enroll the student, and return True. """
        httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON)
        parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44')
        line = self.order.lines.first()

        with LogCapture(LOGGER_NAME) as l:
            self.assertTrue(EnrollmentFulfillmentModule().revoke_line(line))

            l.check(
                (
                    LOGGER_NAME,
                    'INFO',
                    'line_revoked: certificate_type="{}", course_id="{}", order_line_id="{}", order_number="{}", '
                    'product_class="{}", user_id="{}"'.format(
                        getattr(line.product.attr, 'certificate_type', ''),
                        line.product.attr.course_key,
                        line.id,
                        line.order.number,
                        line.product.get_product_class().name,
                        line.order.user.id
                    )
                )
            )

        last_request = httpretty.last_request()
        actual_body = json.loads(last_request.body)
        actual_headers = last_request.headers

        expected_body = {
            'user': self.order.user.username,
            'is_active': False,
            'mode': self.certificate_type,
            'course_details': {
                'course_id': self.course_id,
            },
        }

        expected_headers = {
            'X-Edx-Ga-Client-Id': 'GA-123456789',
            'X-Forwarded-For': '11.22.33.44',
        }

        self.assertDictContainsSubset(expected_headers, actual_headers)
        self.assertEqual(expected_body, actual_body)
Beispiel #13
0
    def test_revoke_product_unknown_exception(self):
        """
        If an exception is raised while contacting the Enrollment API, the method should log an error and return False.
        """

        def request_callback(_method, _uri, _headers):
            raise Timeout

        httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), body=request_callback)
        line = self.order.lines.first()
        logger_name = 'ecommerce.extensions.fulfillment.modules'

        with LogCapture(logger_name) as logger:
            self.assertFalse(EnrollmentFulfillmentModule().revoke_line(line))
            logger.check_present(
                (logger_name, 'INFO', 'Attempting to revoke fulfillment of Line [{}]...'.format(line.id)),
                (logger_name, 'ERROR', 'Failed to revoke fulfillment of Line [{}].'.format(line.id))
            )
Beispiel #14
0
 def test_enrollment_module_request_timeout(self):
     """Test that lines receive a timeout error status if a fulfillment request times out."""
     EnrollmentFulfillmentModule().fulfill_product(
         self.order, list(self.order.lines.all()))
     self.assertEqual(LINE.FULFILLMENT_TIMEOUT_ERROR,
                      self.order.lines.all()[0].status)
Beispiel #15
0
 def test_enrollment_module_network_error(self):
     """Test that lines receive a network error status if a fulfillment request experiences a network error."""
     EnrollmentFulfillmentModule().fulfill_product(
         self.order, list(self.order.lines.all()))
     self.assertEqual(LINE.FULFILLMENT_NETWORK_ERROR,
                      self.order.lines.all()[0].status)
Beispiel #16
0
 def test_enrollment_module_not_configured(self):
     """Test that lines receive a configuration error status if fulfillment configuration is invalid."""
     EnrollmentFulfillmentModule().fulfill_product(
         self.order, list(self.order.lines.all()))
     self.assertEqual(LINE.FULFILLMENT_CONFIGURATION_ERROR,
                      self.order.lines.all()[0].status)
Beispiel #17
0
 def test_enrollment_module_support(self):
     """Test that we get the correct values back for supported product lines."""
     supported_lines = EnrollmentFulfillmentModule().get_supported_lines(
         list(self.order.lines.all()))
     self.assertEqual(1, len(supported_lines))
Beispiel #18
0
 def test_enrollment_module_fulfill_bad_attributes(self):
     """Test that use of the Fulfillment Module fails when the product does not have attributes."""
     # Attempt to enroll without creating the product attributes.
     EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
     self.assertEqual(LINE.FULFILLMENT_CONFIGURATION_ERROR, self.order.lines.all()[0].status)
Beispiel #19
0
    def test_enrollment_module_fulfill(self):
        """Happy path test to ensure we can properly fulfill enrollments."""
        httpretty.register_uri(httpretty.POST,
                               get_lms_enrollment_api_url(),
                               status=200,
                               body='{}',
                               content_type=JSON)
        # Attempt to enroll.
        with LogCapture(LOGGER_NAME) as logger:
            EnrollmentFulfillmentModule().fulfill_product(
                self.order, list(self.order.lines.all()))

            line = self.order.lines.get()
            logger.check_present((
                LOGGER_NAME, 'INFO',
                'line_fulfilled: course_id="{}", credit_provider="{}", mode="{}", order_line_id="{}", '
                'order_number="{}", product_class="{}", user_id="{}"'.format(
                    line.product.attr.course_key,
                    None,
                    mode_for_product(line.product),
                    line.id,
                    line.order.number,
                    line.product.get_product_class().name,
                    line.order.user.id,
                )))

        self.assertEqual(LINE.COMPLETE, line.status)

        last_request = httpretty.last_request()
        actual_body = json.loads(last_request.body.decode('utf-8'))
        actual_headers = last_request.headers

        expected_body = {
            'user':
            self.order.user.username,
            'is_active':
            True,
            'mode':
            self.certificate_type,
            'course_details': {
                'course_id': self.course_id,
            },
            'enrollment_attributes': [{
                'namespace': 'order',
                'name': 'order_number',
                'value': self.order.number
            }, {
                'namespace':
                'order',
                'name':
                'date_placed',
                'value':
                self.order.date_placed.strftime(ISO_8601_FORMAT)
            }]
        }

        expected_headers = {
            'X-Edx-Ga-Client-Id': self.user.tracking_context['ga_client_id'],
            'X-Forwarded-For': self.user.tracking_context['lms_ip'],
        }

        self.assertDictContainsSubset(expected_headers, actual_headers)
        self.assertEqual(expected_body, actual_body)
Beispiel #20
0
 def test_enrollment_module_revoke(self):
     """Test that use of this method due to "not implemented" error."""
     EnrollmentFulfillmentModule().revoke_product(self.order, list(self.order.lines.all()))