SERPent / tests /test_serp_urls.py
Claude
Test the search URLs, and delete the harness that hid them
232eb80 unverified
Raw History Blame Contribute Delete
3.14 kB
"""The search URLs each scraper navigates to.
These had no coverage at all, and the gap was invisible: the Playwright
tests drive a real browser but patch `page.goto` with a stand-in that
accepts the URL and discards it, so everything downstream of navigation
was verified and everything upstream was not. Mutation testing confirmed
it - replacing the Bing URL with a constant, dropping `&num=` from the
Scholar URL, and making the Google Patents URL ignore the query all left
the full suite green. A scraper that searched for the wrong thing entirely
passed.
These are pure string assertions: no browser, no fixtures, microseconds.
"""
import pytest
from serp import (
bing_search_url,
brave_search_url,
google_patents_search_url,
google_scholar_url,
)
class TestQueryIsIncluded:
"""The single most important property: the URL must ask for what the
caller asked for."""
@pytest.mark.parametrize("builder", [
bing_search_url, brave_search_url, google_patents_search_url, google_scholar_url])
def test_the_query_reaches_the_url(self, builder):
assert "widgets" in builder("widgets", 10)
@pytest.mark.parametrize("builder", [
bing_search_url, brave_search_url, google_patents_search_url, google_scholar_url])
def test_a_different_query_produces_a_different_url(self, builder):
assert builder("widgets", 10) != builder("gadgets", 10)
class TestQueryIsEncoded:
"""A raw query in a URL is both a correctness bug (spaces, ampersands
and hashes truncate or corrupt the search) and the kind of thing that
turns user input into URL structure."""
@pytest.mark.parametrize("builder", [
bing_search_url, brave_search_url, google_patents_search_url, google_scholar_url])
def test_spaces_are_encoded(self, builder):
url = builder("agentic ai", 10)
assert "agentic+ai" in url or "agentic%20ai" in url
assert "agentic ai" not in url
@pytest.mark.parametrize("builder", [
bing_search_url, brave_search_url, google_patents_search_url, google_scholar_url])
def test_url_significant_characters_are_escaped(self, builder):
url = builder("a&b=c#d", 10)
query_part = url.split("?", 1)[1]
# Exactly one separator per parameter the builder itself added.
assert "a&b=c#d" not in query_part
assert "%26" in query_part
class TestResultCount:
def test_scholar_asks_for_the_requested_number_of_results(self):
assert "num=25" in google_scholar_url("widgets", 25)
def test_google_patents_asks_for_the_requested_number_of_results(self):
assert "num=25" in google_patents_search_url("widgets", 25)
class TestHosts:
@pytest.mark.parametrize("builder, host", [
(bing_search_url, "https://www.bing.com/search"),
(brave_search_url, "https://search.brave.com/search"),
(google_patents_search_url, "https://patents.google.com/"),
(google_scholar_url, "https://scholar.google.com/scholar"),
])
def test_points_at_the_expected_endpoint(self, builder, host):
assert builder("widgets", 10).startswith(host)