Auto-sourcing a notable weather event from Wikipedia search proved unreliable
(wrong matches, low coverage), so city_events.py is a small, fact-checked list of
one iconic event per city (Katrina/New Orleans, Harvey/Houston, the 1952 Great
Smog/London, the 2021 heat dome/Seattle, ...) — 31 cities, each with a Wikipedia
link. The city page shows a 'Notable weather in {city}' callout when one exists and
otherwise just the flavor blurb. Tests check the curated slugs are all valid and the
callout renders (and is absent for uncurated cities).
108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
"""Tests for the crawlable SEO content pages: the city set, robots/sitemap, and
|
|
that the server-rendered pages contain the stats in the HTML (with the weather
|
|
layer faked, like test_api.py)."""
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
import app as appmod
|
|
import cities
|
|
import climate
|
|
|
|
# BASE defaults to /thermograph in tests (THERMOGRAPH_BASE unset).
|
|
B = "/thermograph"
|
|
SLUG = "london-england-gb" # present in cities.json
|
|
|
|
|
|
@pytest.fixture
|
|
def client(monkeypatch, history, recent):
|
|
monkeypatch.setattr(climate, "load_cached_history", lambda cell: history.clone())
|
|
monkeypatch.setattr(climate, "get_recent_forecast", lambda cell: recent.clone())
|
|
monkeypatch.setattr(climate, "get_history", lambda cell: (history.clone(), {"cached": True}))
|
|
return TestClient(appmod.app)
|
|
|
|
|
|
def test_city_set_slugs_unique_and_lookup():
|
|
slugs = cities.all_slugs()
|
|
assert len(slugs) == len(set(slugs)) >= 100
|
|
assert cities.get(SLUG) is not None
|
|
assert cities.get("does-not-exist") is None
|
|
|
|
|
|
def test_robots_txt(client):
|
|
r = client.get(f"{B}/robots.txt")
|
|
assert r.status_code == 200
|
|
assert "Sitemap:" in r.text and "/sitemap.xml" in r.text
|
|
assert "Disallow: /thermograph/api/" in r.text
|
|
|
|
|
|
def test_sitemap_lists_city_urls(client):
|
|
r = client.get(f"{B}/sitemap.xml")
|
|
assert r.status_code == 200
|
|
assert "<urlset" in r.text
|
|
assert f"/climate/{SLUG}</loc>" in r.text
|
|
assert f"/climate/{SLUG}/july</loc>" in r.text
|
|
assert f"/climate/{SLUG}/records</loc>" in r.text
|
|
|
|
|
|
def test_city_page_renders_stats_in_html(client):
|
|
r = client.get(f"{B}/climate/{SLUG}")
|
|
assert r.status_code == 200
|
|
b = r.text
|
|
assert "London" in b and "climate" in b.lower()
|
|
assert 'rel="canonical"' in b and f"/climate/{SLUG}" in b
|
|
assert '"@type":"Dataset"' in b
|
|
assert "average temperatures by month" in b.lower()
|
|
# a real number rendered (°F appears in the normals/today text)
|
|
assert "°F" in b
|
|
|
|
|
|
def test_city_404(client):
|
|
assert client.get(f"{B}/climate/nope-not-a-city").status_code == 404
|
|
|
|
|
|
def test_curated_events_have_valid_slugs():
|
|
import city_events
|
|
slugs = set(cities.all_slugs())
|
|
assert len(city_events.EVENTS) >= 20
|
|
assert all(s in slugs for s in city_events.EVENTS), \
|
|
[s for s in city_events.EVENTS if s not in slugs]
|
|
|
|
|
|
def test_curated_event_renders(client):
|
|
b = client.get(f"{B}/climate/new-orleans-louisiana-us").text
|
|
assert "Notable weather in New Orleans" in b
|
|
assert "Hurricane Katrina" in b
|
|
|
|
|
|
def test_uncurated_city_has_no_event_section(client):
|
|
b = client.get(f"{B}/climate/shanghai-cn").text
|
|
assert "city-event" not in b # falls back to the flavor blurb
|
|
|
|
|
|
def test_city_travel_cta_prefills_compare(client):
|
|
b = client.get(f"{B}/climate/{SLUG}").text
|
|
assert "Thinking of visiting" in b
|
|
assert "/compare#loc=" in b # the city is pre-loaded on the compare page
|
|
|
|
|
|
def test_city_blurb_renders_with_attribution(client, monkeypatch):
|
|
monkeypatch.setattr(cities, "flavor",
|
|
lambda slug: {"extract": "Testville is a lovely place to test.",
|
|
"url": "https://en.wikipedia.org/wiki/Testville", "title": "Testville"})
|
|
b = client.get(f"{B}/climate/{SLUG}").text
|
|
assert "Testville is a lovely place to test." in b
|
|
assert "via Wikipedia" in b # CC BY-SA attribution link
|
|
|
|
|
|
def test_month_and_records(client):
|
|
assert client.get(f"{B}/climate/{SLUG}/july").status_code == 200
|
|
assert client.get(f"{B}/climate/{SLUG}/records").status_code == 200
|
|
assert client.get(f"{B}/climate/{SLUG}/notamonth").status_code == 404
|
|
|
|
|
|
def test_hub_glossary_about(client):
|
|
assert client.get(f"{B}/climate").status_code == 200
|
|
assert client.get(f"{B}/glossary").status_code == 200
|
|
assert client.get(f"{B}/glossary/percentile").status_code == 200
|
|
assert client.get(f"{B}/glossary/not-a-term").status_code == 404
|
|
assert client.get(f"{B}/about").status_code == 200
|