コード例 #1
0
def test_type_string():
    """
    type_string should return a string representation of the Product type
    """
    program = ProgramFactory.create()
    run = CourseRunFactory.create()
    program_product = ProductFactory.create(content_object=program)
    assert program_product.type_string == "program"
    run_product = ProductFactory.create(content_object=run)
    assert run_product.type_string == "courserun"
コード例 #2
0
def test_title():
    """
    title should return a string representation of the Product's title
    """
    program = ProgramFactory.create(title="test title of the program")
    course = CourseFactory.create(title="test title of the course")
    run = CourseRunFactory.create(course=course)

    program_product = ProductFactory.create(content_object=program)
    assert program_product.title == "test title of the program"
    run_product = ProductFactory.create(content_object=run)
    assert run_product.title == "test title of the course"
コード例 #3
0
def test_course_runs_without_product_in_programs_api(client, has_product):
    """Regardless of whether course runs have a product, runs should **not** be filtered out of the programs API"""
    run = CourseRunFactory.create(live=True, course__live=True)
    ProductVersionFactory.create(product=ProductFactory(
        content_object=run.course.program))
    if has_product:
        ProductVersionFactory.create(product=ProductFactory(
            content_object=run))
    resp = client.get(reverse("programs_api-list"))
    assert resp.status_code == status.HTTP_200_OK
    assert_drf_json_equal(resp.json()[0]["courses"][0]["courseruns"],
                          [CourseRunSerializer(run).data])
コード例 #4
0
def test_start_date():
    """
    start_date should return a start next_run_date of the Product
    """
    program = ProgramFactory.create()
    course = CourseFactory.create()
    run = CourseRunFactory.create(course=course)

    program_product = ProductFactory.create(content_object=program)
    assert program_product.start_date == program.next_run_date
    run_product = ProductFactory.create(content_object=run)
    assert run_product.start_date == course.next_run_date
コード例 #5
0
def test_thumbnail_url():
    """
    thumbnail_url should return a url of the Product's thumbnail
    """
    program = ProgramFactory.create()
    program_product = ProductFactory.create(content_object=program)
    run = CourseRunFactory.create()
    run_product = ProductFactory.create(content_object=run)

    assert (program_product.thumbnail_url == program.page.thumbnail_image.
            get_rendition(CATALOG_COURSE_IMG_WAGTAIL_FILL).url)
    assert (run_product.thumbnail_url == run.course.page.thumbnail_image.
            get_rendition(CATALOG_COURSE_IMG_WAGTAIL_FILL).url)
コード例 #6
0
ファイル: serializers_test.py プロジェクト: mitodl/mitxpro
def test_serialize_basket_product_version_program(mock_context):
    """Test ProductVersion serialization for a Program"""
    program = ProgramFactory.create()
    courses = CourseFactory.create_batch(3, program=program)
    product_version = ProductVersionFactory.create(
        product=ProductFactory(content_object=program)
    )

    data = FullProductVersionSerializer(
        instance=product_version, context=mock_context
    ).data
    assert data == {
        "id": product_version.id,
        "description": product_version.description,
        "content_title": product_version.product.content_object.title,
        "price": str(round_half_up(product_version.price)),
        "type": product_version.product.content_type.model,
        "courses": [
            CourseSerializer(instance=course, context=mock_context).data
            for course in courses
        ],
        "thumbnail_url": program.catalog_image_url,
        "object_id": product_version.product.object_id,
        "product_id": product_version.product.id,
        "readable_id": get_readable_id(product_version.product.content_object),
        "run_tag": None,
        "created_on": product_version.created_on.strftime(datetime_millis_format),
        "start_date": product_version.product.content_object.next_run_date.isoformat()
        if product_version.product.content_object.next_run_date
        else None,
    }
コード例 #7
0
def programs():
    """Fixture for a set of Programs in the database"""
    programs = ProgramFactory.create_batch(3)
    for program in programs:
        ProductVersionFactory.create(product=ProductFactory(
            content_object=program))
    return programs
