| 1 | """Tests for the configurable external podcast link feature.""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | import re |
| 6 | from pathlib import Path |
| 7 | |
| 8 | REPO_ROOT = Path(__file__).resolve().parent.parent |
| 9 | |
| 10 | |
| 11 | def test_podcast_url_param_exists_in_hugo_config() -> None: |
| 12 | """hugo.toml must define the live Spotify podcast URL.""" |
| 13 | config = (REPO_ROOT / "hugo.toml").read_text(encoding="utf-8") |
| 14 | assert "podcast_url" in config |
| 15 | assert 'podcast_url = "https://open.spotify.com/show/033xdn5nDMoCWxB3bss2dB"' in config |
| 16 | |
| 17 | |
| 18 | def test_footer_conditionally_renders_podcast_link() -> None: |
| 19 | """Footer template must conditionally render podcast link from site param.""" |
| 20 | footer = (REPO_ROOT / "layouts" / "partials" / "footer.html").read_text(encoding="utf-8") |
| 21 | # Must trim before `with` so whitespace-only values are treated as falsy |
| 22 | assert "site.Params.podcast_url | strings.TrimSpace" in footer |
| 23 | # safeURL marks the value as trusted (bypasses scheme filtering) — safe here |
| 24 | # because the value comes from controlled site config, not user input. |
| 25 | assert "safeURL" in footer |
| 26 | # Verify the podcast link uses dot context with correct attrs |
| 27 | assert re.search( |
| 28 | r'<a\s+href="{{\s*\.\s*\|\s*safeURL\s*}}"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*>Podcast</a>', |
| 29 | footer, |
| 30 | ), ( |
| 31 | "Footer podcast link must use dot context piped through safeURL with target=_blank and rel=noopener noreferrer" |
| 32 | ) |
| 33 | |
| 34 | |
| 35 | def test_report_shortcuts_link_weekly_reports_to_spotify() -> None: |
| 36 | """Weekly report shortcuts must expose the configured Spotify URL.""" |
| 37 | shortcuts = (REPO_ROOT / "layouts" / "partials" / "report-shortcuts.html").read_text( |
| 38 | encoding="utf-8" |
| 39 | ) |
| 40 | assert 'site.Params.podcast_url | default "" | strings.TrimSpace' in shortcuts |
| 41 | assert "if and $isWeekly $podcastURL" in shortcuts |
| 42 | assert re.search( |
| 43 | r'<a\s+href="{{\s*\$podcastURL\s*\|\s*safeURL\s*}}"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*>Spotify</a>', |
| 44 | shortcuts, |
| 45 | ), "Weekly shortcuts must link to Spotify with target=_blank and rel=noopener noreferrer" |
| 46 | |
| 47 | |
| 48 | def test_header_exposes_spotify_button_when_podcast_url_is_configured() -> None: |
| 49 | """Header actions must expose the configured Spotify URL.""" |
| 50 | header = (REPO_ROOT / "layouts" / "partials" / "header.html").read_text(encoding="utf-8") |
| 51 | assert 'site.Params.podcast_url | default "" | strings.TrimSpace' in header |
| 52 | assert 'class="icon-button spotify-button"' in header |
| 53 | assert re.search( |
| 54 | r'<a\s+class="icon-button spotify-button"\s+href="{{\s*\.\s*\|\s*safeURL\s*}}"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*aria-label="Spotify"', |
| 55 | header, |
| 56 | ), ( |
| 57 | "Header Spotify button must use configured URL with target=_blank and rel=noopener noreferrer" |
| 58 | ) |
| 59 | |
| 60 | |
| 61 | def test_podcast_link_disabled_when_empty() -> None: |
| 62 | """Templates must guard against empty/whitespace podcast_url (disabled state).""" |
| 63 | footer = (REPO_ROOT / "layouts" / "partials" / "footer.html").read_text(encoding="utf-8") |
| 64 | header = (REPO_ROOT / "layouts" / "partials" / "header.html").read_text(encoding="utf-8") |
| 65 | shortcuts = (REPO_ROOT / "layouts" / "partials" / "report-shortcuts.html").read_text( |
| 66 | encoding="utf-8" |
| 67 | ) |
| 68 | assert "{{- with site.Params.podcast_url | strings.TrimSpace }}" in footer, ( |
| 69 | "Footer must render the podcast link only through a trimmed `with` guard" |
| 70 | ) |
| 71 | assert ( |
| 72 | '{{- $podcastURL := site.Params.podcast_url | default "" | strings.TrimSpace }}' in header |
| 73 | ), "Header must normalize podcast_url into $podcastURL before rendering" |
| 74 | assert "{{- with $podcastURL }}" in header, ( |
| 75 | "Header must guard the Spotify button with `with $podcastURL`" |
| 76 | ) |
| 77 | assert "{{- if and $isWeekly $podcastURL }}" in shortcuts, ( |
| 78 | "Weekly shortcuts must require a non-empty $podcastURL before rendering Spotify" |
| 79 | ) |
| 80 | |
| 81 | |
| 82 | def test_no_audio_hosting_or_player_pages() -> None: |
| 83 | """SquadScope must not host audio, embed players, or serve podcast RSS.""" |
| 84 | layouts_dir = REPO_ROOT / "layouts" |
| 85 | # No podcast RSS template |
| 86 | for f in layouts_dir.rglob("*.xml"): |
| 87 | content = f.read_text(encoding="utf-8").lower() |
| 88 | assert "<enclosure" not in content, f"{f.name} must not contain podcast RSS enclosure tags" |
| 89 | assert "open.spotify.com/embed" not in content, f"{f.name} must not embed Spotify players" |
| 90 | # No audio player templates |
| 91 | for f in layouts_dir.rglob("*.html"): |
| 92 | content = f.read_text(encoding="utf-8").lower() |
| 93 | assert "<audio" not in content, f"{f.name} must not contain audio player elements" |
| 94 | assert "open.spotify.com/embed" not in content, f"{f.name} must not embed Spotify players" |
| 95 | assert "spotify:embed:" not in content, f"{f.name} must not contain Spotify embed URIs" |