Beispiel #1
0
def test_load_run(run_exists):
    """Test that load_run loads the course run"""
    course = CourseFactory.create(runs=None)
    learning_resource_run = (LearningResourceRunFactory.create(
        content_object=course) if run_exists else
                             LearningResourceRunFactory.build())

    props = model_to_dict(
        LearningResourceRunFactory.build(
            run_id=learning_resource_run.run_id,
            platform=learning_resource_run.platform))
    del props["content_type"]
    del props["object_id"]
    del props["id"]

    assert LearningResourceRun.objects.count() == (1 if run_exists else 0)

    result = load_run(course, props)

    assert LearningResourceRun.objects.count() == 1

    assert result.content_object == course

    # assert we got a course run back
    assert isinstance(result, LearningResourceRun)

    for key, value in props.items():
        assert getattr(result,
                       key) == value, f"Property {key} should equal {value}"
def test_upcoming_courses_endpoint(client, kwargs, is_upcoming):
    """Test upcoming courses endpoint"""
    course = CourseFactory.create(runs=None)
    LearningResourceRunFactory.create(content_object=course, **kwargs)
    # this should be filtered out
    CourseFactory.create(runs=None)

    resp = client.get(reverse("courses-list") + "upcoming/")

    assert resp.data.get("count") == (1 if is_upcoming else 0)
    if is_upcoming:
        assert resp.data.get("results")[0]["id"] == course.id
Beispiel #3
0
def test_es_run_serializer(has_full_name):
    """
    Test that ESRunSerializer correctly serializes a run object
    """
    learning_resource_run = (
        LearningResourceRunFactory.create()
        if has_full_name
        else LearningResourceRunFactory.create(instructors__full_name=None)
    )
    serialized = ESRunSerializer(learning_resource_run).data

    assert_json_equal(
        serialized,
        {
            "id": learning_resource_run.id,
            "run_id": learning_resource_run.run_id,
            "short_description": learning_resource_run.short_description,
            "full_description": learning_resource_run.full_description,
            "language": learning_resource_run.language,
            "semester": learning_resource_run.semester,
            "year": int(learning_resource_run.year),
            "level": learning_resource_run.level,
            "start_date": learning_resource_run.start_date.strftime(ISOFORMAT),
            "end_date": learning_resource_run.end_date.strftime(ISOFORMAT),
            "enrollment_start": learning_resource_run.enrollment_start.strftime(
                ISOFORMAT
            ),
            "enrollment_end": learning_resource_run.enrollment_end.strftime(ISOFORMAT),
            "best_start_date": learning_resource_run.best_start_date,
            "best_end_date": learning_resource_run.best_end_date,
            "title": learning_resource_run.title,
            "image_src": learning_resource_run.image_src,
            "instructors": [
                (
                    instructor.full_name
                    if has_full_name
                    else " ".join([instructor.first_name, instructor.last_name])
                )
                for instructor in learning_resource_run.instructors.all()
            ],
            "prices": [
                ESCoursePriceSerializer(price).data
                for price in learning_resource_run.prices.all()
            ],
            "published": True,
            "availability": learning_resource_run.availability,
            "offered_by": list(
                learning_resource_run.offered_by.values_list("name", flat=True)
            ),
        },
    )
Beispiel #4
0
def test_load_content_file_error(mocker):
    """ Test that an exception in load_content_file is logged """
    learning_resource_run = LearningResourceRunFactory.create()
    mock_log = mocker.patch("course_catalog.etl.loaders.log.exception")
    load_content_file(learning_resource_run, {"uid": "badfile", "bad": "data"})
    mock_log.assert_called_once_with("ERROR syncing course file %s for run %d",
                                     "badfile", learning_resource_run.id)
