Exemplo n.º 1
0
    def test_is_satisfied_with_enrollments(self):
        """ The condition should be satisfied if one valid course run from each course is in either the
        basket or the user's enrolled courses and the site has enabled partial program offers. """
        offer = factories.ProgramOfferFactory(partner=self.partner,
                                              condition=self.condition)
        basket = BasketFactory(site=self.site, owner=UserFactory())
        program = self.mock_program_detail_endpoint(
            self.condition.program_uuid,
            self.site_configuration.discovery_api_url)

        # Extract one verified seat for each course
        verified_seats = []
        for course in program['courses']:
            course_run = Course.objects.get(id=course['course_runs'][0]['key'])
            for seat in course_run.seat_products:
                if seat.attr.id_verification_required:
                    verified_seats.append(seat)

        # Add verified enrollments for the first two program courses to the mock user data
        enrollments = [{
            'mode': 'verified',
            'course_details': {
                'course_id': program['courses'][0]['course_runs'][0]['key']
            }
        }, {
            'mode': 'verified',
            'course_details': {
                'course_id': program['courses'][1]['course_runs'][0]['key']
            }
        }]
        self.mock_user_data(basket.owner.username, owned_products=enrollments)

        # If the user has not added all of the remaining courses in the program to their basket,
        # the condition should not be satisfied
        basket.flush()
        for seat in verified_seats[2:len(verified_seats) - 1]:
            basket.add_product(seat)
        self.assertFalse(self.condition.is_satisfied(offer, basket))

        # When all courses in the program that the user is not already enrolled in are in their basket
        # and the site allows partial program completion, the condition should be satisfied
        basket.add_product(verified_seats[-1])
        self.assertTrue(self.condition.is_satisfied(offer, basket))

        # If the site does not allow partial program completion and the user does not have all of the program
        # courses in their basket, the condition should not be satisfied
        basket.site.siteconfiguration.enable_partial_program = False
        self.assertFalse(self.condition.is_satisfied(offer, basket))

        # Verify the user enrollments are cached
        basket.site.siteconfiguration.enable_partial_program = True
        httpretty.disable()
        with mock.patch('ecommerce.programs.conditions.get_program',
                        return_value=program):
            self.assertTrue(self.condition.is_satisfied(offer, basket))
Exemplo n.º 2
0
    def test_is_satisfied_with_entitlements(self):
        """
        The condition should be satisfied if, for each course in the program, their is either an entitlement sku in the
        basket or the user already has an entitlement for the course and the site has enabled partial program offers.
        """
        offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition)
        basket = BasketFactory(site=self.site, owner=UserFactory())
        program = self.mock_program_detail_endpoint(
            self.condition.program_uuid, self.site_configuration.discovery_api_url
        )
        entitlements_response = {
            "count": 0, "num_pages": 1, "current_page": 1, "results": [
                {'mode': 'verified', 'course_uuid': '268afbfc-cc1e-415b-a5d8-c58d955bcfc3'},
                {'mode': 'verified', 'course_uuid': '268afbfc-cc1e-415b-a5d8-c58d955bcfc4'}
            ], "next": None, "start": 0, "previous": None
        }

        # Extract one verified seat for each course
        verified_entitlements = []
        course_uuids = {course['uuid'] for course in program['courses']}
        for parent_entitlement in Product.objects.filter(
                product_class__name=COURSE_ENTITLEMENT_PRODUCT_CLASS_NAME, structure=Product.PARENT
        ):
            for entitlement in Product.objects.filter(parent=parent_entitlement):
                if entitlement.attr.UUID in course_uuids and entitlement.attr.certificate_type == 'verified':
                    verified_entitlements.append(entitlement)

        self.mock_user_data(basket.owner.username, mocked_api='entitlements', owned_products=entitlements_response)
        self.mock_user_data(basket.owner.username)
        # If the user has not added all of the remaining courses in program to their basket,
        # the condition should not be satisfied
        basket.flush()
        for entitlement in verified_entitlements[2:len(verified_entitlements) - 1]:
            basket.add_product(entitlement)
        self.assertFalse(self.condition.is_satisfied(offer, basket))

        # When all courses in the program that the user is not already enrolled in are in their basket
        # and the site allows partial program completion, the condition should be satisfied
        basket.add_product(verified_entitlements[-1])
        self.assertTrue(self.condition.is_satisfied(offer, basket))

        # If the site does not allow partial program completion and the user does not have all of the program
        # courses in their basket, the condition should not be satisfied
        basket.site.siteconfiguration.enable_partial_program = False
        self.assertFalse(self.condition.is_satisfied(offer, basket))

        # Verify the user enrollments are cached
        basket.site.siteconfiguration.enable_partial_program = True
        httpretty.disable()
        with mock.patch('ecommerce.programs.conditions.get_program',
                        return_value=program):
            self.assertTrue(self.condition.is_satisfied(offer, basket))
