Files
wehub-resource-sync 2860fb5d18
Security / Dependency review (push) Has been skipped
Scorecard / Scorecard analysis (push) Failing after 0s
Validate / eval (push) Failing after 0s
Security / Dependency audit (push) Failing after 1s
Security / Secret scan (push) Failing after 1s
Validate / tests (push) Failing after 0s
Validate / mcp-tests (push) Failing after 1s
GitHub Actions Security Analysis with zizmor 🌈 / zizmor (push) Failing after 1s
Security / SAST scan (push) Failing after 13m52s
chore: import upstream snapshot with attribution
2026-07-13 12:05:33 +08:00

135 lines
5.4 KiB
Python

"""Diverse-topic evaluation harness for the three default-on Printing Press
sources (arxiv, techmeme, trustpilot).
Two layers:
1. Deterministic fire/quiet matrix over five diverse topics drawn from real
run history. Asserts each source's *gating* decision without calling any CLI
-- this is the part that proves "Trustpilot fires only on company topics" and
"arXiv stays quiet on stale/off-topic matches".
2. Opt-in --live mode (set LAST30DAYS_EVAL_LIVE=1) that actually invokes the
installed CLIs against the same topics, prints a sample for human review, and
asserts the negative controls stay quiet. Skipped by default so CI stays
deterministic and offline.
"""
from __future__ import annotations
import os
import shutil
from datetime import datetime, timedelta, timezone
import pytest
from lib import arxiv, techmeme, trustpilot
# Five diverse topics from real run history, with the expected fire/quiet call
# for each source. None = "not asserted deterministically" (depends on live
# corpus; checked only in --live mode).
TOPICS = {
"AI coding agents": {"arxiv": True, "techmeme": True, "trustpilot": False},
"agent memory": {"arxiv": True, "techmeme": True, "trustpilot": False},
"Nothing Phone": {"arxiv": None, "techmeme": None, "trustpilot": True},
"ChowNow": {"arxiv": None, "techmeme": None, "trustpilot": True},
"Golden State Warriors": {"arxiv": False, "techmeme": None, "trustpilot": False},
}
NOW = datetime(2026, 6, 27, tzinfo=timezone.utc)
# ---- Layer 1: deterministic gating matrix ----
@pytest.mark.parametrize("topic,expected", [(t, e["trustpilot"]) for t, e in TOPICS.items()])
def test_trustpilot_brand_gate_matrix(topic, expected):
"""Trustpilot's brand-shape gate is the primary quiet-keeper."""
assert trustpilot.is_brand_shaped(topic) is expected
def test_arxiv_quiet_on_offtopic_stale_match():
"""The "Golden State Warriors" control resolves to a 2017 stats paper;
arXiv's recency cutoff drops it, keeping the source quiet off-topic."""
stale = "2017-06-12T12:00:00Z"
resp = {"results": [{
"id": "http://arxiv.org/abs/1706.03442v2",
"title": "Do Steph Curry and Klay Thompson Have Hot Hands?",
"summary": "An analysis of Golden State Warriors shooting.",
"published": stale,
"links": [{"rel": "alternate", "href": "https://arxiv.org/abs/1706.03442v2"}],
}]}
assert arxiv.parse_arxiv_response(resp, query="Golden State Warriors", today=NOW) == []
def test_arxiv_fires_on_recent_ontopic():
recent = (NOW - timedelta(days=20)).strftime("%Y-%m-%dT12:00:00Z")
resp = {"results": [{
"id": "http://arxiv.org/abs/2606.1v1",
"title": "Is Agent Memory a Database?",
"summary": "Rethinking data foundations for long-term AI agent memory.",
"published": recent,
"authors": [{"name": "A"}],
"links": [{"rel": "alternate", "href": "https://arxiv.org/abs/2606.1v1"}],
}]}
items = arxiv.parse_arxiv_response(resp, query="agent memory", today=NOW)
assert len(items) == 1
def test_techmeme_drops_header_rows_keeps_stories():
resp = {"results": [
{"num": 1, "source": "techcrunch.com", "headline": "TechCrunch", "link": "http://techcrunch.com/"},
{"num": 2, "source": "techcrunch.com",
"headline": "OpenAI ships a new coding agent for developers today",
"link": "https://www.techmeme.com/x"},
]}
items = techmeme.parse_techmeme_response(resp, query="AI coding agents")
assert len(items) == 1
assert items[0]["title"].startswith("OpenAI")
# ---- Layer 2: opt-in live exploration ----
_LIVE = os.environ.get("LAST30DAYS_EVAL_LIVE", "").strip().lower() in ("1", "true", "yes")
def _have(binary: str) -> bool:
return shutil.which(binary) is not None
@pytest.mark.skipif(not _LIVE, reason="set LAST30DAYS_EVAL_LIVE=1 to run live source exploration")
def test_live_fire_matrix(capsys):
"""Live: run the real CLIs against the diverse topics, print samples, and
assert the negative controls stay quiet."""
fd, td = "2026-05-28", "2026-06-27"
lines = []
for topic, expected in TOPICS.items():
lines.append(f"\n=== {topic} ===")
if _have("arxiv-pp-cli"):
items = arxiv.parse_arxiv_response(
arxiv.search_arxiv(topic, fd, td), query=topic)
lines.append(f" arXiv: {len(items)} papers")
for it in items[:2]:
lines.append(f" - {it['title'][:80]}")
if expected["arxiv"] is False:
assert items == [], f"arXiv should stay quiet on {topic!r}"
if _have("techmeme-pp-cli"):
items = techmeme.parse_techmeme_response(
techmeme.search_techmeme(topic, fd, td), query=topic)
lines.append(f" Techmeme: {len(items)} headlines")
for it in items[:2]:
lines.append(f" - {it['title'][:80]}")
if _have("trustpilot-pp-cli"):
items = trustpilot.parse_trustpilot_response(
trustpilot.search_trustpilot(topic, fd, td), query=topic)
lines.append(f" Trustpilot: {len(items)} companies")
for it in items[:1]:
lines.append(f" - {it['title'][:80]}")
if expected["trustpilot"] is False:
assert items == [], f"Trustpilot should stay quiet on {topic!r}"
with capsys.disabled():
print("\n".join(lines))