コード例 #8
0
ファイル: conftest.py プロジェクト: mitodl/mitxpro
def voucher_and_partial_matches_with_coupons(voucher_and_partial_matches):
    """
    Returns a voucher with partial matching CourseRuns and valid coupons
    """
    context = voucher_and_partial_matches
    products = [
        ProductFactory(content_object=course_run)
        for course_run in context.partial_matches
    ]
    coupon_eligibility_list = [
        CouponEligibilityFactory(product=product) for product in products
    ]
    payment_versions = [
        CouponPaymentVersionFactory(amount=1, company=context.company)
        for _ in coupon_eligibility_list
    ]
    coupon_versions = [
        CouponVersionFactory(
            coupon=coupon_eligibility_list[i].coupon,
            payment_version=payment_versions[i],
        ) for i in range(len(coupon_eligibility_list))
    ]

    return SimpleNamespace(
        **vars(voucher_and_partial_matches),
        products=products,
        coupon_eligibility_list=coupon_eligibility_list,
        coupon_versions=coupon_versions,
        payment_versions=payment_versions,
    )
コード例 #9
0
def test_course_runs_not_live_in_courses_api(client, live):
    """Course runs should be filtered out of the courses API if not live"""
    run = CourseRunFactory.create(live=live, course__live=True)
    ProductVersionFactory.create(product=ProductFactory(content_object=run))
    resp = client.get(reverse("courses_api-list"))
    assert resp.status_code == status.HTTP_200_OK
    assert_drf_json_equal(resp.json()[0]["courseruns"],
                          [CourseRunSerializer(run).data] if live else [])
コード例 #10
0
def test_programs_not_live(client, live):
    """Programs should be filtered out if live=False"""
    program = ProgramFactory.create(live=live)
    ProductVersionFactory.create(product=ProductFactory(
        content_object=program))
    resp = client.get(reverse("programs_api-list"))
    assert resp.status_code == status.HTTP_200_OK
    assert_drf_json_equal(resp.json(),
                          [ProgramSerializer(program).data] if live else [])
コード例 #11
0
def test_courses_not_live_in_programs_api(client, live):
    """Courses should be filtered out of the programs API if not live"""
    course = CourseFactory.create(live=live, program__live=True)
    ProductVersionFactory.create(product=ProductFactory(
        content_object=course.program))
    resp = client.get(reverse("programs_api-list"))
    assert resp.status_code == status.HTTP_200_OK
    assert_drf_json_equal(resp.json()[0]["courses"],
                          [CourseSerializer(course).data] if live else [])
コード例 #12
0
def test_product_version_save_text_id_badproduct(mocker):
    """ProductVersion.text_id should None if ProductVersion.product is invalid"""
    mock_log = mocker.patch("ecommerce.models.log")
    product_version = ProductVersionFactory.create(
        product=ProductFactory.create(content_object=LineFactory()))
    assert product_version.text_id is None
    assert mock_log.called_once_with(
        f"The content object for this ProductVersion ({product_version.id}) does not have a `text_id` property"
    )
コード例 #13
0
def test_program_view(client, user, home_page, is_enrolled, has_product,
                      has_unexpired_run, is_anonymous):
    """
    Test that the program detail view has the right context and shows the right HTML for the enroll/view button
    """
    program = ProgramFactory.create(live=True, page__parent=home_page)

    if has_unexpired_run:
        now = now_in_utc()
        CourseRunFactory.create_batch(
            3,
            course=CourseFactory.create(program=program,
                                        live=True,
                                        position_in_program=1),
            live=True,
            start_date=now + timedelta(hours=2),
        )

    if has_product:
        product_id = ProductVersionFactory.create(product=ProductFactory(
            content_object=program)).product.id
    else:
        product_id = None
    if is_enrolled:
        ProgramEnrollmentFactory.create(user=user, program=program)

    if not is_anonymous:
        client.force_login(user)
    resp = client.get(program.page.get_url())
    assert resp.context["user"] == user if not is_anonymous else AnonymousUser(
    )
    assert resp.context["product_id"] == product_id
    assert resp.context["enrolled"] == (is_enrolled and not is_anonymous)

    # Anynoymous users don't see the enrolled/enroll-now button.
    # For logged in users:
    # a) product should exist, next courserun should be there, user not enrolled (enroll now button)
    # b) user is enrolled (enrolled button)
    has_button = ((has_product and has_unexpired_run and not is_enrolled)
                  or is_enrolled) and not is_anonymous
    url = ""  # make linter happy
    class_name = ""
    if not is_anonymous:
        if not is_enrolled and has_product and has_unexpired_run:
            url = f'{reverse("checkout-page")}?product={product_id}'
            class_name = "enroll-now"
        if is_enrolled:
            url = reverse("user-dashboard")
            class_name = "enrolled"

    assert (
        f'<a class="enroll-button {class_name}" href="{url}">'.encode("utf-8")
        in resp.content) is has_button
    assert ("Please Sign In to MITx PRO to enroll in a course".encode("utf-8")
            in resp.content) is (is_anonymous and has_product
                                 and has_unexpired_run)