Exemplo n.º 3
0
    def test_is_satisfied_no_enrollments(self):
        """ The method should return True if the basket contains one course run seat corresponding to each
        course in the program. """
        offer = factories.ProgramOfferFactory(partner=self.partner,
                                              condition=self.condition)
        basket = BasketFactory(site=self.site, owner=UserFactory())
        program = self.mock_program_detail_endpoint(
            self.condition.program_uuid,
            self.site_configuration.discovery_api_url)

        # Extract one audit and one verified seat for each course
        audit_seats = []
        verified_seats = []

        for course in program['courses']:
            course_run = Course.objects.get(id=course['course_runs'][0]['key'])
            for seat in course_run.seat_products:
                if seat.attr.id_verification_required:
                    verified_seats.append(seat)
                else:
                    audit_seats.append(seat)

        self.mock_user_data(basket.owner.username)
        # Empty baskets should never be satisfied
        basket.flush()
        self.assertTrue(basket.is_empty)
        self.assertFalse(self.condition.is_satisfied(offer, basket))

        # Adding seats of all the courses with the wrong seat type should NOT satisfy the condition.
        basket.flush()
        for seat in audit_seats:
            basket.add_product(seat)
        self.assertFalse(self.condition.is_satisfied(offer, basket))

        # All courses must be represented in the basket.
        # NOTE: We add all but the first verified seat to ensure complete branch coverage of the method.
        basket.flush()
        for verified_seat in verified_seats[1:len(verified_seats)]:
            basket.add_product(verified_seat)
        self.assertFalse(self.condition.is_satisfied(offer, basket))

        # The condition should be satisfied if one valid course run from each course is in the basket.
        basket.add_product(verified_seats[0])
        self.assertTrue(self.condition.is_satisfied(offer, basket))

        # If the user is enrolled with the wrong seat type for courses missing from their basket that are
        # needed for the program, the condition should NOT be satisfied
        basket.flush()
        for verified_seat in verified_seats[1:len(verified_seats)]:
            basket.add_product(verified_seat)
        self.assertFalse(self.condition.is_satisfied(offer, basket))
Exemplo n.º 4
0
class JournalBundleConditionTests(TestCase, JournalMixin):
    def setUp(self):
        super(JournalBundleConditionTests, self).setUp()
        user = self.create_user(is_staff=True)
        self.client.login(username=user.username, password=self.password)

        self.condition = factories.JournalConditionFactory()
        self.offer = factories.JournalBundleOfferFactory(
            partner=self.partner, condition=self.condition)
        self.basket = BasketFactory(site=self.site, owner=UserFactory())
        self.basket.add_product(self.create_product(self.client), 1)

    def test_name(self, mocked_journal_api_response):
        """ The name should contain the program's UUID. """
        mocked_journal_api_response.return_value = None
        expected = 'Basket contains every product in bundle {}'.format(
            self.condition.journal_bundle_uuid)
        self.assertEqual(self.condition.name, expected)

    def test_is_satisfied_with_empty_basket(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with empty basket """
        mocked_journal_api_response.return_value = None
        self.basket.flush()
        self.assertTrue(self.basket.is_empty)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_exception(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with 'HttpNotFoundError' exception  """
        mocked_journal_api_response.side_effect = HttpNotFoundError
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_slumber_exception(self,
                                                 mocked_journal_api_response):
        """ Test the 'is_satisfied' with 'SlumberBaseException' exception  """
        mocked_journal_api_response.side_effect = SlumberBaseException
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_timeout(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with 'Timeout' exception  """
        mocked_journal_api_response.side_effect = Timeout
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_without_journal_bundle(self,
                                                 mocked_journal_api_response):
        """ Test the 'is_satisfied' without Journal bundle """
        mocked_journal_api_response.return_value = None
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_without_courses(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' without courses in Journal bundle """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
            empty_courses=True)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_some_but_not_all_courses(
            self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with only some of the courses in the Journal bundle """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
            multiple_courses=True)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_dummy_product(self,
                                             mocked_journal_api_response):
        """ Test the 'is_satisfied' with dummy product in basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        self.basket.flush()
        self.basket.add_product(
            self.create_product(
                self.client, data=self.get_data_for_create(sku="dummy-sku")),
            1)
        self.assertFalse(self.condition.is_satisfied(self.offer, self.basket))

    def test_is_satisfied_with_valid_data(self, mocked_journal_api_response):
        """ Test the 'is_satisfied' with valid Journal bundle """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
            empty_journals=True)
        self.assertTrue(self.condition.is_satisfied(self.offer, self.basket))

    def test_get_applicable_lines(self, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' with valid product in basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        applicable_lines = [(line.product.stockrecords.first().price_excl_tax,
                             line) for line in self.basket.all_lines()]
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket),
            applicable_lines)

    def test_get_applicable_lines_with_empty_basket(
            self, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' with empty basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        self.basket.flush()
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    def test_get_applicable_lines_sku_not_in_basket(
            self, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' where the sku is not in the basket """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        self.basket.flush()
        self.basket.add_product(
            self.create_product(
                self.client, data=self.get_data_for_create(sku="dummy-sku")),
            1)
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    @mock.patch(
        "ecommerce.extensions.catalogue.models.Product.get_is_discountable")
    def test_get_applicable_lines_product_is_not_discountable(
            self, mocked_product_is_discountable, mocked_journal_api_response):
        """ Test the 'get_applicable_lines' where the product is not discountable """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        mocked_product_is_discountable.return_value = False

        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    @mock.patch("oscar.apps.offer.utils.unit_price")
    def test_get_applicable_lines_no_price(self, mocked_unit_price,
                                           mocked_journal_api_response):
        """ Test the 'get_applicable_lines' where there is no price """
        mocked_journal_api_response.return_value = self.get_mocked_discovery_journal_bundle(
        )
        mocked_unit_price.return_value = None
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])

    def test_get_applicable_lines_no_journal_bundle(self,
                                                    mock_journal_api_response):
        """ Test 'get_applicable_lines' where the journal bundle is None """
        mock_journal_api_response.return_value = None
        self.assertEqual(
            self.condition.get_applicable_lines(self.offer, self.basket), [])