示例#1
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
示例#2
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 [])
示例#3
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 [])
示例#4
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 [])
示例#5
0
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
示例#6
0
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
示例#7
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 [])
示例#8
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 [],
    )
示例#9
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])
示例#10
0
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,
    }
示例#11
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"
    )
示例#12
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)
示例#13
0
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
示例#14
0
def test_serialize_b2b_order_with_coupon(client, mocker):
    """Test that B2BOrderToDealSerializer produces the correct serialized data for an order with coupon"""

    product_version = ProductVersionFactory.create(price=10)
    payload = {"a": "payload"}
    mocker.patch(
        "b2b_ecommerce.views.generate_b2b_cybersource_sa_payload",
        autospec=True,
        return_value=payload,
    )
    coupon = B2BCouponFactory.create(
        product=product_version.product, discount_percent=Decimal(0.8)
    )
    num_seats = 10
    resp = client.post(
        reverse("b2b-checkout"),
        {
            "num_seats": num_seats,
            "email": "*****@*****.**",
            "product_version_id": product_version.id,
            "discount_code": coupon.coupon_code,
            "contract_number": "",
        },
    )

    assert resp.status_code == status.HTTP_200_OK
    assert B2BOrder.objects.count() == 1
    order = B2BOrder.objects.first()
    discount = round(Decimal(coupon.discount_percent) * 100, 2)
    serialized_data = B2BOrderToDealSerializer(instance=order).data
    assert serialized_data == {
        "id": order.id,
        "name": f"XPRO-B2BORDER-{order.id}",
        "stage": ORDER_STATUS_MAPPING[order.status],
        "discount_amount": discount.to_eng_string(),
        "amount": order.total_price.to_eng_string(),
        "close_date": (
            int(order.updated_on.timestamp() * 1000)
            if order.status == Order.FULFILLED
            else None
        ),
        "coupon_code": coupon.coupon_code,
        "company": coupon.company.name,
        "payment_type": None,
        "payment_transaction": None,
        "num_seats": num_seats,
        "discount_percent": round(
            Decimal(coupon.discount_percent) * 100, 2
        ).to_eng_string(),
        "status": order.status,
        "purchaser": format_hubspot_id(order.email),
    }
示例#15
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)
示例#16
0
def test_program_page_checkout_url_product(client, wagtail_basics):
    """
    The checkout URL in the program page context should include the product ID if a product exists
    for the given program
    """
    program_page = ProgramPageFactory.create()
    program_page.save_revision().publish()
    product_version = ProductVersionFactory.create(
        product__content_object=program_page.program
    )
    resp = client.get(program_page.get_url())
    checkout_url = resp.context["checkout_url"]
    assert f"product={product_version.product.id}" in checkout_url
示例#17
0
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
示例#18
0
def test_basket_thumbnail_program(basket_and_coupons, mock_context):
    """Basket thumbnail should be serialized for a program"""
    thumbnail_filename = "abcde.jpg"
    program_page = ProgramPageFactory.create(
        thumbnail_image__file__filename=thumbnail_filename
    )
    program = program_page.program
    product_version = ProductVersionFactory.create(product__content_object=program)
    data = FullProductVersionSerializer(
        instance=product_version, context=mock_context
    ).data
    assert (
        data["thumbnail_url"]
        == program_page.thumbnail_image.get_rendition(
            CATALOG_COURSE_IMG_WAGTAIL_FILL
        ).url
    )
示例#19
0
def test_basket_thumbnail_courserun(basket_and_coupons, mock_context):
    """Basket thumbnail should be serialized for a courserun"""
    thumbnail_filename = "abcde.jpg"
    course_page = CoursePageFactory.create(
        thumbnail_image__file__filename=thumbnail_filename
    )
    run = CourseRunFactory.create(course=course_page.course)
    product_version = ProductVersionFactory.create(product__content_object=run)
    data = FullProductVersionSerializer(
        instance=product_version, context=mock_context
    ).data
    assert (
        data["thumbnail_url"]
        == course_page.thumbnail_image.get_rendition(
            CATALOG_COURSE_IMG_WAGTAIL_FILL
        ).url
    )
示例#20
0
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
示例#21
0
def test_serialize_course_run(has_product):
    """Test CourseRun serialization"""
    faculty_names = ["Emma Jones", "Joe Smith"]
    course_run = CourseRunFactory.create()
    FacultyMembersPageFactory.create(
        parent=course_run.course.page,
        **{
            f"members__{idx}__member__name": name
            for idx, name in enumerate(faculty_names)
        },
    )
    product_id = (ProductVersionFactory.create(
        product__content_object=course_run).product.id
                  if has_product else None)

    course_run.refresh_from_db()

    data = CourseRunSerializer(course_run).data
    assert_drf_json_equal(
        data,
        {
            "title": course_run.title,
            "courseware_id": course_run.courseware_id,
            "run_tag": course_run.run_tag,
            "courseware_url": course_run.courseware_url,
            "start_date": drf_datetime(course_run.start_date),
            "end_date": drf_datetime(course_run.end_date),
            "enrollment_start": drf_datetime(course_run.enrollment_start),
            "enrollment_end": drf_datetime(course_run.enrollment_end),
            "expiration_date": drf_datetime(course_run.expiration_date),
            "current_price": course_run.current_price,
            "instructors": course_run.instructors,
            "id": course_run.id,
            "product_id": product_id,
        },
    )
