Ticket: Create/Update Comments

I’m stuck at Chapter 2, Ticket: Create/Update Comments. The test looks like this:

comment = {
    'text': 'fe-fi-fo-fum',
    'id': ''
}
...
@pytest.mark.create_update_comments
def test_update_comment(client):
    result = update_comment(comment['id'], user.email, 'foo foo foo', now)
    assert result.acknowledged is True

    comments = get_movie(movie_id).get('comments')
    assert result.raw_result.get('nModified') == 1
    assert comments[0].get('text') == 'foo foo foo'

And the instructions in db.py:

# TODO: Create/Update Comments
# Use the user_email and comment_id to select the proper comment, then
# update the "text" and "date" of the selected comment.

As you can see, the update_comment() method gets an empty string as comment_id. How am I supposed to select the proper comment with an empty string?

My attempt at solving it, looks like this so far:

if type(comment_id) == str:
    if len(comment_id) > 0:
        comment_id = ObjectId(comment_id)  # Use existing ObjectId
    else:
        comment_id = ObjectId()  # Create a new ObjectId

response = db.comments.update_one(
    {"user_email": user_email, "comment_id": comment_id},
    {"$set": {"text": text, "date": date}},
    upsert=True
)

But this fails at some of the later assertions, because apparently I’m not supposed to create a new document.

Read about upsert. What you read should help.

I have struggled some more with this now, and while I did find problems with my implementation, I still think there’s a bug in the test. :face_with_monocle:

First, I removed the upsert=True. There is an add_comment() method already, so it makes no sense to let update_comment() insert comments too.

But it makes no difference, since the test assumes that a comment will be found and a document will be updated/modified. However, no document will ever match an ObjectId based on an empty string:

comment = {
    'text': 'fe-fi-fo-fum',
    'id': ''
}

I also realized that whenever you use upsert=True, you have to explicitly set all the fields that you want in your document at a minimum, or you may end up with an incomplete document (in case there’s an insert rather than an update). So I also tried this:

response = db.comments.update_one(
    {"user_email": user_email, "comment_id": comment_id},
    {"$set": {"user_email": user_email, "comment_id": comment_id, "text": text, "date": date}},
    upsert=True
)

But again, this doesn’t help when the filter always fails to find a matching comment.

I have made tests with update_one() with upsert=True outside this project and found it to be working as expected.

Hi @Gustaf_Liljegren

Rather than setting the field to comment_id have you tried setting it to _id? In terms of the $set you do not need to include the _id/comment_id field or the user_email fields as you are not updating these fields rather you are just using them as the search criteria.

Let me know if this helps.

Kindest regards,
Eoin

Many thanks for helping out. I had another go at it and finally figured it out.

First, as you noticed, I had a misnamed _id field. But the bigger issue (and this is something I’d still argue is a problem with the test) is that the test_update_comment relies on test_add_comment. This line from test_add_comment

comment['id'] = result.inserted_id

is being used (or assumed) in the test_update_comment test:

result = update_comment(comment['id'], user.email, 'foo foo foo', now)

When I go through this course, I like to debug the tests separately to see what’s going on, but in this case, it didn’t work. I think you should be able to run the tests in any order.