コード例 #14
0
def test_program_without_product_in_programs_api(client, has_product):
    """Programs should be filtered out of the programs API if they don't have an associated product"""
    program = ProgramFactory.create(live=True)
    if has_product:
        ProductVersionFactory.create(product=ProductFactory(
            content_object=program))
    resp = client.get(reverse("programs_api-list"))
    assert resp.status_code == status.HTTP_200_OK
    assert_drf_json_equal(
        resp.json(), [ProgramSerializer(program).data] if has_product else [])
コード例 #15
0
ファイル: models_test.py プロジェクト: mitodl/mitxpro
def test_program_current_price():
    """
    current_price should return the price of the latest product version if it exists
    """
    program = ProgramFactory.create()
    assert program.current_price is None
    price = 10
    ProductVersionFactory.create(
        product=ProductFactory(content_object=program), price=price)
    assert program.current_price == price
コード例 #16
0
ファイル: models_test.py プロジェクト: mitodl/mitxpro
def test_course_run_current_price():
    """
    current_price should return the price of the latest product version if it exists
    """
    run = CourseRunFactory.create()
    assert run.current_price is None
    price = 10
    ProductVersionFactory.create(product=ProductFactory(content_object=run),
                                 price=price)
    assert run.current_price == price
コード例 #17
0
ファイル: tasks_test.py プロジェクト: mitodl/mitxpro
def test_sync_product_with_hubspot(mock_hubspot_request):
    """Test that send_hubspot_request is called properly for a PRODUCT sync"""
    product = ProductFactory.create()
    sync_product_with_hubspot(product.id)
    body = make_product_sync_message(product.id)
    body[0]["changeOccurredTimestamp"] = ANY
    mock_hubspot_request.assert_called_once_with("PRODUCT",
                                                 HUBSPOT_SYNC_URL,
                                                 "PUT",
                                                 body=body)
コード例 #18
0
ファイル: serializers_test.py プロジェクト: mitodl/mitxpro
def test_serialize_basket_product_version_programrun(mock_context):
    """Test ProductVersion serialization for a Program with an associated ProgramRun"""
    program_run = ProgramRunFactory()
    product_version = ProductVersionFactory.create(
        product=ProductFactory(content_object=program_run.program)
    )
    context = {**mock_context, **{"program_run": program_run}}

    data = FullProductVersionSerializer(instance=product_version, context=context).data
    assert data["object_id"] == program_run.program.id
    assert data["run_tag"] == program_run.run_tag
コード例 #19
0
def test_course_runs_without_product_in_courses_api(client, has_product):
    """Course runs should be filtered out of the courses API if they don't have an associated product"""
    run = CourseRunFactory.create(live=True, course__live=True)
    if has_product:
        ProductVersionFactory.create(product=ProductFactory(
            content_object=run))
    resp = client.get(reverse("courses_api-list"))
    assert resp.status_code == status.HTTP_200_OK
    assert_drf_json_equal(
        resp.json()[0]["courseruns"],
        [CourseRunSerializer(run).data] if has_product else [],
    )