def test_sync_xpro_course_files(mock_xpro_learning_bucket, mocker):
    """sync xpro courses from a tarball stored in S3"""
    mock_xpro_learning_bucket.bucket.put_object(
        Key="path/to/exported_courses_123.tar.gz",
        Body=open("test_json/exported_courses_12345.tar.gz", "rb").read(),
        ACL="public-read",
    )
    mock_load_content_files = mocker.patch(
        "course_catalog.api.load_content_files", autospec=True, return_value=[]
    )
    course_content_type = ContentType.objects.get_for_model(Course)
    run = LearningResourceRunFactory.create(
        platform=PlatformType.xpro.value,
        run_id="content-devops-0001",
        content_type=course_content_type,
    )
    course_id = run.object_id
    fake_data = '{"key": "data"}'
    mock_log = mocker.patch("course_catalog.api.log.exception")
    mock_transform = mocker.patch(
        "course_catalog.api.transform_content_files_xpro", return_value=fake_data
    )
    sync_xpro_course_files(ids=[course_id])
    assert mock_transform.call_count == 1
    assert mock_transform.call_args[0][0].endswith("content-devops-0001.tar.gz") is True
    mock_load_content_files.assert_called_once_with(run, fake_data)
    mock_log.assert_not_called()
def test_sync_xpro_course_files_no_courses(mock_xpro_learning_bucket, mocker):
    """If there are no matching runs for the given courses, it should be skipped"""
    mock_xpro_learning_bucket.bucket.put_object(
        Key="path/to/exported_courses_123.tar.gz",
        Body=open("test_json/exported_courses_12345.tar.gz", "rb").read(),
        ACL="public-read",
    )
    mock_load_content_files = mocker.patch(
        "course_catalog.api.load_content_files", autospec=True, return_value=[]
    )
    course_content_type = ContentType.objects.get_for_model(Course)
    LearningResourceRunFactory.create(
        platform=PlatformType.xpro.value,
        run_id="content-devops-0001",
        content_type=course_content_type,
    )
    sync_xpro_course_files(ids=[])
    mock_load_content_files.assert_not_called()
Beispiel #7
0
def test_load_instructors(instructor_exists):
    """Test that load_instructors creates and/or assigns instructors to the course run"""
    instructors = (CourseInstructorFactory.create_batch(3) if instructor_exists
                   else CourseInstructorFactory.build_batch(3))
    run = LearningResourceRunFactory.create(no_instructors=True)

    assert run.instructors.count() == 0

    load_instructors(run, [{
        "full_name": instructor.full_name
    } for instructor in instructors])

    assert run.instructors.count() == len(instructors)
Beispiel #8
0
def test_serialize_courserun_related_models():
    """
    Verify that a serialized course run contains attributes for related objects
    """
    courserun = LearningResourceRunFactory(
        prices=CoursePriceFactory.create_batch(2),
        instructors=CourseInstructorFactory.create_batch(2),
    )
    serializer = LearningResourceRunSerializer(courserun)
    assert "raw_json" not in serializer.data
    assert len(serializer.data["prices"]) == 2
    for attr in ("mode", "price"):
        assert attr in serializer.data["prices"][0].keys()
    assert len(serializer.data["instructors"]) == 2
    for attr in ("first_name", "last_name", "full_name"):
        assert attr in serializer.data["instructors"][0].keys()
def test_bulk_index_content_files(mocked_es, mocker, settings, errors,
                                  indexing_func_name, doc):  # pylint: disable=too-many-arguments
    """
    index functions for content files should call bulk with correct arguments
    """
    settings.ELASTICSEARCH_INDEXING_CHUNK_SIZE = 3
    course = CourseFactory.create()
    run = LearningResourceRunFactory.create(content_object=course)
    content_files = ContentFileFactory.create_batch(5, run=run)
    mock_get_aliases = mocker.patch("search.indexing_api.get_active_aliases",
                                    autospec=True,
                                    return_value=["a", "b"])
    bulk_mock = mocker.patch("search.indexing_api.bulk",
                             autospec=True,
                             return_value=(0, errors))
    mocker.patch(
        f"search.indexing_api.serialize_content_file_for_bulk",
        autospec=True,
        return_value=doc,
    )
    mocker.patch(
        f"search.indexing_api.serialize_content_file_for_bulk_deletion",
        autospec=True,
        return_value=doc,
    )

    index_func = getattr(indexing_api, indexing_func_name)
    if errors:
        with pytest.raises(ReindexException):
            index_func(run.id)
    else:
        index_func(run.id)
        for alias in mock_get_aliases.return_value:
            for chunk in chunks(
                [doc for _ in content_files],
                    chunk_size=settings.ELASTICSEARCH_INDEXING_CHUNK_SIZE,
            ):
                bulk_mock.assert_any_call(
                    mocked_es.conn,
                    chunk,
                    index=alias,
                    doc_type=GLOBAL_DOC_TYPE,
                    chunk_size=settings.ELASTICSEARCH_INDEXING_CHUNK_SIZE,
                    routing=gen_course_id(course.platform, course.course_id),
                )
