Files
last30days-skill/tests/test_status_banner.py
T
Matt Van Horn 775596ce21 feat: v2.9.6 — free-first NUX, cookie extraction, quality scoring
Setup wizard with consent-first cookie extraction (Chrome/Firefox/Safari),
yt-dlp auto-install, ScrapeCreators push, quality scoring (5 core sources),
status banner redesign, honest Reddit labeling, inline YouTube transcripts,
Exa free web search, Reddit public fallback, and post-research quality nudge.

Co-authored-by: Matt Van Horn <mvanhorn@MacBook-Pro.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 14:33:17 -07:00

284 lines
9.1 KiB
Python

"""Tests for the redesigned status banner (free-first design)."""
import pytest
from scripts.lib.ui import _build_status_banner
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _base_diag(**overrides):
"""Return a minimal diag dict with common defaults."""
diag = {
"setup_complete": False,
"reddit_source": None, # None = public fallback
"x_source": None,
"x_method": None,
"youtube": False,
"tiktok": False,
"instagram": False,
"hackernews": True,
"polymarket": True,
"bluesky": False,
"truthsocial": False,
"xiaohongshu": False,
"scrapecreators": False,
"web_search_backend": None,
}
diag.update(overrides)
return diag
def _banner_text(diag):
"""Return full banner as a single string."""
return "\n".join(_build_status_banner(diag))
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
class TestZeroConfig:
"""Zero-config state: SETUP_COMPLETE not set, wizard hasn't run."""
def test_shows_first_run_title(self):
banner = _banner_text(_base_diag())
assert "First Run" in banner
def test_shows_three_free_sources(self):
banner = _banner_text(_base_diag())
assert "Reddit (threads only)" in banner
assert "HN" in banner
assert "Polymarket" in banner
def test_shows_setup_prompt(self):
banner = _banner_text(_base_diag())
assert "/last30days setup" in banner
def test_does_not_show_source_status_title(self):
banner = _banner_text(_base_diag())
assert "Source Status" not in banner
class TestFullConfig:
"""Fully configured: X + yt-dlp + ScrapeCreators = everything active."""
def _full_diag(self):
return _base_diag(
setup_complete=True,
reddit_source="scrapecreators",
x_source="bird",
x_method="browser-chrome",
youtube=True,
tiktok=True,
instagram=True,
hackernews=True,
polymarket=True,
bluesky=True,
truthsocial=True,
xiaohongshu=True,
scrapecreators=True,
web_search_backend="parallel",
)
def test_shows_source_status_title(self):
banner = _banner_text(self._full_diag())
assert "Source Status" in banner
def test_shows_all_sources(self):
banner = _banner_text(self._full_diag())
assert "Reddit (with comments)" in banner
assert "X (Chrome)" in banner
assert "YouTube" in banner
assert "HN" in banner
assert "Polymarket" in banner
assert "TikTok" in banner
assert "Instagram" in banner
assert "Bluesky" in banner
assert "Truth Social" in banner
assert "Xiaohongshu" in banner
def test_no_recommendations(self):
banner = _banner_text(self._full_diag())
assert "" not in banner
assert "scrapecreators.com" not in banner
assert "/last30days setup" not in banner
class TestPartialConfig:
"""Partially configured: X + yt-dlp but no ScrapeCreators."""
def _partial_diag(self):
return _base_diag(
setup_complete=True,
reddit_source=None, # public fallback
x_source="bird",
x_method="browser-chrome",
youtube=True,
scrapecreators=False,
)
def test_shows_active_sources(self):
banner = _banner_text(self._partial_diag())
assert "Reddit (threads only)" in banner
assert "X (Chrome)" in banner
assert "YouTube" in banner
assert "HN" in banner
assert "Polymarket" in banner
def test_recommends_scrapecreators(self):
banner = _banner_text(self._partial_diag())
assert "SCRAPECREATORS_API_KEY" in banner
def test_scrapecreators_free_calls_copy(self):
banner = _banner_text(self._partial_diag())
assert "100 free calls, no CC" in banner
assert "scrapecreators.com" in banner
def test_shows_tiktok_instagram_unlock(self):
banner = _banner_text(self._partial_diag())
assert "TikTok" in banner
assert "Instagram" in banner
class TestScrapeCreatorsRecommendation:
"""ScrapeCreators recommendation always includes key copy."""
def test_always_includes_free_calls_no_cc(self):
"""Any config missing SC should show the free-calls copy."""
# Zero config
banner_zero = _banner_text(_base_diag())
# Zero config doesn't recommend SC directly (recommends setup wizard)
# But after setup, missing SC should always include the copy
banner_partial = _banner_text(_base_diag(
setup_complete=True,
scrapecreators=False,
))
assert "100 free calls, no CC" in banner_partial
def test_present_when_x_available_but_no_sc(self):
banner = _banner_text(_base_diag(
setup_complete=True,
x_source="xai",
x_method="api",
scrapecreators=False,
))
assert "100 free calls, no CC" in banner
assert "scrapecreators.com" in banner
class TestRedditLabelDisplay:
"""Reddit label reflects comment availability, not implementation details."""
def test_no_scrapecreators_shows_threads_only(self):
"""Without SC, Reddit label should say 'threads only' regardless of OpenAI auth."""
banner = _banner_text(_base_diag(
setup_complete=True,
reddit_source="openai",
scrapecreators=False,
))
assert "Reddit (threads only)" in banner
assert "OpenAI" not in banner
assert "Codex" not in banner
def test_no_scrapecreators_public_shows_threads_only(self):
"""Public fallback also shows 'threads only'."""
banner = _banner_text(_base_diag(
setup_complete=True,
reddit_source=None,
scrapecreators=False,
))
assert "Reddit (threads only)" in banner
def test_scrapecreators_shows_with_comments(self):
"""With SC configured, Reddit label should say 'with comments'."""
banner = _banner_text(_base_diag(
setup_complete=True,
reddit_source="scrapecreators",
scrapecreators=True,
))
assert "Reddit (with comments)" in banner
def test_no_openai_or_codex_in_banner(self):
"""Banner should never mention OpenAI or Codex — those are implementation details."""
for source in [None, "openai", "scrapecreators"]:
banner = _banner_text(_base_diag(
setup_complete=True,
reddit_source=source,
scrapecreators=(source == "scrapecreators"),
))
assert "OpenAI" not in banner, f"Found 'OpenAI' with reddit_source={source}"
assert "Codex" not in banner, f"Found 'Codex' with reddit_source={source}"
class TestXMethodDisplay:
"""X source shows auth method in parens."""
def test_browser_chrome(self):
banner = _banner_text(_base_diag(
setup_complete=True,
x_source="bird",
x_method="browser-chrome",
))
assert "X (Chrome)" in banner
def test_browser_firefox(self):
banner = _banner_text(_base_diag(
setup_complete=True,
x_source="bird",
x_method="browser-firefox",
))
assert "X (Firefox)" in banner
def test_env_method(self):
banner = _banner_text(_base_diag(
setup_complete=True,
x_source="bird",
x_method="env",
))
assert "X (env)" in banner
def test_xai_api(self):
banner = _banner_text(_base_diag(
setup_complete=True,
x_source="xai",
x_method="api",
))
assert "X (xAI)" in banner
class TestBannerStructure:
"""Banner formatting and structure tests."""
def test_has_box_drawing(self):
lines = _build_status_banner(_base_diag())
assert lines[0].startswith("")
assert lines[-1].startswith("")
def test_shows_config_path(self):
banner = _banner_text(_base_diag())
assert "~/.config/last30days/.env" in banner
def test_max_10_inner_lines(self):
"""Banner should be compact — max 10 lines inside the box."""
# Full config (most lines)
diag = _base_diag(
setup_complete=True,
reddit_source="scrapecreators",
x_source="bird",
x_method="browser-chrome",
youtube=True,
tiktok=True,
instagram=True,
bluesky=True,
truthsocial=True,
xiaohongshu=True,
scrapecreators=True,
)
lines = _build_status_banner(diag)
# Subtract top and bottom border
inner_lines = [l for l in lines if l.startswith("")]
assert len(inner_lines) <= 10, f"Banner has {len(inner_lines)} inner lines, max is 10"