I recently finished the course, and while going through it I happened upon a bug in the API side that I don’t think anyone has noticed. I’ll first describe the ‘bug’ that led me to movies.py: during the course, I set up a mongodb Docker container for local testing, and used the atlas cluster for prod. This container was mongodb v4.4, while the atlas cluster it has you set up is v4.2. Where I ran into an issue was the unit test for paging and the assert statement about the title of the movie on the last page of the paged results. From test_paging.py:
@pytest.mark.paging
def test_supports_paging_by_text(client):
filter = {'text': 'bank robbery'}
(movies0, results0) = get_movies(filter, 0, 20)
assert len(list(movies0)) == 20
assert results0 == 475
assert movies0[0].get('title') == 'The Bank'
last_page = int(475 / 20) # last_page = 23
(movies2, results2) = get_movies(filter, last_page, 20)
assert len(list(movies2)) == 15
assert movies2[0].get('title') == "Ugetsu"
That last assert was failing against the test DB (4.4), but succeeding if I ran it against the cluster (4.2). After seeing that it was giving ‘Skippy’ as the first title on the last page, I tried to figure out why and came to the conclusion it must be a difference in the way the cursor.sort() method behaves between the 2 versions. Since Ugetsu and Skippy had the same meta textscore, I hypothesized it must be using natural order when subsorting on equal fields in 4.2, and implicitly subsorting on additional fields (title? Since ‘S’ comes before ‘U’) in 4.4. Again this only happens if you have DB version 4.4, so if you follow the instructions and do everything against the cluster you won’t have any issues. So that’s fine, a ‘bug’ of my own creation. But while trying to figure this out, I noticed a bug in movies.py in how the paging is implemented when doing a text search.
If you do exactly what the unit test is doing, but using the actual website and scrolling, you’ll start to see something wrong. Here are lines 61-64 of movies.py:
search = request.args.get('text')
if search:
filters["text"] = search
return_filters["search"] = search
Notice it returns a filter key of ‘search’. This leads to the following behavior: opening the search panel, selecting text, then typing in ‘bank robbery’ renders the first 20 movies as expected, and as you can see from the debug output from flask:
"GET /api/v1/movies/search?text=bank%20robbery HTTP/1.1" 200 -
Now what happens if we scroll to the bottom? It should simply add &page=1 to the end of the GET, but since the return filter is ‘search’ this happens:
"GET /api/v1/movies/search?search=bank%20robbery&page=1 HTTP/1.1" 200 -
Now ‘search’ is not a valid key/type of search, so this part of the request is ignored, and what we see is the unfiltered collection of all movies but with page=1. This becomes obvious for all subsequent pages:
"GET /api/v1/movies/search?page=2 HTTP/1.1" 200 -
"GET /api/v1/movies/search?page=3 HTTP/1.1" 200 -
"GET /api/v1/movies/search?page=4 HTTP/1.1" 200 -
and so on…
The simple fix for this is to change line 64 in movies.py from
return_filters["search"] = search
to
return_filters["text"] = search
and now the filter is kept as expected for all pages > 0.
Since this is a bug in the actual front-end I doubt many would have noticed unless they tried as I did to replicate what the unit test was doing within the UI.
Just wanted to let the instructors know about 2 potential issues. Let me know if this helps anyone.