Files
ai-podcast/tests/test_publish_regenerates_pages.py
lukeandClaude Opus 5 242eb15e0a Generate the episode page after publishing, not before
regenerate_website_pages() ran at step 3.7, before the step-4 publish call.
generate_episode_pages.py is driven entirely by the RSS feed, so the episode
being published was never in the data it read: the generator exited 0, printed
its success line, and silently omitted the new episode. Its page only appeared
on the NEXT publish.

Social posts link to /episode/<slug>/, so every launch-day link across X,
Bluesky, Discord, Instagram, Threads, Facebook, LinkedIn, Mastodon and Nostr
404'd during exactly the window the post was circulating. That held for all 58
published episodes.

The existing test only asserted the generator gets invoked with --sitemap,
which passed throughout. Added two tests pinning the call between the publish
and the wrangler deploy, verified to fail against the previous ordering.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 17:05:17 -05:00

117 lines
3.9 KiB
Python

"""Publishing must rebuild the static episode pages and the sitemap.
Before static pages existed, publish_episode.py appended one entry to
sitemap.xml itself. That appender is gone — generate_episode_pages.py owns the
sitemap now — so the publish has to invoke it, or a newly published episode
would have no page and never reach the sitemap.
A generator failure must never abort a publish: by the time this runs the audio
is already live on Castopod and the RSS feed has been rebuilt.
"""
import subprocess
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import publish_episode
class _Result:
def __init__(self, returncode=0, stderr=""):
self.returncode = returncode
self.stderr = stderr
self.stdout = ""
def test_invokes_the_generator_with_sitemap(monkeypatch):
calls = []
monkeypatch.setattr(subprocess, "run", lambda cmd, **kw: calls.append(cmd) or _Result())
assert publish_episode.regenerate_website_pages() is True
assert len(calls) == 1
cmd = calls[0]
assert "generate_episode_pages.py" in " ".join(cmd)
assert "--sitemap" in cmd
def test_uses_the_running_interpreter(monkeypatch):
"""Must not shell out to a bare 'python' that may not have the venv."""
calls = []
monkeypatch.setattr(subprocess, "run", lambda cmd, **kw: calls.append(cmd) or _Result())
publish_episode.regenerate_website_pages()
assert calls[0][0] == sys.executable
def test_generator_failure_is_not_fatal(monkeypatch):
monkeypatch.setattr(subprocess, "run",
lambda cmd, **kw: _Result(returncode=1, stderr="boom"))
assert publish_episode.regenerate_website_pages() is False
def test_generator_timeout_is_not_fatal(monkeypatch):
def _boom(cmd, **kw):
raise subprocess.TimeoutExpired(cmd, 300)
monkeypatch.setattr(subprocess, "run", _boom)
assert publish_episode.regenerate_website_pages() is False
def test_missing_generator_is_not_fatal(monkeypatch):
def _boom(cmd, **kw):
raise OSError("no such file")
monkeypatch.setattr(subprocess, "run", _boom)
assert publish_episode.regenerate_website_pages() is False
def test_publish_flow_calls_it_after_copying_the_transcript():
"""Guards the wiring, not just the helper."""
source = Path(publish_episode.__file__).read_text()
copy_at = source.index("Transcript copied to website/transcripts/")
call_at = source.index("regenerate_website_pages()", copy_at)
assert call_at > copy_at
def test_generator_runs_after_the_publish_call():
"""Ordering regression guard.
generate_episode_pages.py is driven entirely by the RSS feed, so it only
sees an episode once Castopod has published it. For 58 episodes the
regenerate call sat at step 3.7 — before the step-4 publish — so the
episode being published was silently absent from its own run and its page
only appeared on the NEXT publish. Social posts link to /episode/<slug>/,
so every launch-day link 404'd.
Asserted on source order because the surrounding publish flow does network
and filesystem work that isn't practical to drive end to end here.
"""
src = Path(publish_episode.__file__).read_text()
body = src[src.index("def main("):]
publish_call = body.index("published = publish_episode(")
regen_call = body.index("regenerate_website_pages()")
deploy_call = body.index("wrangler")
assert publish_call < regen_call, (
"regenerate_website_pages() must run AFTER publish_episode(), or the new "
"episode is missing from the feed the generator reads"
)
assert regen_call < deploy_call, (
"regenerate_website_pages() must run BEFORE the wrangler deploy, or the "
"freshly built page never ships"
)
def test_regenerate_is_called_exactly_once_in_main():
src = Path(publish_episode.__file__).read_text()
body = src[src.index("def main("):]
assert body.count("regenerate_website_pages()") == 1