示例#22
0
def courseware_objects():
    """Database objects that CSV data depends on"""
    run = CourseRunFactory.create(
        courseware_id="course-v1:edX+DemoX+Demo_Course")
    ProductVersionFactory.create(product__content_object=run)
示例#23
0
def test_serialize_course(mock_context, is_anonymous, all_runs):
    """Test Course serialization"""
    now = datetime.now(tz=pytz.UTC)
    if is_anonymous:
        mock_context["request"].user = AnonymousUser()
    if all_runs:
        mock_context["all_runs"] = True
    user = mock_context["request"].user
    course_run = CourseRunFactory.create(course__no_program=True, live=True)
    course = course_run.course
    topic = "a course topic"
    course.topics.set([CourseTopic.objects.create(name=topic)])

    # Create expired, enrollment_ended, future, and enrolled course runs
    CourseRunFactory.create(course=course,
                            end_date=now - timedelta(1),
                            live=True)
    CourseRunFactory.create(course=course,
                            enrollment_end=now - timedelta(1),
                            live=True)
    CourseRunFactory.create(course=course,
                            enrollment_start=now + timedelta(1),
                            live=True)
    enrolled_run = CourseRunFactory.create(course=course, live=True)
    unexpired_runs = [enrolled_run, course_run]
    CourseRunEnrollmentFactory.create(run=enrolled_run,
                                      **({} if is_anonymous else {
                                          "user": user
                                      }))

    # create products for all courses so the serializer shows them
    for run in CourseRun.objects.all():
        ProductVersionFactory.create(product__content_object=run)

    data = CourseSerializer(instance=course, context=mock_context).data

    if all_runs or is_anonymous:
        expected_runs = unexpired_runs
    else:
        expected_runs = [course_run]

    assert_drf_json_equal(
        data,
        {
            "title":
            course.title,
            "description":
            course.page.description,
            "readable_id":
            course.readable_id,
            "id":
            course.id,
            "courseruns": [
                CourseRunSerializer(run).data
                for run in sorted(expected_runs,
                                  key=lambda run: run.start_date)
            ],
            "thumbnail_url":
            f"http://localhost:8053{course.page.thumbnail_image.file.url}",
            "next_run_id":
            course.first_unexpired_run.id,
            "topics": [{
                "name": topic
            }],
        },
    )
示例#24
0
def test_serialize_program(mock_context, has_product):
    """Test Program serialization"""
    program = ProgramFactory.create()
    run1 = CourseRunFactory.create(course__program=program)
    course1 = run1.course
    run2 = CourseRunFactory.create(course__program=program)
    course2 = run2.course
    runs = ([run1, run2] +
            [CourseRunFactory.create(course=course1) for _ in range(2)] +
            [CourseRunFactory.create(course=course2) for _ in range(2)])
    faculty_names = ["Teacher 1", "Teacher 2"]
    FacultyMembersPageFactory.create(
        parent=program.page,
        **{
            f"members__{idx}__member__name": name
            for idx, name in enumerate(faculty_names)
        },
    )
    if has_product:
        ProductVersionFactory.create(product__content_object=program)
    topics = [
        CourseTopic.objects.create(name=f"topic{num}") for num in range(3)
    ]
    course1.topics.set([topics[0], topics[1]])
    course2.topics.set([topics[1], topics[2]])

    data = ProgramSerializer(instance=program, context=mock_context).data

    assert_drf_json_equal(
        data,
        {
            "title":
            program.title,
            "readable_id":
            program.readable_id,
            "id":
            program.id,
            "description":
            program.page.description,
            "courses": [
                CourseSerializer(instance=course,
                                 context={
                                     **mock_context, "filter_products": False
                                 }).data for course in [course1, course2]
            ],
            "thumbnail_url":
            f"http://localhost:8053{program.page.thumbnail_image.file.url}",
            "current_price":
            program.current_price,
            "start_date":
            sorted(runs, key=lambda run: run.start_date)
            [0].start_date.strftime(datetime_format),
            "end_date":
            sorted(runs, key=lambda run: run.end_date)[-1].end_date.strftime(
                datetime_format),
            "enrollment_start":
            sorted(runs, key=lambda run: run.enrollment_start)
            [0].enrollment_start.strftime(datetime_format),
            "url":
            f"http://localhost{program.page.get_url()}",
            "instructors": [{
                "name": name
            } for name in faculty_names],
            "topics": [{
                "name": topic.name
            } for topic in topics],
        },
    )
示例#25
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
示例#26
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