コード例 #20
0
def test_course_view(client, user, home_page, is_enrolled, has_unexpired_run,
                     has_product, is_anonymous):
    """
    Test that the course detail view has the right context and shows the right HTML for the enroll/view button
    """
    course = CourseFactory.create(live=True, page__parent=home_page)

    if has_unexpired_run:
        run = CourseRunFactory.create(course=course, live=True)
    else:
        run = None
    if has_product and has_unexpired_run:
        product_id = ProductVersionFactory.create(product=ProductFactory(
            content_object=run)).product.id
    else:
        product_id = None
    if is_enrolled and has_unexpired_run:
        CourseRunEnrollmentFactory.create(user=user, run=run)

    if not is_anonymous:
        client.force_login(user)
    resp = client.get(course.page.get_url())
    assert resp.context["user"] == user if not is_anonymous else AnonymousUser(
    )
    assert resp.context["product_id"] == product_id
    assert resp.context["enrolled"] == (is_enrolled and has_unexpired_run
                                        and not is_anonymous)

    # Anynoymous users don't see the enrolled/enroll-now button.
    # For logged in users:
    # a) product should exist, next courserun should be there, user not enrolled (enroll now button)
    # b) user is enrolled (enrolled button)
    # NOTE: added `has_unexpired_run` to test for case (b) only because of the way the test is written,
    #       enrollment isn't actually created unless the course has an unexpired run.
    has_button = ((has_product and has_unexpired_run and not is_enrolled) or
                  (is_enrolled and has_unexpired_run)) and not is_anonymous
    url = ""  # make linter happy
    class_name = ""
    if not is_anonymous:
        if not is_enrolled and has_product and has_unexpired_run:
            url = f'{reverse("checkout-page")}?product={product_id}'
            class_name = "enroll-now"
        if is_enrolled and has_unexpired_run:
            url = reverse("user-dashboard")
            class_name = "enrolled"

    assert (
        f'<a class="enroll-button {class_name}" href="{url}">'.encode("utf-8")
        in resp.content) is has_button
    assert ("Please Sign In to MITx PRO to enroll in a course".encode("utf-8")
            in resp.content) is (is_anonymous and has_product
                                 and has_unexpired_run)
コード例 #21
0
def test_latest_version():
    """
    The latest_version property should return the latest product version
    """
    versions_to_create = 4
    product = ProductFactory.create()
    versions = ProductVersionFactory.create_batch(versions_to_create,
                                                  product=product)
    assert str(product) == "Product for {}".format(str(product.content_object))
    assert str(versions[0]) == "ProductVersion for {}, ${}".format(
        versions[0].description, versions[0].price)
    # Latest version should be the most recently created
    assert product.latest_version == versions[versions_to_create - 1]
コード例 #22
0
def test_apply_coupon_on_all_runs(include_future_runs):
    """
    Test that coupons added to all future course runs of a course,
    only if `include_future_runs = True`
    """

    course = CourseFactory.create()
    run = CourseRunFactory.create(course=course)
    coupon = CouponFactory.create(include_future_runs=include_future_runs)
    product = ProductFactory.create(content_object=run)
    CouponEligibilityFactory.create(coupon=coupon, product=product)

    # create another run with same course
    new_run = CourseRunFactory.create(course=course)
    new_product = ProductFactory.create(content_object=new_run)

    if include_future_runs:
        assert CouponEligibility.objects.filter(
            coupon=coupon, product=new_product
        ).exists()
    else:
        assert not CouponEligibility.objects.filter(
            coupon=coupon, product=new_product
        ).exists()
コード例 #23
0
ファイル: serializers_test.py プロジェクト: mitodl/mitxpro
def test_serialize_product(text_id, expected):
    """ Test that ProductSerializer has correct data """
    product_version = ProductVersionFactory.create(
        product=ProductFactory.create(
            content_object=CourseRunFactory.create(courseware_id=text_id)
        )
    )
    product = Product.objects.get(id=product_version.product.id)
    run = product.content_object
    serialized_data = ProductSerializer(instance=product).data
    assert serialized_data.get("title") == f"{run.title}: {expected}"
    assert serialized_data.get("product_type") == "courserun"
    assert serialized_data.get("id") == product.id
    assert serialized_data.get("price") == product.latest_version.price.to_eng_string()
    assert serialized_data.get("description") == product.latest_version.description
