Repository: WordPress/openverse-api

Issue:
Update sample data
## Problem
<!-- Describe a problem solved by this feature; or delete the section entirely. -->

The API has support for `filesize` and `filetype` fields after WordPress/openverse#266. These fields are not populated completely for image and audio samples.

| Media type | `filesize` | `filetype` |
|-|-|-|
| Image | ❌ | ❌ |
| Audio | ❌ | ✅ |

We also need to make sure these changes are reflected in the new data:

- https://github.com/WordPress/openverse-catalog/issues/263
- https://github.com/WordPress/openverse-catalog/issues/292

## Description
<!-- Describe the feature and how it solves the problem. -->
The presence of this data can help to ensure that these fields are working as expected.

## Implementation
<!-- Replace the [ ] with [x] to check the box. -->
- [ ] 🙋 I would be interested in implementing this feature.



Discussion:

Resolving patch:
diff --git a/openverse_api/catalog/api/examples/image_requests.py b/openverse_api/catalog/api/examples/image_requests.py
--- a/openverse_api/catalog/api/examples/image_requests.py
+++ b/openverse_api/catalog/api/examples/image_requests.py
@@ -5,7 +5,7 @@
 origin = os.getenv("AUDIO_REQ_ORIGIN", "https://api.openverse.engineering")
 
 auth = f'-H "Authorization: Bearer {token}"' if token else ""
