diff --git a/scripts/check_growth_live.py b/scripts/check_growth_live.py index 71a9d29..2e29903 100644 --- a/scripts/check_growth_live.py +++ b/scripts/check_growth_live.py @@ -13,6 +13,14 @@ CATALOG_MIN_ITEMS = 92 FEATURED_SLUGS = ("debate-me", "tappitsu-battle", "future-me", "twotaku") GROWTH_RELEASE_ID = "growth-20261106-wave2" +GROWTH_LASTMOD_BASELINE = "2026-11-07" +GROWTH_UPDATED_PATHS = ( + "/", "/apps", + "/support/taletime/", "/support/debate-me/", + "/support/dog-breeds-encyclopedia/", "/support/face-reaction-battle/", + "/guides/quick-games/", "/guides/daily-habits/", + "/guides/decision-helpers/", "/guides/ai-tools/", +) PATHS = ("/", "/apps", "/robots.txt", "/sitemap.xml") + tuple( f"/support/{slug}/" for slug in SLUGS ) @@ -154,6 +162,18 @@ def check(path, key, valid): loc = "https://appdevelopsk.com" + path check("/sitemap.xml", f"listed_{path}", f"{loc}" in sitemap) + # Recent content changes must be discoverable through a current sitemap. + # Dates later than the baseline are valid when the page changes again. + for path in GROWTH_UPDATED_PATHS: + canonical = "https://appdevelopsk.com" + path + match = re.search( + r"" + re.escape(canonical) + + r"\s*(\d{4}-\d{2}-\d{2})", + sitemap, + ) + check("/sitemap.xml", f"fresh_lastmod_{path}", + bool(match and match.group(1) >= GROWTH_LASTMOD_BASELINE)) + return {"passed": all(item["passed"] for item in findings), "passedChecks": sum(item["passed"] for item in findings), "totalChecks": len(findings), "checks": findings, diff --git a/scripts/test_growth_live.py b/scripts/test_growth_live.py index 89205a7..365ed4a 100644 --- a/scripts/test_growth_live.py +++ b/scripts/test_growth_live.py @@ -2,7 +2,7 @@ import json import unittest -from check_growth_live import FEATURED_SLUGS, GROWTH_RELEASE_ID, PATHS, SLUGS, inspect +from check_growth_live import FEATURED_SLUGS, GROWTH_RELEASE_ID, GROWTH_UPDATED_PATHS, PATHS, SLUGS, inspect def valid_pages(): @@ -59,9 +59,17 @@ def valid_pages(): f'' + " ".join(f'' for x in other)) pages["/robots.txt"] = "User-agent: *\\nAllow: /icons/\\n" - pages["/sitemap.xml"] = "".join( - f"https://appdevelopsk.com{p}" - for p in ("/", "/apps") + tuple(f"/support/{s}/" for s in SLUGS)) + pages["/sitemap.xml"] = ( + "".join( + f"https://appdevelopsk.com{p}2026-11-07" + for p in GROWTH_UPDATED_PATHS + ) + + "".join( + f"https://appdevelopsk.com/support/{slug}/" + for slug in SLUGS + if f"/support/{slug}/" not in GROWTH_UPDATED_PATHS + ) + ) for path in ("/", "/apps") + tuple(f"/support/{slug}/" for slug in SLUGS): pages[path] = (f'' + pages[path]) @@ -74,6 +82,22 @@ def test_valid_source_fixture_passes(self): self.assertTrue(report["passed"]) self.assertEqual(report["passedChecks"], report["totalChecks"]) + def test_recent_sitemap_timestamp_regression_fails(self): + data = valid_pages() + data["/sitemap.xml"] = data["/sitemap.xml"].replace( + "https://appdevelopsk.com/apps2026-11-07", + "https://appdevelopsk.com/apps2026-11-06", + ) + self.assertFalse(inspect(data)["passed"]) + + def test_missing_recent_sitemap_page_fails(self): + data = valid_pages() + data["/sitemap.xml"] = data["/sitemap.xml"].replace( + "https://appdevelopsk.com/guides/ai-tools/2026-11-07", + "", + ) + self.assertFalse(inspect(data)["passed"]) + def test_missing_related_link_fails(self): data = valid_pages() data["/support/future-me/"] = data["/support/future-me/"].replace( diff --git a/scripts/test_sitemap_growth_freshness.py b/scripts/test_sitemap_growth_freshness.py new file mode 100644 index 0000000..52a3e52 --- /dev/null +++ b/scripts/test_sitemap_growth_freshness.py @@ -0,0 +1,61 @@ +"""Validate catalog and recently revised support/guide sitemap dates. + +Run with: python3 -m unittest discover -s scripts -p 'test_sitemap_growth_freshness.py' -v +""" +from datetime import date +from pathlib import Path +import unittest +import xml.etree.ElementTree as ET + +ROOT = Path(__file__).resolve().parent.parent +NS = "{http://www.sitemaps.org/schemas/sitemap/0.9}" +BASE = "https://appdevelopsk.com" +CHANGED_2026_11_07 = ( + "/", "/apps", + "/support/taletime/", "/support/debate-me/", + "/support/dog-breeds-encyclopedia/", "/support/face-reaction-battle/", + "/guides/quick-games/", "/guides/daily-habits/", + "/guides/decision-helpers/", "/guides/ai-tools/", +) + + +def entries(): + root = ET.parse(ROOT / "sitemap.xml").getroot() + if root.tag != NS + "urlset": + raise ValueError("unexpected sitemap XML root") + records = [] + for item in root.findall(NS + "url"): + url = item.findtext(NS + "loc") + lastmod = item.findtext(NS + "lastmod") + records.append((url, lastmod)) + return records + + +class GrowthSitemapFreshnessTests(unittest.TestCase): + def test_sitemap_canonical_urls_are_unique(self): + records = entries() + urls = [url for url, _ in records] + self.assertEqual(len(urls), len(set(urls))) + self.assertGreaterEqual(len(urls), 319) + self.assertTrue(all(url.startswith(BASE + "/") for url in urls)) + self.assertNotIn(BASE + "/apps.html", urls) + + def test_changed_pages_have_accurate_nov_7_date(self): + pages = dict(entries()) + for path in CHANGED_2026_11_07: + with self.subTest(path=path): + self.assertEqual(pages.get(BASE + path), "2026-11-07") + + def test_sitemap_dates_are_valid_and_not_future(self): + for url, stamp in entries(): + with self.subTest(url=url): + parsed = date.fromisoformat(stamp) + self.assertLessEqual(parsed, date.today()) + + def test_filtered_catalog_urls_are_not_indexed_separately(self): + urls = [url for url, _ in entries()] + self.assertFalse(any("?goal=" in url for url in urls)) + + +if __name__ == "__main__": + unittest.main() diff --git a/sitemap.xml b/sitemap.xml index 0fa0e69..f284b1b 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,11 +1,11 @@ - https://appdevelopsk.com/2026-11-05weekly1.0 + https://appdevelopsk.com/2026-11-07weekly1.0 https://appdevelopsk.com/about2026-08-18yearly0.4 https://appdevelopsk.com/contact2026-08-18yearly0.4 https://appdevelopsk.com/privacy2026-08-18yearly0.4 https://appdevelopsk.com/terms2026-08-18yearly0.4 - https://appdevelopsk.com/apps2026-11-06weekly0.9 + https://appdevelopsk.com/apps2026-11-07weekly0.9 https://appdevelopsk.com/support/2026-08-18weekly0.6 https://appdevelopsk.com/privacy-policy/2026-08-18weekly0.3 https://appdevelopsk.com/support/30sec-challenge/2026-08-18monthly0.8 @@ -27,16 +27,16 @@ https://appdevelopsk.com/support/daily-duel/2026-08-18monthly0.8 https://appdevelopsk.com/support/date-pilot/2026-08-18monthly0.8 https://appdevelopsk.com/support/date-roulette/2026-08-18monthly0.8 - https://appdevelopsk.com/support/debate-me/2026-11-05monthly0.8 + https://appdevelopsk.com/support/debate-me/2026-11-07monthly0.8 https://appdevelopsk.com/support/debrief/2026-08-18monthly0.8 https://appdevelopsk.com/support/dinner-home-roulette/2026-08-18monthly0.8 https://appdevelopsk.com/support/dinosaur-encyclopedia/2026-08-18monthly0.8 - https://appdevelopsk.com/support/dog-breeds-encyclopedia/2026-08-18monthly0.8 + https://appdevelopsk.com/support/dog-breeds-encyclopedia/2026-11-07monthly0.8 https://appdevelopsk.com/support/dream-log/2026-08-18monthly0.8 https://appdevelopsk.com/support/drift/2026-08-18monthly0.8 https://appdevelopsk.com/support/earsense/2026-08-18monthly0.8 https://appdevelopsk.com/support/eye-break/2026-08-18monthly0.8 - https://appdevelopsk.com/support/face-reaction-battle/2026-10-27monthly0.8 + https://appdevelopsk.com/support/face-reaction-battle/2026-11-07monthly0.8 https://appdevelopsk.com/support/face-symmetry/2026-08-18monthly0.8 https://appdevelopsk.com/support/face-vibe/2026-11-04monthly0.8 https://appdevelopsk.com/support/fileforce/2026-08-18monthly0.8 @@ -112,7 +112,7 @@ https://appdevelopsk.com/support/study-roulette/2026-08-18monthly0.8 https://appdevelopsk.com/support/sub-tracker/2026-08-18monthly0.8 https://appdevelopsk.com/support/take-sides/2026-08-18monthly0.8 - https://appdevelopsk.com/support/taletime/2026-08-18monthly0.8 + https://appdevelopsk.com/support/taletime/2026-11-07monthly0.8 https://appdevelopsk.com/support/tappitsu-battle/2026-11-05monthly0.8 https://appdevelopsk.com/support/tier-rank-me/2026-08-18monthly0.8 https://appdevelopsk.com/support/tracepedia/2026-08-18monthly0.8 @@ -315,8 +315,8 @@ https://appdevelopsk.com/terms/smile-score/2026-08-18yearly0.3 https://appdevelopsk.com/terms/idea-market/2026-11-04yearly0.3 https://appdevelopsk.com/csae-standards/2026-08-18yearly0.3 - https://appdevelopsk.com/guides/quick-games/2026-10-30monthly0.7 - https://appdevelopsk.com/guides/daily-habits/2026-10-27monthly0.7 - https://appdevelopsk.com/guides/decision-helpers/2026-10-27monthly0.7 - https://appdevelopsk.com/guides/ai-tools/2026-10-27monthly0.7 + https://appdevelopsk.com/guides/quick-games/2026-11-07monthly0.7 + https://appdevelopsk.com/guides/daily-habits/2026-11-07monthly0.7 + https://appdevelopsk.com/guides/decision-helpers/2026-11-07monthly0.7 + https://appdevelopsk.com/guides/ai-tools/2026-11-07monthly0.7