コード例 #24
0
def test_run_queryset(is_program):
    """
    run_queryset should return all runs related to the product
    """
    program = ProgramFactory.create()
    runs = [CourseRunFactory.create(course__program=program) for _ in range(4)]
    run = runs[2]
    obj = program if is_program else run
    product = ProductFactory.create(content_object=obj)

    def key_func(_run):
        return _run.id

    assert sorted(product.run_queryset,
                  key=key_func) == sorted(runs if is_program else [run],
                                          key=key_func)
コード例 #25
0
def test_make_product_sync_message():
    """Test make_deal_sync_message serializes a deal and returns a properly formatted sync message"""
    product = ProductFactory()
    contact_sync_message = api.make_product_sync_message(product.id)

    serialized_product = ProductSerializer(product).data
    assert contact_sync_message == [
        {
            "integratorObjectId": "{}-{}".format(
                settings.HUBSPOT_ID_PREFIX, product.id
            ),
            "action": "UPSERT",
            "changeOccurredTimestamp": any_instance_of(int),
            "propertyNameToValues": serialized_product,
        }
    ]
コード例 #26
0
ファイル: conftest.py プロジェクト: mitodl/mitxpro
def voucher_and_exact_match_with_coupon(voucher_and_exact_match):
    """
    Returns a voucher with exact matching and partial matching CourseRuns and valid coupons
    """
    context = voucher_and_exact_match
    company = context.company
    exact_match = context.exact_match
    product = ProductFactory(content_object=exact_match)
    coupon_eligibility = CouponEligibilityFactory(product=product)
    payment_version = CouponPaymentVersionFactory(amount=1, company=company)
    coupon_version = CouponVersionFactory(coupon=coupon_eligibility.coupon,
                                          payment_version=payment_version)
    return SimpleNamespace(
        **vars(voucher_and_exact_match),
        product=product,
        coupon_eligibility=coupon_eligibility,
        coupon_version=coupon_version,
        payment_version=payment_version,
    )
コード例 #27
0
ファイル: mail_api_test.py プロジェクト: mitodl/mitxpro
def test_send_enrollment_failure_message(mocker, is_program):
    """Test that send_enrollment_failure_message sends a message with proper formatting"""
    patched_django_mail = mocker.patch("ecommerce.mail_api.mail")
    product_object = (ProgramFactory.create()
                      if is_program else CourseRunFactory.create())
    product_version = ProductVersionFactory.create(
        product=ProductFactory.create(content_object=product_object))
    order = LineFactory.create(product_version=product_version).order
    details = "TestException on line 21"
    expected_message = "{name}({email}): Order #{order_id}, {error_obj} #{obj_id} ({obj_title})\n\n{details}".format(
        name=order.purchaser.username,
        email=order.purchaser.email,
        order_id=order.id,
        error_obj=("Program" if is_program else "Run"),
        obj_id=product_object.id,
        obj_title=product_object.title,
        details=details,
    )

    send_enrollment_failure_message(order, product_object, details)
    patched_django_mail.send_mail.assert_called_once()
    send_mail_args = patched_django_mail.send_mail.call_args[0]
    assert send_mail_args[0] == ENROLL_ERROR_EMAIL_SUBJECT
    assert send_mail_args[1] == expected_message
コード例 #28
0
def test_product_version_save_text_id_courserun():
    """ProductVersion.text_id should be set to CourseRun.courseware_id on save"""
    run = CourseRunFactory.create()
    product_version = ProductVersionFactory.create(
        product=ProductFactory.create(content_object=run))
    assert product_version.text_id == run.courseware_id
コード例 #29
0
def test_product_version_save_text_id_program():
    """ProductVersion.text_id should be set to Program.readable_id on save"""
    program = ProgramFactory.create()
    product_version = ProductVersionFactory.create(
        product=ProductFactory.create(content_object=program))
    assert product_version.text_id == program.readable_id