Files
upbeatBytes/tests/test_dashboard.py
T
thejayman77 d87347b032 Dashboard: content + source-health; per-viewer local dates
* Date fix: introduce GOODNEWS_TZ (goodnews/localtime.py) so the brief's "today"
  rolls over in a pinned zone (Eastern) instead of UTC — robust to host-clock
  resets. The home page now formats the brief's date in each VISITOR's local
  timezone (from its UTC freshness stamp), so nobody ever sees "tomorrow."

* Admin "Content served": articles live, fresh (7d), ingested (24h), summaries,
  active sources, today's brief size — queries.content_stats().

* Admin "Source health": per active source, the failure streak, last error,
  accepted contribution, and computed next-poll time (so backoff / "resting
  until" is visible), via queries.source_health() reusing the feeds backoff
  math. Failing sources sort to the top; times render in the viewer's zone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 19:34:22 +00:00

50 lines
2.5 KiB
Python

from goodnews.db import connect, init_db
from goodnews import queries, localtime
def test_site_tz_from_env_and_fallback(monkeypatch):
monkeypatch.setenv("GOODNEWS_TZ", "America/New_York")
assert localtime.site_tz().key == "America/New_York"
assert localtime.local_now().utcoffset() is not None # tz-aware
# A bogus zone must not crash — fall back to UTC.
monkeypatch.setenv("GOODNEWS_TZ", "Not/AZone")
assert localtime.site_tz().key == "UTC"
def _seed(c):
c.execute("INSERT INTO sources (id,name,feed_url,active,default_category) VALUES (1,'Good','http://s/1',1,'science')")
c.execute("INSERT INTO sources (id,name,feed_url,active,consecutive_failures,poll_interval_minutes) "
"VALUES (2,'Flaky','http://s/2',1,5,60)")
# source 1: two accepted (one duplicate → not served), one rejected
for aid, dup in [(1, None), (2, None), (3, 1)]:
c.execute("INSERT INTO articles (id,source_id,canonical_url,title,url_hash,duplicate_of,published_at) "
"VALUES (?,1,?,?,?,?,datetime('now'))", (aid, f"u{aid}", f"T{aid}", f"h{aid}", dup))
c.execute("INSERT INTO article_scores (article_id,accepted) VALUES (1,1)")
c.execute("INSERT INTO article_scores (article_id,accepted) VALUES (2,0)")
c.execute("INSERT INTO article_scores (article_id,accepted) VALUES (3,1)") # duplicate → excluded
c.commit()
def test_content_stats_counts_served_excluding_duplicates(tmp_path):
c = connect(str(tmp_path / "t.db")); init_db(c); _seed(c)
cs = queries.content_stats(c)
assert cs["served"] == 1 # only article 1 (2 is rejected, 3 is a duplicate)
assert cs["total"] == 3
assert cs["rejected"] == 1
assert cs["active_sources"] == 2
def test_source_health_orders_failing_first_and_computes_next_due(tmp_path):
c = connect(str(tmp_path / "t.db")); init_db(c); _seed(c)
c.execute("INSERT INTO ingest_runs (source_id, finished_at, status) "
"VALUES (2, datetime('now','-30 minutes'), 'failed')")
c.commit()
sh = queries.source_health(c)
assert [s["name"] for s in sh][0] == "Flaky" # failing source floats to top
flaky = next(s for s in sh if s["name"] == "Flaky")
assert flaky["failures"] == 5
# next_due = last_attempt + 60*(1+5)=360 min; attempt was 30m ago → still resting
assert flaky["next_due_at"] is not None
good = next(s for s in sh if s["name"] == "Good")
assert good["served"] == 1 and good["next_due_at"] is None # never attempted → due now