-identifier = "29cb352c-60c1-41d8-bfa1-7d6f7d955f63"
+identifier = "cdbd3bf6-1745-45bb-b399-61ee149cd58a"
 
 syntax_examples = {
     "using single query parameter": "test",
@@ -30,7 +30,7 @@
 
 image_search_curl = f"""
 # Search for images titled "Bust" by Talbot
-curl {auth} "{origin}/v1/images/?title=Bust&creator=Talbot"
+curl {auth} "{origin}/v1/images/?title=Train&creator=Trolle"
 """
 
 image_stats_curl = f"""
diff --git a/openverse_api/catalog/api/examples/image_responses.py b/openverse_api/catalog/api/examples/image_responses.py
--- a/openverse_api/catalog/api/examples/image_responses.py
+++ b/openverse_api/catalog/api/examples/image_responses.py
@@ -3,20 +3,36 @@
 
 origin = os.getenv("AUDIO_REQ_ORIGIN", "https://api.openverse.engineering")
 
-identifier = "29cb352c-60c1-41d8-bfa1-7d6f7d955f63"
+identifier = "cdbd3bf6-1745-45bb-b399-61ee149cd58a"
 
 base_image = {
     "id": identifier,
-    "title": "Bust of Patroclus (photograph; calotype; salt print)",
-    "foreign_landing_url": "https://collection.sciencemuseumgroup.org.uk/objects/co8554747/bust-of-patroclus-photograph-calotype-salt-print",  # noqa
-    "creator": "William Henry Fox Talbot",
-    "url": "https://coimages.sciencemuseumgroup.org.uk/images/439/67/large_1937_1281_0001__0001_.jpg",  # noqa
-    "license": "by-nc-nd",
-    "license_version": "4.0",
-    "license_url": "https://creativecommons.org/licenses/by-nc-nd/4.0/",
-    "provider": "sciencemuseum",
-    "source": "sciencemuseum",
-    "category": None,
+    "title": "Train area in Copenhagen South / Tog område i Syd København",
+    "foreign_landing_url": "https://www.flickr.com/photos/126744325@N07/51745389858",
+    "creator": "Kristoffer Trolle",
+    "creator_url": "https://www.flickr.com/photos/126744325@N07",
+    "url": "https://live.staticflickr.com/65535/51745389858_c10358e1a3_b.jpg",
+    "license": "by",
+    "license_version": "2.0",
+    "license_url": "https://creativecommons.org/licenses/by/2.0/",
+    "provider": "flickr",
+    "source": "flickr",
+    "category": "photograph",
+    "tags": [
+        {"name": "copenhagen"},
+        {"name": "danmark"},
+        {"name": "denmark"},
+        {"name": "dsb"},
+        {"name": "fujifilmxf35mmf2rwr"},
+        {"name": "fujifilmxh1"},
+        {"name": "københavn"},
+        {"name": "område"},
+        {"name": "south"},
+        {"name": "syd"},
+        {"name": "tiffenblackpromist14filter"},
+        {"name": "tog"},
+        {"name": "train"},
+    ],
     "thumbnail": f"{origin}/v1/images/{identifier}/thumb/",
     "detail_url": f"{origin}/v1/images/{identifier}/",
     "related_url": f"{origin}/v1/images/{identifier}/related/",
@@ -52,35 +68,14 @@
             "display_name": "Flickr",
             "source_url": "https://www.flickr.com",
             "logo_url": None,
-            "media_count": 1000,
-        },
-        {
-            "source_name": "rawpixel",
-            "display_name": "rawpixel",
-            "source_url": "https://www.rawpixel.com",
-            "logo_url": None,
-            "media_count": 1000,
-        },
-        {
-            "source_name": "sciencemuseum",
-            "display_name": "Science Museum",
-            "source_url": "https://www.sciencemuseum.org.uk",
-            "logo_url": None,
-            "media_count": 1000,
+            "media_count": 2500,
         },
         {
             "source_name": "stocksnap",
             "display_name": "StockSnap",
             "source_url": "https://stocksnap.io",
             "logo_url": None,
-            "media_count": 1000,
-        },
-        {
-            "source_name": "wikimedia",
-            "display_name": "Wikimedia",
-            "source_url": "https://commons.wikimedia.org",
-            "logo_url": None,
-            "media_count": 1000,
+            "media_count": 2500,
         },
     ]
 }
@@ -88,13 +83,11 @@
 image_detail_200_example = {
     "application/json": base_image
     | {
-        "attribution": '"Bust of Patroclus (photograph; calotype; salt print)" by William Henry Fox Talbot is licensed under CC-BY-NC-ND 4.0. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-nd/4.0/.',  # noqa
-        "height": 1536,
-        "width": 1276,
-        "tags": None,
-        "creator_url": None,
-        "filesize": None,
-        "filetype": None,
+        "attribution": '"Train area in Copenhagen South / Tog område i Syd København" by Kristoffer Trolle is licensed under CC-BY 2.0. To view a copy of this license, visit https://creativecommons.org/licenses/by/2.0/.',  # noqa
+        "height": 683,
+        "width": 1024,
+        "filesize": "157497",
+        "filetype": "jpg",
     }
 }
 
@@ -134,12 +127,12 @@
     "application/json": {
         "version": "1.0",
         "type": "photo",
-        "width": 1276,
-        "height": 1536,
-        "title": "Bust of Patroclus (photograph; calotype; salt print)",
-        "author_name": "William Henry Fox Talbot",
-        "author_url": None,
-        "license_url": "https://creativecommons.org/licenses/by-nc-nd/4.0/",
+        "width": 1024,
+        "height": 683,
+        "title": "Train area in Copenhagen South / Tog område i Syd København",
+        "author_name": "Kristoffer Trolle",
+        "author_url": "https://www.flickr.com/photos/126744325@N07",
+        "license_url": "https://creativecommons.org/licenses/by/2.0/",
     }
 }
 
diff --git a/openverse_api/catalog/api/utils/validate_images.py b/openverse_api/catalog/api/utils/validate_images.py
--- a/openverse_api/catalog/api/utils/validate_images.py
+++ b/openverse_api/catalog/api/utils/validate_images.py
@@ -1,9 +1,9 @@
 import logging
 import time
 
+import django_redis
 import grequests
 from catalog.api.utils.dead_link_mask import get_query_mask, save_query_mask
-from django_redis import get_redis_connection
 
 
 log = logging.getLogger(__name__)
@@ -22,7 +22,7 @@ def validate_images(query_hash, start_slice, results, image_urls):
         return
     start_time = time.time()
     # Pull matching images from the cache.
-    redis = get_redis_connection("default")
+    redis = django_redis.get_redis_connection("default")
     cache_prefix = "valid:"
     cached_statuses = redis.mget([cache_prefix + url for url in image_urls])
     cached_statuses = [


Test patch:
diff --git a/analytics/tests.py b/analytics/tests.py
--- a/analytics/tests.py
+++ b/analytics/tests.py
@@ -25,7 +25,7 @@
 
 API_URL = os.getenv("ANALYTICS_SERVER_URL", "http://localhost:8090")
 session_id = "00000000-0000-0000-0000-000000000000"
-result_id = "29cb352c-60c1-41d8-bfa1-7d6f7d955f63"
+result_id = "cdbd3bf6-1745-45bb-b399-61ee149cd58a"
 test_query = "integration test"
 engine = create_engine(settings.DATABASE_CONNECTION)
 session_maker = sessionmaker(bind=engine)
diff --git a/openverse_api/test/auth_test.py b/openverse_api/test/auth_test.py
new file mode 100644
--- /dev/null
+++ b/openverse_api/test/auth_test.py
@@ -0,0 +1,80 @@
+import time
+import uuid
+from test.constants import API_URL
+
+import pytest
+from catalog.api.models import OAuth2Verification
+from django.urls import reverse
+from django.utils.http import urlencode
+
+
+@pytest.mark.django_db
+@pytest.fixture
+def test_auth_tokens_registration(client):
+    data = {
+        "name": f"INTEGRATION TEST APPLICATION {uuid.uuid4()}",
+        "description": "A key for testing the OAuth2 registration process.",
+        "email": "example@example.org",
+    }
+    res = client.post(
+        "/v1/auth_tokens/register",
+        data,
+        verify=False,
+    )
+    assert res.status_code == 201
+    res_data = res.json()
+    print(res_data)
+    return res_data
+
+
+@pytest.mark.django_db
+@pytest.fixture
+def test_auth_token_exchange(client, test_auth_tokens_registration):
+    client_id = test_auth_tokens_registration["client_id"]
+    client_secret = test_auth_tokens_registration["client_secret"]
+    data = urlencode(
+        {
+            "client_id": client_id,
+            "client_secret": client_secret,
+            "grant_type": "client_credentials",
+        }
+    )
+    res = client.post(
+        "/v1/auth_tokens/token/",
+        data,
+        "application/x-www-form-urlencoded",
+        verify=False,
+    )
+    res_data = res.json()
+    assert "access_token" in res_data
+    print(res_data)
+    return res_data
+
+
+@pytest.mark.django_db
+def test_auth_email_verification(client, test_auth_token_exchange):
+    # This test needs to cheat by looking in the database, so it will be
+    # skipped in non-local environments.
+    if API_URL == "http://localhost:8000":
+        verify = OAuth2Verification.objects.last()
+        code = verify.code
+        path = reverse("verify-email", args=[code])
+        res = client.get(path)
+        assert res.status_code == 200
+        test_auth_rate_limit_reporting(client, test_auth_token_exchange, verified=True)
+
+
+@pytest.mark.django_db
+def test_auth_rate_limit_reporting(client, test_auth_token_exchange, verified=False):
+    # We're anonymous still, so we need to wait a second before exchanging
+    # the token.
+    time.sleep(1)
+    token = test_auth_token_exchange["access_token"]
+    res = client.get("/v1/rate_limit", HTTP_AUTHORIZATION=f"Bearer {token}")
+    res_data = res.json()
+    if verified:
+        assert res_data["rate_limit_model"] == "standard"
+        assert res_data["verified"] is True
+    else:
+        assert res_data["rate_limit_model"] == "standard"
+        assert res_data["verified"] is False
diff --git a/openverse_api/test/dead_link_filter_test.py b/openverse_api/test/dead_link_filter_test.py
new file mode 100644
--- /dev/null
+++ b/openverse_api/test/dead_link_filter_test.py
@@ -0,0 +1,136 @@
+from test.constants import API_URL
+from unittest.mock import MagicMock, patch
+
+import pytest
+import requests
+
+
+def _patch_redis():
+    def redis_mget(keys, *_, **__):
+        """
+        Patch for ``redis.mget`` used by ``validate_images`` to use validity
+        information from the cache
+        """
+        return [None] * len(keys)
+
+    mock_conn = MagicMock()
+    mock_conn.mget = MagicMock(side_effect=redis_mget)
+    return patch("django_redis.get_redis_connection", return_value=mock_conn)
+
+
+def _patch_grequests():
+    def grequests_map(reqs, *_, **__):
+        """
+        Patch for ``grequests.map`` used by ``validate_images`` to filter
+        and remove dead links
+        """
+        responses = []
+        for idx in range(len(list(reqs))):
+            mocked_res = MagicMock()
+            mocked_res.status_code = 200 if idx % 10 != 0 else 404
+            responses.append(mocked_res)
+        return responses
+
+    return patch("grequests.map", side_effect=grequests_map)
+
+
+@pytest.mark.django_db
+@_patch_redis()
+@_patch_grequests()
+def test_dead_link_filtering(mocked_map, _, client):
+    path = "/v1/images/"
+    query_params = {"q": "*", "page_size": 100}
+
+    # Make a request that does not filter dead links...
+    res_with_dead_links = client.get(
+        path,
+        query_params | {"filter_dead": False},
+    )
+    # ...and ensure that our patched function was not called
+    mocked_map.assert_not_called()
+
+    # Make a request that filters dead links...
+    res_without_dead_links = client.get(
+        path,
+        query_params | {"filter_dead": True},
+    )
+    # ...and ensure that our patched function was called
+    mocked_map.assert_called()
+
+    assert res_with_dead_links.status_code == 200
+    assert res_without_dead_links.status_code == 200
+
+    data_with_dead_links = res_with_dead_links.json()
+    data_without_dead_links = res_without_dead_links.json()
+
+    res_1_ids = set(result["id"] for result in data_with_dead_links["results"])
+    res_2_ids = set(result["id"] for result in data_without_dead_links["results"])
+    assert bool(res_1_ids - res_2_ids)
+
+
+@pytest.fixture
+def search_factory(client):
+    """
+    Allows passing url parameters along with a search request.
+    """
+
+    def _parameterized_search(**kwargs):
+        response = requests.get(f"{API_URL}/v1/images", params=kwargs, verify=False)
+        assert response.status_code == 200
+        parsed = response.json()
+        return parsed
+
+    return _parameterized_search
+
+
+@pytest.fixture
+def search_without_dead_links(search_factory):
+    """
+    Here we pass filter_dead = True.
+    """
+
+    def _search_without_dead_links(**kwargs):
+        return search_factory(filter_dead=True, **kwargs)
+
+    return _search_without_dead_links
+
+
+@pytest.mark.django_db
+def test_page_size_removing_dead_links(search_without_dead_links):
+    """
+    We have about 500 dead links in the sample data and should have around
+    8 dead links in the first 100 results on a query composed of a single
+    wildcard operator.
+
+    Test whether the number of results returned is equal to the requested
+    page_size of 100.
+    """
+    data = search_without_dead_links(q="*", page_size=100)
+    assert len(data["results"]) == 100
+
+
+@pytest.mark.django_db
+def test_page_consistency_removing_dead_links(search_without_dead_links):
+    """
+    Test the results returned in consecutive pages are never repeated when
+    filtering out dead links.
+    """
+    total_pages = 30
+    page_size = 5
+
+    page_results = []
+    for page in range(1, total_pages + 1):
+        page_data = search_without_dead_links(q="*", page_size=page_size, page=page)
+        page_results += page_data["results"]
+
+    def no_duplicates(xs):
+        s = set()
+        for x in xs:
+            if x in s:
+                return False
+            s.add(x)
+        return True
+
+    ids = list(map(lambda x: x["id"], page_results))
+    # No results should be repeated so we should have no duplicate ids
+    assert no_duplicates(ids)
diff --git a/openverse_api/test/image_integration_test.py b/openverse_api/test/image_integration_test.py
--- a/openverse_api/test/image_integration_test.py
+++ b/openverse_api/test/image_integration_test.py
@@ -22,6 +22,9 @@
 import requests
 
 
+identifier = "cdbd3bf6-1745-45bb-b399-61ee149cd58a"
+
+
 @pytest.fixture
 def image_fixture():
     response = requests.get(f"{API_URL}/v1/images?q=dog", verify=False)
@@ -65,7 +68,7 @@ def test_audio_report(image_fixture):
 
 def test_oembed_endpoint_for_json():
     params = {
-        "url": "https://any.domain/any/path/29cb352c-60c1-41d8-bfa1-7d6f7d955f63",
+        "url": f"https://any.domain/any/path/{identifier}",
         # 'format': 'json' is the default
     }
     response = requests.get(
@@ -75,14 +78,14 @@ def test_oembed_endpoint_for_json():
     assert response.headers["Content-Type"] == "application/json"
 
     parsed = response.json()
-    assert parsed["width"] == 1276
-    assert parsed["height"] == 1536
-    assert parsed["license_url"] == "https://creativecommons.org/licenses/by-nc-nd/4.0/"
+    assert parsed["width"] == 1024
+    assert parsed["height"] == 683
+    assert parsed["license_url"] == "https://creativecommons.org/licenses/by/2.0/"
 
 
 def test_oembed_endpoint_for_xml():
     params = {
-        "url": "https://any.domain/any/path/29cb352c-60c1-41d8-bfa1-7d6f7d955f63",
+        "url": f"https://any.domain/any/path/{identifier}",
         "format": "xml",
     }
     response = requests.get(
@@ -93,9 +96,9 @@ def test_oembed_endpoint_for_xml():
 
     response_body_as_xml = ET.fromstring(response.content)
     xml_tree = ET.ElementTree(response_body_as_xml)
-    assert xml_tree.find("width").text == "1276"
-    assert xml_tree.find("height").text == "1536"
+    assert xml_tree.find("width").text == "1024"
+    assert xml_tree.find("height").text == "683"
     assert (
         xml_tree.find("license_url").text
-        == "https://creativecommons.org/licenses/by-nc-nd/4.0/"
+        == "https://creativecommons.org/licenses/by/2.0/"
     )
diff --git a/openverse_api/test/v1_integration_test.py b/openverse_api/test/v1_integration_test.py
--- a/openverse_api/test/v1_integration_test.py
+++ b/openverse_api/test/v1_integration_test.py
@@ -4,18 +4,13 @@
 """
 
 import json
-import time
-import uuid
 from test.constants import API_URL
 
-import catalog.settings
 import pytest
 import requests
 from catalog.api.licenses import LICENSE_GROUPS
-from catalog.api.models import Image, OAuth2Verification
+from catalog.api.models import Image
 from catalog.api.utils.watermark import watermark
-from django.db.models import Max
-from django.urls import reverse
 
 
 @pytest.fixture
@@ -106,105 +101,18 @@ def test_creator_quotation_grouping():
     down their searches more effectively.
     """
     no_quotes = json.loads(
-        requests.get(
-            f"{API_URL}/v1/images?creator=william%20ford%stanley", verify=False
-        ).text
+        requests.get(f"{API_URL}/v1/images?creator=Steve%20Wedgwood", verify=False).text
     )
     quotes = json.loads(
         requests.get(
-            f'{API_URL}/v1/images?creator="william%20ford%stanley"', verify=False
+            f'{API_URL}/v1/images?creator="Steve%20Wedgwood"', verify=False
         ).text
     )
     # Did quotation marks actually narrow down the search?
     assert len(no_quotes["results"]) > len(quotes["results"])
     # Did we find only William Ford Stanley works, or also by others?
     for result in quotes["results"]:
-        assert "William Ford Stanley" in result["creator"]
-
-
-@pytest.fixture
-def test_auth_tokens_registration():
-    payload = {
-        "name": f"INTEGRATION TEST APPLICATION {uuid.uuid4()}",
-        "description": "A key for testing the OAuth2 registration process.",
-        "email": "example@example.org",
-    }
-    response = requests.post(
-        f"{API_URL}/v1/auth_tokens/register", json=payload, verify=False
-    )
-    parsed_response = json.loads(response.text)
-    assert response.status_code == 201
-    return parsed_response
-
-
-@pytest.fixture
-def test_auth_token_exchange(test_auth_tokens_registration):
-    client_id = test_auth_tokens_registration["client_id"]
-    client_secret = test_auth_tokens_registration["client_secret"]
-    token_exchange_request = (
-        f"client_id={client_id}&"
-        f"client_secret={client_secret}&"
-        "grant_type=client_credentials"
-    )
-    headers = {
-        "content-type": "application/x-www-form-urlencoded",
-        "cache-control": "no-cache",
-    }
-    response = json.loads(
-        requests.post(
-            f"{API_URL}/v1/auth_tokens/token/",
-            data=token_exchange_request,
-            headers=headers,
-            verify=False,
-        ).text
-    )
-    assert "access_token" in response
-    return response
-
-
-def test_auth_rate_limit_reporting(test_auth_token_exchange, verified=False):
-    # We're anonymous still, so we need to wait a second before exchanging
-    # the token.
-    time.sleep(1)
-    token = test_auth_token_exchange["access_token"]
-    headers = {"Authorization": f"Bearer {token}"}
-    response = json.loads(
-        requests.get(f"{API_URL}/v1/rate_limit", headers=headers).text
-    )
-    if verified:
-        assert response["rate_limit_model"] == "standard"
-        assert response["verified"] is True
-    else:
-        assert response["rate_limit_model"] == "standard"
-        assert response["verified"] is False
-
-
-@pytest.fixture(scope="session")
-def django_db_setup():
-    if API_URL == "http://localhost:8000":
-        catalog.settings.DATABASES["default"] = {
-            "ENGINE": "django.db.backends.postgresql",
-            "HOST": "127.0.0.1",
-            "NAME": "openledger",
-            "PASSWORD": "deploy",
-            "USER": "deploy",
-            "PORT": 5432,
-        }
-
-
-@pytest.mark.django_db
-def test_auth_email_verification(test_auth_token_exchange, django_db_setup):
-    # This test needs to cheat by looking in the database, so it will be
-    # skipped in non-local environments.
-    if API_URL == "http://localhost:8000":
-        _id = OAuth2Verification.objects.aggregate(Max("id"))["id__max"]
-        verify = OAuth2Verification.objects.get(id=_id)
-        code = verify.code
-        path = reverse("verify-email", args=[code])
-        url = f"{API_URL}{path}"
-        response = requests.get(url)
-        assert response.status_code == 200
-        test_auth_rate_limit_reporting(test_auth_token_exchange, verified=True)
+        assert "Steve Wedgwood" in result["creator"]
 
 
 @pytest.mark.skip(reason="Unmaintained feature/grequests ssl recursion bug")
@@ -306,106 +214,6 @@ def test_extension_filter():
         assert ".jpg" in result["url"]
 
 
-@pytest.fixture
-def search_factory():
-    """
-    Allows passing url parameters along with a search request.
-    """
-
-    def _parameterized_search(**kwargs):
-        response = requests.get(f"{API_URL}/v1/images", params=kwargs, verify=False)
-        assert response.status_code == 200
-        parsed = response.json()
-        return parsed
-
-    return _parameterized_search
-
-
-@pytest.fixture
-def search_with_dead_links(search_factory):
-    """
-    Here we pass filter_dead = False.
-    """
-
-    def _search_with_dead_links(**kwargs):
-        return search_factory(filter_dead=False, **kwargs)
-
-    return _search_with_dead_links
-
-
-@pytest.fixture
-def search_without_dead_links(search_factory):
-    """
-    Here we pass filter_dead = True.
-    """
-
-    def _search_without_dead_links(**kwargs):
-        return search_factory(filter_dead=True, **kwargs)
-
-    return _search_without_dead_links
-
-
-def test_page_size_removing_dead_links(search_without_dead_links):
-    """
-    We have about 500 dead links in the sample data and should have around
-    8 dead links in the first 100 results on a query composed of a single
-    wildcard operator.
-
-    Test whether the number of results returned is equal to the requested
-    page_size of 100.
-    """
-    data = search_without_dead_links(q="*", page_size=100)
-    assert len(data["results"]) == 100
-
-
-def test_dead_links_are_correctly_filtered(
-    search_with_dead_links, search_without_dead_links
-):
-    """
-    Test the results for the same query with and without dead links are
-    actually different.
-
-    We use the results' id to compare them.
-    """
-    data_with_dead_links = search_with_dead_links(q="*", page_size=100)
-    data_without_dead_links = search_without_dead_links(q="*", page_size=100)
-
-    comparisons = []
-    for result_1 in data_with_dead_links["results"]:
-        for result_2 in data_without_dead_links["results"]:
-            comparisons.append(result_1["id"] == result_2["id"])
-
-    # Some results should be different
-    # so we should have less than 100 True comparisons
-    assert comparisons.count(True) < 100
-
-
-def test_page_consistency_removing_dead_links(search_without_dead_links):
-    """
-    Test the results returned in consecutive pages are never repeated when
-    filtering out dead links.
-    """
-    total_pages = 30
-    page_size = 5
-
-    page_results = []
-    for page in range(1, total_pages + 1):
-        page_data = search_without_dead_links(q="*", page_size=page_size, page=page)
-        page_results += page_data["results"]
-
-    def no_duplicates(xs):
-        s = set()
-        for x in xs:
-            if x in s:
-                return False
-            s.add(x)
-        return True
-
-    ids = list(map(lambda x: x["id"], page_results))
-    # No results should be repeated so we should have no duplicate ids
-    assert no_duplicates(ids)
-
-
 @pytest.fixture
 def recommendation_factory():
     """