Beispiel #10
0
def test_load_content_files(mocker, is_published):
    """Test that load_content_files calls the expected functions"""
    course_run = LearningResourceRunFactory.create(published=is_published)
    content_data = [{"a": "b"}, {"a": "c"}]
    mock_load_content_file = mocker.patch(
        "course_catalog.etl.loaders.load_content_file", autospec=True)
    mock_bulk_index = mocker.patch(
        "course_catalog.etl.loaders.search_task_helpers.index_run_content_files",
        autospec=True,
    )
    mock_bulk_delete = mocker.patch(
        "course_catalog.etl.loaders.search_task_helpers.delete_run_content_files",
        autospec=True,
    )
    load_content_files(course_run, content_data)
    assert mock_load_content_file.call_count == len(content_data)
    assert mock_bulk_index.call_count == (1 if is_published else 0)
    assert mock_bulk_delete.call_count == (0 if is_published else 1)
Beispiel #11
0
def test_load_prices(prices_exist):
    """Test that load_prices creates and/or assigns prices to the parent object"""
    prices = (CoursePriceFactory.create_batch(3)
              if prices_exist else CoursePriceFactory.build_batch(3))
    course_run = LearningResourceRunFactory.create(no_prices=True)

    assert course_run.prices.count() == 0

    load_prices(
        course_run,
        [{
            "price": price.price,
            "mode": price.mode,
            "upgrade_deadline": price.upgrade_deadline,
        } for price in prices],
    )

    assert course_run.prices.count() == len(prices)
Beispiel #12
0
def test_load_course(mock_upsert_tasks, course_exists, is_published,
                     blacklisted):
    """Test that load_course loads the course"""
    course = (CourseFactory.create(runs=None, published=is_published)
              if course_exists else CourseFactory.build())
    assert Course.objects.count() == (1 if course_exists else 0)
    assert LearningResourceRun.objects.count() == 0

    props = model_to_dict(
        CourseFactory.build(course_id=course.course_id,
                            platform=course.platform,
                            published=is_published))
    del props["id"]
    run = model_to_dict(
        LearningResourceRunFactory.build(platform=course.platform))
    del run["content_type"]
    del run["object_id"]
    del run["id"]
    props["runs"] = [run]

    blacklist = [course.course_id] if blacklisted else []

    result = load_course(props, blacklist, [])

    if course_exists and not is_published and not blacklisted:
        mock_upsert_tasks.delete_course.assert_called_with(result)
    elif is_published and not blacklisted:
        mock_upsert_tasks.upsert_course.assert_called_with(result.id)
    else:
        mock_upsert_tasks.delete_program.assert_not_called()
        mock_upsert_tasks.upsert_course.assert_not_called()

    assert Course.objects.count() == 1
    assert LearningResourceRun.objects.count() == 1

    # assert we got a course back
    assert isinstance(result, Course)

    for key, value in props.items():
        assert getattr(result,
                       key) == value, f"Property {key} should equal {value}"
