Skip to content

Tests: one conftest, shared fakes, and 51 files become 15 - #14

Merged
pythonjeff merged 4 commits into
mainfrom
chore/test-consolidation
Oct 15, 2026
Merged

pythonjeff merged 4 commits into
mainfrom
chore/test-consolidation

Conversation

@pythonjeff

Copy link
Copy Markdown
Owner

Summary

Stage 3 of 4 (hygiene → package layout → test consolidation → docs split). Stacked on #13; merge that first and this retargets to main. 1,430 tests before and after, and the suite is faster (5s vs 7s) for a reason worth reading.

Commit 1 — tests/conftest.py and tests/helpers.py

  • Env placeholders set before any palmer import; the eight module-level load_dotenv() calls go, and with them the last way real keys could reach a test. PALMER_NO_SCHEDULER is set there too, retiring the three with patch("…BackgroundScheduler.start"): import main blocks and the collection-order dependency they hid.
  • fresh_db fixture replaces eight _fresh/_fresh_db helpers and thirteen inline copies (ten hand-numbered t.db…t12.db in one file).
  • Two autouse guards. The socket guard makes any fetch that escapes its patch fail by name. Turning it on exposed the second problem: a dozen tests were calling the real Anthropic API with a placeholder key on every run, including in CI, spending 1.4s each in the SDK's retry backoff and swallowing the error. The Anthropic guard makes an unpatched call fail at once on the same exception path. That is the 2 seconds.
  • helpers.py: llm_reply (was _haiku_reply ×4, _haiku, _resp, _reply), Block/Resp (×4 byte-identical), tool_by_name (×2), and drive_tool, the get_reply-through-one-tool-call scaffold four files carried in full.

Commit 2 — 51 files → 15, grouped by module under test. Concatenated section by section; every original docstring is kept as a comment block heading its section, because those carry the incident behind the test. Imports hoisted and merged. Three placements go by what the file tests rather than its name: test_weather_city → morning (its docstring says weather.py was innocent), test_add_price_watch_tool → agent (it tests dispatch), test_shows → opening (shared seeded-cache machinery).

Test plan

  • ruff check . clean
  • pytest -q — 1,430 passed after each commit, same as main
  • Suite passes in a clean environment with no .env and no exported keys
  • --durations shows nothing over 0.5s; the retry-backoff tests are gone
  • CI green

🤖 Generated with Claude Code

https://claude.ai/code/session_018QpUhnYC3U7KxF8QCeenfQ

pythonjeff and others added 2 commits September 14, 2026 14:52
…d setup

- tests/conftest.py sets the env placeholders before any palmer import, so
  the eight module-level load_dotenv() calls go, and with them the last way
  real keys could reach a test. PALMER_NO_SCHEDULER is set there too, which
  retires the three `with patch("...BackgroundScheduler.start"): import
  main` blocks and the collection-order dependency they hid.
- fresh_db fixture replaces eight _fresh/_fresh_db helpers and thirteen
  inline copies (ten of them hand-numbered t.db..t12.db in one file). Files
  whose tests reach the setup through a local helper keep that helper.
- Two autouse guards. The socket guard makes any fetch that escapes its
  patch fail by name instead of succeeding silently on a machine with live
  keys. The Anthropic guard came out of turning the first one on: a dozen
  tests were calling the real API with a placeholder key on every run,
  spending 1.4s each in the SDK's retry backoff and swallowing the error.
  The suite is 5s now against 7s before, for that reason.
- tests/helpers.py holds llm_reply (was _haiku_reply x4, _haiku, _resp,
  _reply), Block/Resp (x4, byte-identical), tool_by_name (x2) and
  drive_tool, the get_reply-through-one-tool-call scaffold that four files
  each carried in full. Each of those keeps a short wrapper naming its
  patches; the wrappers carry distinct names so the files can merge.

1,430 tests before and after.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QpUhnYC3U7KxF8QCeenfQ
Each new file is the concatenation of the old ones, section by section,
with every original docstring kept as a comment block at the top of its
section — those carry the incident that led to the test and are the part
worth keeping. Imports are hoisted and merged; the two identical PHONE
constants collapse to one. Names that would have collided were given
distinct names in the previous commit.

    test_db          test_db_claims, test_phantom_history, test_local_day_guards
    test_userprofile test_profile_schema, test_profile_freshness, test_user_covered, test_onboard
    test_time        test_timeutil, test_reminder_time, test_reminder_recurrence, test_clock_correctness
    test_weather     test_weather, test_weather_source, test_weather_locations, test_forecast_audit
    test_shopping    test_price_watches, test_price_daily_cap, test_price_alert, test_amazon_watches
    test_watches     test_watches_story, test_watches_genre, test_corroboration, test_subject_dedup, test_rubrics, test_followup
    test_news        test_sources, test_trends, test_tickers
    test_morning     test_morning_link, test_morning_schedule, test_briefing_shape, test_weather_city, test_repetition
    test_page        test_page, test_cards, test_my_page_tool, test_arrange_page_tool
    test_opening     test_opening, test_shows
    test_sms         test_sms_chunking, test_link_integrity, test_tapback, test_reaction_interpret
    test_agent       test_calibration, test_reply_resilience, test_commute, test_add_price_watch_tool
    test_guards      test_guards_and_flights, test_scheduler_config
    test_home, test_sports unchanged

Three placements are by what the file tests rather than by its name:
test_weather_city tests the morning drafter and the agent write path (its
own docstring says weather.py was innocent), test_add_price_watch_tool
tests dispatch, and test_shows shares Opening's seeded-cache machinery.

1,430 tests before and after.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018QpUhnYC3U7KxF8QCeenfQ
fresh_db's monkeypatch used raising=False, which silently no-ops when
DATABASE_URL is set instead of failing loudly like the old per-file
fixtures did — a dev with DATABASE_URL exported would have tests run
against a real Postgres database instead of a fresh SQLite file. Restore
the loud failure with an explicit assertion.

Also finish the consolidation: six files still reimplemented fresh_db's
two-line setup locally instead of using the shared fixture, and three
more rebuilt helpers.llm_reply's mock shape by hand in files that already
import it. Fix four leftover "Run: pytest test_X.py" comments pointing at
files this PR deleted, and correct CLAUDE.md's claim that the network
guard "fails by name" — netutil._http_get_json's six callers catch every
exception by contract and return None, so an escape there still reads as
an empty result rather than a named failure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Re-applies the morning-format, preview-card and local-day test changes into
the consolidated test_morning.py, test_page.py and test_userprofile.py.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Base automatically changed from chore/package-layout to main October 15, 2026 01:00
@pythonjeff
pythonjeff merged commit 5ebd9fa into main Oct 15, 2026
1 check passed
@pythonjeff
pythonjeff deleted the chore/test-consolidation branch October 15, 2026 01:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant