def test_get_course_availability(mitx_valid_data):
    """ Test that availability is calculated as expected """
    ocw_course = CourseFactory.create(platform=PlatformType.ocw.value)
    # test mitx course with raw_json
    assert get_course_availability(ocw_course) == AvailabilityType.current.value
    mitx_course_with_json = CourseFactory.create(
        course_id=mitx_valid_data["course_runs"][0]["key"],
        raw_json=mitx_valid_data,
        platform=PlatformType.mitx.value,
    )
    # test mitx course without raw_json
    assert (
        get_course_availability(mitx_course_with_json)
        == mitx_valid_data["course_runs"][0]["availability"]
    )
    mitx_course_no_json = CourseFactory.create(
        raw_json=None, platform=PlatformType.mitx.value
    )
    assert get_course_availability(mitx_course_no_json) is None
    # test mitx course without course_runs
    mitx_valid_data["course_runs"] = None  # pop course_runs json
    mitx_course_no_runs_json = CourseFactory.create(
        raw_json=mitx_valid_data, platform=PlatformType.mitx.value
    )
    assert get_course_availability(mitx_course_no_runs_json) is None
def test_list_course_endpoint(client):
    """Test course endpoint"""
    course = CourseFactory.create()
    # this should be filtered out
    CourseFactory.create(runs=None)

    resp = client.get(reverse("courses-list"))
    assert resp.data.get("count") == 1
    assert resp.data.get("results")[0]["id"] == course.id
Beispiel #3
0
def test_parse_mitx_json_data_overwrite(mocker, force_overwrite):
    """
    Test that valid mitx json data is skipped if it doesn't need an update
    """
    CourseFactory.create(course_id=mitx_valid_data["course_runs"][0]["key"],
                         last_modified=datetime.now().astimezone(pytz.utc))
    mock_save = mocker.patch(
        'course_catalog.tasks_helpers.CourseSerializer.save')
    parse_mitx_json_data(mitx_valid_data, force_overwrite=force_overwrite)
    assert mock_save.call_count == (1 if force_overwrite else 0)
def test_featured_courses_endpoint(client, featured):
    """Test featured courses endpoint"""
    course = CourseFactory.create(featured=featured)
    # this should be filtered out
    CourseFactory.create(runs=None)

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

    assert resp.data.get("count") == (1 if featured else 0)
    if featured:
        assert resp.data.get("results")[0]["id"] == course.id
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 #6
0
def test_import_all_xpro_files(settings, mocker, mocked_celery, mock_blacklist):
    """import_all_xpro_files should start chunked tasks which """
    setup_s3(settings)
    get_xpro_files_mock = mocker.patch(
        "course_catalog.tasks.get_xpro_files", autospec=True
    )
    courses = CourseFactory.create_batch(
        3, platform=PlatformType.xpro.value, published=True
    )
    CourseFactory.create_batch(3, platform=PlatformType.oll.value, published=False)

    with pytest.raises(mocked_celery.replace_exception_class):
        import_all_xpro_files.delay(3)
    assert mocked_celery.group.call_count == 1
    get_xpro_files_mock.si.assert_called_once_with([course.id for course in courses])
def test_get_course_endpoint(client):
    """Test course detail endpoint"""
    course = CourseFactory.create()

    resp = client.get(reverse("courses-detail", args=[course.id]))

    assert resp.data.get("course_id") == course.course_id
Beispiel #8
0
def test_es_course_serializer(offered_by):
    """
    Test that ESCourseSerializer correctly serializes a course object
    """
    course = CourseFactory.create(offered_by=offered_by)
    serialized = ESCourseSerializer(course).data

    assert_json_equal(
        serialized,
        {
            "object_type": COURSE_TYPE,
            "id": course.id,
            "course_id": course.course_id,
            "coursenum": course.course_id.split("+")[-1],
            "short_description": course.short_description,
            "full_description": course.full_description,
            "platform": course.platform,
            "title": course.title,
            "image_src": course.image_src,
            "topics": list(course.topics.values_list("name", flat=True)),
            "runs": [
                ESRunSerializer(course_run).data
                for course_run in course.runs.order_by("-best_start_date")
            ],
            "published": True,
            "offered_by": list(course.offered_by.values_list("name", flat=True)),
            "created": drf_datetime(course.created_on),
            "default_search_priority": 1,
            "minimum_price": minimum_price(course),
            "resource_relations": {"name": "resource"},
        },
    )
def test_sync_ocw_course_files(mock_ocw_learning_bucket, mocker, with_error):
    """Test that sync_ocw_course_files calls load_content_files for each run"""
    fake_data = '{"key": "data"}'
    mock_log = mocker.patch("course_catalog.api.log.exception")
    mock_transform = mocker.patch(
        "course_catalog.api.transform_content_files", return_value=fake_data
    )
    mock_load_content_files = mocker.patch(
        "course_catalog.api.load_content_files", autospec=True, return_value=[]
    )
    if with_error:
        mock_load_content_files.side_effect = Exception
    course = CourseFactory.create(published=True, platform=PlatformType.ocw.value)
    runs = course.runs.all()
    for run in runs:
        mock_ocw_learning_bucket.bucket.put_object(
            Key="{}/{}_master.json".format(run.url.split("/")[-1], run.run_id),
            Body=fake_data,
            ACL="public-read",
        )
    sync_ocw_course_files(ids=[course.id])
    assert mock_load_content_files.call_count == len(runs)
    for run in runs:
        mock_load_content_files.assert_any_call(run, mock_transform.return_value)
        if with_error:
            mock_log.assert_any_call("Error syncing files for course run %d", run.id)
Beispiel #10
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}"
Beispiel #11
0
def test_favorites_serializer():
    """
    Test that the favorite serializer generic foreign key works and also rejects unexpected classes
    """
    user = UserFactory.create()
    course = CourseFactory.create()
    bootcamp = BootcampFactory.create()
    user_list = UserListFactory.create(author=user)
    program = ProgramFactory.create()
    course_topic = CourseTopicFactory.create()

    favorite_item = FavoriteItem(user=user, item=course)
    serializer = FavoriteItemSerializer(favorite_item)
    assert serializer.data.get("content_data") == CourseSerializer(course).data

    favorite_item = FavoriteItem(user=user, item=bootcamp)
    serializer = FavoriteItemSerializer(favorite_item)
    assert serializer.data.get("content_data") == BootcampSerializer(
        bootcamp).data

    favorite_item = FavoriteItem(user=user, item=user_list)
    serializer = FavoriteItemSerializer(favorite_item)
    assert serializer.data.get("content_data") == UserListSerializer(
        user_list).data

    favorite_item = FavoriteItem(user=user, item=program)
    serializer = FavoriteItemSerializer(favorite_item)
    assert serializer.data.get("content_data") == ProgramSerializer(
        program).data

    favorite_item = FavoriteItem(user=user, item=course_topic)
    serializer = FavoriteItemSerializer(favorite_item)
    with pytest.raises(Exception):
        assert serializer.data.get("content_data").get("id") == course_topic.id
def test_upsert_course(mocker):
    """
    Tests that upsert_course calls update_field_values_by_query with the right parameters
    """
    patched_task = mocker.patch("search.tasks.upsert_course")
    course = CourseFactory.create()
    upsert_course(course.id)
    patched_task.delay.assert_called_once_with(course.id)
Beispiel #13
0
def test_serialize_bulk_courses(mocker):
    """
    Test that serialize_bulk_courses calls serialize_course_for_bulk for every existing course
    """
    mock_serialize_course = mocker.patch("search.serializers.serialize_course_for_bulk")
    courses = CourseFactory.create_batch(5)
    list(serialize_bulk_courses([course.id for course in Course.objects.all()]))
    for course in courses:
        mock_serialize_course.assert_any_call(course)
Beispiel #14
0
def test_serialize_course_related_models(offered_by):
    """
    Verify that a serialized course contains attributes for related objects
    """
    course = CourseFactory(offered_by=offered_by,
                           topics=CourseTopicFactory.create_batch(3))
    serializer = CourseSerializer(course)
    assert len(serializer.data["topics"]) == 3
    assert "name" in serializer.data["topics"][0].keys()
    assert len(serializer.data["runs"]) == 3
def test_index_content_files(mocker):
    """
    ES should try indexing content files for all runs in a course
    """
    mock_index_run_content_files = mocker.patch(
        "search.indexing_api.index_run_content_files", autospec=True)
    courses = CourseFactory.create_batch(2)
    index_course_content_files([course.id for course in courses])
    for course in courses:
        for run in course.runs.all():
            mock_index_run_content_files.assert_any_call(run.id)
Beispiel #16
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}"
def test_content_type_interactions_serializer_valid():
    """Verifies that valid data results in no errors"""
    course = CourseFactory.create()
    serializer = ContentTypeInteractionSerializer(
        data={
            "interaction_type": "view",
            "content_type": "course",
            "content_id": course.id,
        })

    assert serializer.is_valid() is True
    assert serializer.errors == {}
Beispiel #18
0
def test_serialize_course_for_bulk():
    """
    Test that serialize_course_for_bulk yields a valid ESCourseSerializer
    """
    course = CourseFactory.create()
    assert_json_equal(
        serialize_course_for_bulk(course),
        {
            "_id": gen_course_id(course.platform, course.course_id),
            **ESCourseSerializer(course).data,
        },
    )
Beispiel #19
0
def test_generate_duplicates_yaml(settings, mocker):
    """Test for generate_duplicates_yaml"""
    duplicate_course1 = CourseFactory.create(title="course1", platform="mitx")
    desired_course1 = CourseFactory.create(title="course1", platform="mitx")
    extra_offered_by = LearningResourceOfferorFactory.create()
    desired_course1.offered_by.add(extra_offered_by)

    duplicate_course2 = CourseFactory.create(title="course2", platform="mitx")
    desired_course2 = CourseFactory.create(title="course2", platform="mitx")

    settings.DUPLICATE_COURSES_URL = "url"
    duplicate_file_content = f"""mitx:
- course_id: {desired_course2.course_id}
  duplicate_course_ids:
  - {desired_course2.course_id}
  - {duplicate_course2.course_id}
  title: course2
"""
    mocker.patch(
        "requests.get",
        autospec=True,
        return_value=mocker.Mock(text=duplicate_file_content),
    )

    expected_output = f"""mitx:
- course_id: {desired_course1.course_id}
  duplicate_course_ids:
  - {desired_course1.course_id}
  - {duplicate_course1.course_id}
  title: course1
- course_id: {desired_course2.course_id}
  duplicate_course_ids:
  - {desired_course2.course_id}
  - {duplicate_course2.course_id}
  title: course2
"""

    assert generate_duplicates_yaml() == expected_output
def test_delete_course(mocker):
    """
    Tests that delete_course calls the delete tasks for the course and its content files
    """
    patched_delete_task = mocker.patch("search.task_helpers.delete_document")
    course = CourseFactory.create()
    course_es_id = gen_course_id(course.platform, course.course_id)
    content_files = [ContentFileFactory.create(run=run) for run in course.runs.all()]

    delete_course(course)
    patched_delete_task.delay.assert_any_call(course_es_id, COURSE_TYPE)
    for content_file in content_files:
        patched_delete_task.delay.assert_any_call(
            gen_content_file_id(content_file.key), COURSE_TYPE, routing=course_es_id
        )
def test_course_detail_endpoint_lists(user_client, user):
    """Test that author's list ids are included"""
    course = CourseFactory.create()
    user_lists = UserListFactory.create_batch(3, author=user)
    list_items = sorted(
        [
            UserListItemFactory.create(content_object=course,
                                       user_list=user_list)
            for user_list in user_lists
        ],
        key=lambda x: x.id,
    )
    resp = user_client.get(reverse("courses-detail", args=[course.id]))
    assert sorted(resp.data.get("lists"), key=lambda x: x["item_id"]) == [
        MicroUserListItemSerializer(list_item).data for list_item in list_items
    ]
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),
                )
def test_serialize_course_related_models():
    """
    Verify that a serialized course contains attributes for related objects
    """
    course = CourseFactory(
        topics=CourseTopicFactory.create_batch(3),
        prices=CoursePriceFactory.create_batch(2),
        instructors=CourseInstructorFactory.create_batch(2),
    )
    serializer = CourseSerializer(course)
    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"):
        assert attr in serializer.data["instructors"][0].keys()
    assert len(serializer.data["topics"]) == 3
    assert "name" in serializer.data["topics"][0].keys()
def test_user_list_items_endpoint_create_item_bad_data(client, user):
    """Test userlistitems endpoint for creating a UserListItem"""
    userlist = UserListFactory.create(author=user,
                                      privacy_level=PrivacyLevel.public.value)
    course = CourseFactory.create()

    client.force_login(user)

    data = {"content_type": "bad_content", "object_id": course.id}

    resp = client.post(reverse("userlistitems-list", args=[userlist.id]),
                       data=data,
                       format="json")
    assert resp.status_code == 400
    assert resp.json() == {
        "non_field_errors": ["Incorrect object type bad_content"],
        "error_type": "ValidationError",
    }
def test_find_similar_resources(mocker, client):
    """The view should return the results of the API method for finding similar resources"""
    course = CourseFactory.create()
    doc_vals = {
        "id": course.id,
        "object_id": COURSE_TYPE,
        "title": course.title,
        "short_description": course.short_description,
    }
    fake_response = {"similar": "resources"}
    similar_resources_mock = mocker.patch(
        "search.views.find_similar_resources",
        autospec=True,
        return_value=fake_response)
    resp = client.post(reverse("similar-resources"), data=doc_vals)
    assert resp.json() == fake_response
    similar_resources_mock.assert_called_once_with(user=AnonymousUser(),
                                                   value_doc=doc_vals)
def test_user_list_items_endpoint_create_item(client, user, is_author,
                                              mock_user_list_index):
    """Test userlistitems endpoint for creating a UserListItem"""
    author = UserFactory.create()
    userlist = UserListFactory.create(author=author,
                                      privacy_level=PrivacyLevel.public.value)
    course = CourseFactory.create()

    client.force_login(author if is_author else user)

    data = {"content_type": "course", "object_id": course.id}

    resp = client.post(reverse("userlistitems-list", args=[userlist.id]),
                       data=data,
                       format="json")
    assert resp.status_code == (201 if is_author else 403)
    if resp.status_code == 201:
        assert resp.json().get("object_id") == course.id
        mock_user_list_index.upsert_user_list.assert_called_once_with(
            userlist.id)
def test_popular_content_types(client, user, mocker):
    """Test the popular content types API"""
    # create 2 of each, generate interactions for only the first one
    # second one shouldn't show up in the results
    course = CourseFactory.create_batch(2)[0]
    bootcamp = BootcampFactory.create_batch(2)[0]
    program = ProgramFactory.create_batch(2)[0]
    user_list = UserListFactory.create_batch(2)[0]
    video = VideoFactory.create_batch(2)[0]

    # generate interactions with an increasing count
    interactions = [
        ContentTypeInteractionFactory.create_batch(count + 1,
                                                   content=content)[0]
        for count, content in enumerate(
            [user_list, bootcamp, video, course, program])
    ]

    response = client.get(reverse("popular_content-list"))

    # the response should be ordered such that items with a higher count of interactions are first
    # this ends up being the reverse order of `interactions` since we used `enumerate()`
    assert response.json() == {
        "results":
        PopularContentSerializer(
            [{
                "content_type_id": interaction.content_type_id,
                "content_id": interaction.content_id,
            } for interaction in reversed(interactions)],
            many=True,
            context={
                "request": mocker.Mock(user=user)
            },
        ).data,
        "next":
        None,
        "previous":
        None,
        "count":
        len(interactions),
    }
def test_popular_content_serializer(mocker, is_deleted, user):
    """Test PopularContentSerializer"""
    resources = [
        VideoFactory.create(),
        ProgramFactory.create(),
        CourseFactory.create(),
        UserListFactory.create(),
        BootcampFactory.create(),
    ]

    data = [{
        "content_type_id": ContentType.objects.get_for_model(resource).id,
        "content_id": resource.id,
    } for resource in resources]

    if is_deleted:
        for resource in resources:
            resource.delete()
        resources = []

    resources = [
        type(resource).objects.filter(
            id=resource.id).prefetch_list_items_for_user(
                user).annotate_is_favorite_for_user(user).first()
        for resource in resources
    ]

    context = {"request": mocker.Mock(user=user)}
    # NOTE: we test PopularContentSerializer instead of PopularContentListSerializer
    #       because the list serializer is never used directly, but rather many=True tells
    #       PopularContentSerializer to delegate to PopularContentListSerializer
    results = PopularContentSerializer(data, context=context, many=True).data

    # should be sorted by the same order they were passed in
    assert_json_equal(
        results,
        [
            GenericForeignKeyFieldSerializer(resource, context=context).data
            for resource in resources
        ],
    )
def test_course_report(client):
    """Test ocw course report"""
    CourseFactory.create(
        platform=PlatformType.ocw.value,
        learning_resource_type=ResourceType.course.value,
        published=False,
    )
    CourseFactory.create(
        platform=PlatformType.ocw.value,
        learning_resource_type=ResourceType.course.value,
        published=True,
        image_src="",
    )
    CourseFactory.create(
        platform=PlatformType.ocw.value,
        learning_resource_type=ResourceType.course.value,
        published=True,
        image_src="abc123",
    )
    CourseFactory.create(
        platform=PlatformType.ocw.value,
        learning_resource_type=ResourceType.ocw_resource.value,
        published=False,
    )

    username = "******"
    password = "******"
    User.objects.create_user(username=username, password=password)
    client.login(username=username, password=password)
    resp = client.get(reverse("ocw-course-report"))
    assert resp.data == {
        "total_number_of_ocw_courses": 3,
        "published_ocw_courses_with_image": 2,
        "unpublished_ocw_courses": 1,
        "ocw_courses_without_image": 1,
        "ocw_resources": 1,
    }
def test_content_type_interaction_create(client):
    """Test that the content type interaction API creates a record"""
    course = CourseFactory.create()
    content_type = ContentType.objects.get_for_model(course)

    assert ContentTypeInteraction.objects.count() == 0

    payload = {
        "interaction_type": "view",
        "content_type": content_type.name,
        "content_id": course.id,
    }

    response = client.post(reverse("interactions-list"), payload)

    assert response.status_code == status.HTTP_201_CREATED
    assert response.json() == payload

    assert ContentTypeInteraction.objects.count() == 1

    interaction = ContentTypeInteraction.objects.first()
    assert interaction.interaction_type == "view"
    assert interaction.content == course