Merge pull request #416 from tmchow/worktree-inherited-discovering-pebble

fix(ci): run full pytest suite, repair 13 rotted tests
This commit is contained in:
Trevin Chow
2026-05-16 22:11:34 -07:00
committed by GitHub
5 changed files with 64 additions and 37 deletions
+3 -3
View File
@@ -10,7 +10,7 @@ permissions:
contents: read
jobs:
plugin-contract:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -22,5 +22,5 @@ jobs:
- name: Set up Python
run: uv python install 3.12
- name: Run plugin contract tests
run: uv run pytest tests/test_plugin_contract.py tests/test_version_consistency.py
- name: Run test suite
run: uv run pytest
+18 -14
View File
@@ -14,7 +14,7 @@ import argparse
import json
import sqlite3
import sys
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional
@@ -519,7 +519,7 @@ def get_daily_cost(date: Optional[str] = None) -> float:
conn = _connect()
try:
if not date:
date = datetime.now().strftime("%Y-%m-%d")
date = datetime.now(timezone.utc).strftime("%Y-%m-%d")
row = conn.execute(
"""SELECT COALESCE(SUM(token_cost), 0) as total
FROM research_runs
@@ -575,7 +575,7 @@ def get_stats() -> Dict[str, Any]:
topic_count = conn.execute("SELECT COUNT(*) FROM topics WHERE enabled = 1").fetchone()[0]
finding_count = conn.execute("SELECT COUNT(*) FROM findings").fetchone()[0]
week_ago = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")
week_ago = (datetime.now(timezone.utc) - timedelta(days=7)).strftime("%Y-%m-%d")
runs_7d = conn.execute(
"SELECT COUNT(*) FROM research_runs WHERE run_date >= ?", (week_ago,)
).fetchone()[0]
@@ -621,7 +621,7 @@ def get_trending(days: int = 7) -> List[Dict[str, Any]]:
"""Get topics ranked by recent finding activity."""
conn = _connect()
try:
since = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
since = (datetime.now(timezone.utc) - timedelta(days=days)).strftime("%Y-%m-%d")
rows = conn.execute(
"""SELECT t.name, t.id,
COUNT(f.id) as new_findings,
@@ -676,24 +676,28 @@ def findings_from_report(
Uses ranked candidates (post-rerank) when available for quality scores and explanations.
Supplements with raw items from items_by_source for HN/PM that didn't rank highly
but are valuable for watchlist persistence.
but are valuable for watchlist persistence. When ranked_candidates is empty
(degraded path — rerank failed or was skipped), falls back to supplementing
all sources from items_by_source so findings aren't silently dropped.
"""
findings = []
seen_urls = set()
# Phase 1: Process ranked candidates (high-quality data with explanations and corroboration)
for candidate in report.ranked_candidates:
finding = finding_from_candidate(candidate)
findings.append(finding)
findings.append(finding_from_candidate(candidate))
seen_urls.add(candidate.url)
# Phase 2: Add HN/PM items not already captured in ranked candidates
for source_name in ["hackernews", "polymarket"]:
supplement_sources = (
list(report.items_by_source)
if not report.ranked_candidates
else ["hackernews", "polymarket"]
)
for source_name in supplement_sources:
if source_name not in report.items_by_source:
continue
for item in report.items_by_source[source_name]:
if item.url in seen_urls:
continue # Already captured with rich data
continue
findings.append({
"source": source_name,
"source_url": item.url,
@@ -706,7 +710,6 @@ def findings_from_report(
})
seen_urls.add(item.url)
# Apply global limit after collecting all findings (fix: was per-source, now global)
return findings[:limit] if limit is not None else findings
@@ -722,9 +725,10 @@ def _cli_query(args):
since = None
if args.since:
# Parse duration like "7d", "30d"
# Parse duration like "7d", "30d". Use UTC to match SQLite's
# datetime('now') which writes first_seen in UTC.
days = int(args.since.rstrip("d"))
since = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
since = (datetime.now(timezone.utc) - timedelta(days=days)).strftime("%Y-%m-%d")
findings = get_new_findings(topic["id"], since)
print(json.dumps({"topic": topic["name"], "findings": findings, "count": len(findings)}, default=str))
+23 -4
View File
@@ -6,6 +6,7 @@ from __future__ import annotations
import os
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
@@ -27,13 +28,31 @@ class FooterNudgeSuppressionTests(unittest.TestCase):
"--emit=md",
*argv,
]
env = {**os.environ, "LAST30DAYS_SKIP_PREFLIGHT": "1"}
env = {
**os.environ,
"LAST30DAYS_SKIP_PREFLIGHT": "1",
# Skip ~/.config/last30days/.env so a contributor's saved
# BRAVE/EXA/SERPER/PARALLEL key doesn't make grounding "available"
# and suppress the promo we're checking for.
"LAST30DAYS_CONFIG_DIR": "",
# Pin X as available so _missing_sources_for_promo selects "web"
# (otherwise the "x" promo wins and the BRAVE_API_KEY string never
# appears).
"XAI_API_KEY": "test-stub",
}
# Strip any grounded-web keys the host might have so the promo path
# triggers deterministically in mock + no-backend.
# triggers deterministically in mock + no-backend. Also strip X cookie
# credentials so XAI_API_KEY is the unambiguous X backend.
for key in ("BRAVE_API_KEY", "EXA_API_KEY", "SERPER_API_KEY",
"PARALLEL_API_KEY", "OPENROUTER_API_KEY"):
"PARALLEL_API_KEY", "OPENROUTER_API_KEY",
"AUTH_TOKEN", "CT0", "LAST30DAYS_X_BACKEND"):
env.pop(key, None)
return subprocess.run(cmd, capture_output=True, text=True, env=env)
# Run from a tmpdir so _find_project_env() can't walk up into any
# .claude/last30days.env above the repo on the contributor's machine.
with tempfile.TemporaryDirectory() as tmp:
return subprocess.run(
cmd, capture_output=True, text=True, env=env, cwd=tmp,
)
def test_bare_run_emits_web_promo(self):
result = self._run(topic="OpenAI")
+8 -4
View File
@@ -213,8 +213,10 @@ class TestPollDeviceAuth:
@patch("lib.setup_wizard.urlopen")
def test_timeout_returns_none(self, mock_urlopen, mock_time):
"""Returns None when timeout is exceeded."""
# Simulate time passing beyond deadline
mock_time.time = MagicMock(side_effect=[0, 301])
# poll_device_auth calls time.time() for deadline init, last_reminder init,
# then once per while-loop iteration. Three values are enough for one check
# that exceeds the deadline.
mock_time.time = MagicMock(side_effect=[0, 0, 301])
mock_time.sleep = MagicMock()
result = setup_wizard.poll_device_auth("dc-123", interval=5, timeout=300)
@@ -224,7 +226,9 @@ class TestPollDeviceAuth:
@patch("lib.setup_wizard.urlopen")
def test_expired_token_returns_none(self, mock_urlopen, mock_time):
"""Returns None on expired_token error."""
mock_time.time = MagicMock(side_effect=[0, 0])
# Loop terminates via urlopen response, not the clock — pin time to 0
# so the deadline check stays a non-event regardless of call count.
mock_time.time = MagicMock(return_value=0)
mock_time.sleep = MagicMock()
expired_resp = MagicMock()
@@ -243,7 +247,7 @@ class TestPollDeviceAuth:
"""HTTP 400 during polling continues (authorization pending)."""
from urllib.error import HTTPError
mock_time.time = MagicMock(side_effect=[0, 0, 0])
mock_time.time = MagicMock(return_value=0)
mock_time.sleep = MagicMock()
success_resp = MagicMock()
+5 -5
View File
@@ -3,7 +3,7 @@
import json
import sqlite3
import tempfile
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from pathlib import Path
import pytest
@@ -503,14 +503,14 @@ def test_get_new_findings_filters_by_date(temp_db, sample_report):
findings = store.findings_from_report(sample_report)
store.store_findings(run_id, topic["id"], findings)
# Get findings since tomorrow (should be empty)
tomorrow = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
# Use UTC because store writes first_seen via SQLite's datetime('now') (UTC).
# Local-time math here would flake near midnight UTC.
tomorrow = (datetime.now(timezone.utc) + timedelta(days=1)).strftime("%Y-%m-%d")
new_findings = store.get_new_findings(topic["id"], since=tomorrow)
assert len(new_findings) == 0
# Get findings since yesterday (should have all)
yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
yesterday = (datetime.now(timezone.utc) - timedelta(days=1)).strftime("%Y-%m-%d")
new_findings = store.get_new_findings(topic["id"], since=yesterday)
assert len(new_findings) == 4