Beispiel #13
0
def test_load_content_file():
    """Test that load_content_file saves a ContentFile object"""
    learning_resource_run = LearningResourceRunFactory.create()

    props = model_to_dict(
        ContentFileFactory.build(run_id=learning_resource_run.id))
    props.pop("run")
    props.pop("id")

    result = load_content_file(learning_resource_run, props)

    assert ContentFile.objects.count() == 1

    assert result.run == learning_resource_run

    # assert we got a content file back
    assert isinstance(result, ContentFile)

    for key, value in props.items():
        assert getattr(result,
                       key) == value, f"Property {key} should equal {value}"
Beispiel #14
0
def test_get_most_relevant_run():
    """Verify that most_relevant_run returns the correct run"""

    most_relevant_run = LearningResourceRunFactory.create(
        availability=AvailabilityType.archived.value,
        best_start_date=datetime(2019, 10, 1, tzinfo=pytz.utc),
        run_id="1",
    )
    LearningResourceRunFactory.create(
        availability=AvailabilityType.archived.value,
        best_start_date=datetime(2018, 10, 1, tzinfo=pytz.utc),
        run_id="2",
    )

    assert (get_most_relevant_run(
        LearningResourceRun.objects.filter(
            run_id__in=["1", "2"])) == most_relevant_run)

    most_relevant_run = LearningResourceRunFactory.create(
        availability=AvailabilityType.upcoming.value,
        best_start_date=datetime(2017, 10, 1, tzinfo=pytz.utc),
        run_id="3",
    )

    LearningResourceRunFactory.create(
        availability=AvailabilityType.upcoming.value,
        best_start_date=datetime(2020, 10, 1, tzinfo=pytz.utc),
        run_id="4",
    )

    assert (get_most_relevant_run(
        LearningResourceRun.objects.filter(
            run_id__in=["1", "2", "3", "4"])) == most_relevant_run)

    most_relevant_run = LearningResourceRunFactory.create(
        availability=AvailabilityType.current.value, run_id="5")

    assert (get_most_relevant_run(
        LearningResourceRun.objects.filter(
            run_id__in=["1", "2", "3", "4", "5"])) == most_relevant_run)
Beispiel #15
0
def test_load_duplicate_course(mock_upsert_tasks, course_exists,
                               course_id_is_duplicate,
                               duplicate_course_exists):
    """Test that load_course loads the course"""
    course = CourseFactory.create(
        runs=None) if course_exists else CourseFactory.build()

    duplicate_course = (CourseFactory.create(runs=None,
                                             platform=course.platform)
                        if duplicate_course_exists else CourseFactory.build())

    if course_exists and duplicate_course_exists:
        assert Course.objects.count() == 2
    elif course_exists or duplicate_course_exists:
        assert Course.objects.count() == 1
    else:
        assert Course.objects.count() == 0

    assert LearningResourceRun.objects.count() == 0

    duplicates = [{
        "course_id":
        course.course_id,
        "duplicate_course_ids": [course.course_id, duplicate_course.course_id],
    }]

    course_id = (duplicate_course.course_id
                 if course_id_is_duplicate else course.course_id)

    props = model_to_dict(
        CourseFactory.build(course_id=course_id, platform=course.platform))

    del props["id"]
    run = model_to_dict(
        LearningResourceRunFactory.build(platform=course.platform))
    del run["content_type"]
    del run["object_id"]
    del run["id"]
    props["runs"] = [run]

    result = load_course(props, [], duplicates)

    if course_id_is_duplicate and duplicate_course_exists:
        mock_upsert_tasks.delete_course.assert_called()

    mock_upsert_tasks.upsert_course.assert_called_with(result.id)

    assert Course.objects.count() == (2 if duplicate_course_exists else 1)

    assert LearningResourceRun.objects.count() == 1

    # assert we got a course back
    assert isinstance(result, Course)

    saved_course = Course.objects.filter(course_id=course.course_id).first()

    for key, value in props.items():
        assert getattr(result,
                       key) == value, f"Property {key} should equal {value}"
        assert (
            getattr(saved_course, key) == value
        ), f"Property {key} should be updated to {value} in the database"