Skip to content

fix(e2e): bump stale 2026-06-07 seed dates so /v/open tests stop timing out - #227

Merged
tomqwu merged 1 commit into
mainfrom
fix-e2e-stale-dates
Jun 25, 2026
Merged

tomqwu merged 1 commit into
mainfrom
fix-e2e-stale-dates

Conversation

@tomqwu

@tomqwu tomqwu commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Why

Four e2e tests (test_decline_reopen, test_event_roster_fill, test_open_shifts, test_swap_marketplace) seed a "Sunday 10am Service" event with hard-coded date 2026-06-07 and then wait for it on /v/open (or the swap-open list). Those volunteer-facing views filter out past events, so as soon as today's date rolled past 2026-06-07 the selector #open-list:has-text('Sunday 10am Service') started timing out on every PR — blocking the E2E lane for all new work, not just the PR that happens to be open.

PR #226 (calendar/export auth) is currently red on this exact pattern. Landing this first unblocks both #226 and the in-flight sprint-4-pr-4-5c-calendar-org-export-auth work.

What changed

  • tests/e2e/_helpers.py — new next_sunday_iso() helper returning a Sunday at least 7 days from date.today(). Keeps the seed event reliably in the future regardless of when the suite runs.
  • tests/e2e/test_decline_reopen.py, test_event_roster_fill.py, test_open_shifts.py, test_swap_marketplace.py — replace the literal "2026-06-07" with next_sunday_iso().

Scope

Narrow on purpose. Seven other e2e files still pass "2026-06-07", but they exercise admin views that do not filter on date, and some pair the date with a hard-coded solver window (e.g. _solve_and_publish uses from=2026-05-19, to=2026-06-30). Bumping their date without also bumping the window would break previously-green tests. Left as a follow-up.

Validation

  • poetry run black tests/e2e/ clean
  • poetry run ruff check tests/e2e/ clean
  • poetry run pytest --co tests/ collects 361 tests (imports OK)
  • E2E suite could not run locally — the pre-installed Playwright Chromium is v1194 while the CI install pulls v1223, so the launch fails. CI is the source of truth for this lane.

Follow-ups

  • Sweep the remaining 7 e2e files that still use 2026-06-07. Bump the dates and the matching solver windows in the same PR.

Generated by Claude Code.


Generated by Claude Code

Summary: Four e2e tests (test_decline_reopen, test_event_roster_fill,
test_open_shifts, test_swap_marketplace) seed a "Sunday 10am Service"
event with hard-coded date 2026-06-07 and then wait for it on `/v/open`
(or the swap-open list). Those volunteer-facing views filter out past
events, so as soon as today's date passed 2026-06-07 the selector
`#open-list:has-text('Sunday 10am Service')` started timing out on
every PR — blocking the E2E lane for all new work.

The fix replaces the literal with a `next_sunday_iso()` helper in
`tests/e2e/_helpers.py` that always returns a Sunday at least 7 days
out, so the seed event is reliably in the future regardless of when the
suite runs.

Scoped narrowly: only the 4 tests whose date drives a future-filtered
view are touched. Other tests still using 2026-06-07 (analytics,
recurring, smoke, etc.) interact with admin views that don't filter on
date, and some pair the date with a hard-coded solver window — bumping
their date would require also bumping the window. Left for a follow-up.

Changed files:
- tests/e2e/_helpers.py — add next_sunday_iso() helper
- tests/e2e/test_decline_reopen.py
- tests/e2e/test_event_roster_fill.py
- tests/e2e/test_open_shifts.py
- tests/e2e/test_swap_marketplace.py

Validation:
- poetry run black tests/e2e/ — clean
- poetry run ruff check tests/e2e/ — clean
- poetry run pytest --co tests/ — 361 tests collected (imports OK)
- E2E suite couldn't run locally (Playwright browser version mismatch);
  CI is the source of truth for the E2E lane.

Follow-ups:
- Sweep the remaining 7 e2e files that still use 2026-06-07; they pass
  today but will rot. Bump dates AND the associated solver windows in
  the same PR to avoid breaking previously-green tests.
@tomqwu
tomqwu marked this pull request as ready for review June 25, 2026 13:46
@tomqwu
tomqwu merged commit 934dae4 into main Jun 25, 2026
5 checks passed
tomqwu pushed a commit that referenced this pull request Jun 25, 2026
Summary: `GET /api/v1/calendar/org/export` previously accepted `person_id`
as a query parameter and used it as the auth proxy — "must refer to an
admin in this org." Person ids are deterministic (often email-derived
slugs), so anyone who could enumerate or guess an admin's id could
download the entire org's events. Auth is now JWT-only via
Depends(get_current_admin_user), with the same-org check delegated to
verify_org_member. This closes the follow-up explicitly named in PR
#226 (4.5b).

Changed files:
- api/routers/calendar.py — replace spoofable person_id query param
  with Depends(get_current_admin_user) + verify_org_member(current_admin,
  org_id). The `person_id` query param is dropped from the signature; any
  extra query params remaining on legacy callers are ignored by FastAPI.
- tests/api/test_calendar_auth.py — new TestOrgExportAuth class with 5
  cases: unauth → 401/403, volunteer-in-same-org → 403, admin-cross-org
  → 403, admin-same-org-no-events → 404, and an explicit regression test
  that a volunteer cannot escalate by passing an admin's id as the legacy
  ?person_id= spoof param.
- tests/conftest.py — extend the unit-tier verify_org_member monkey-patch
  list to include api.routers.calendar (it now imports the symbol
  directly so the patch must rebind there too).
- tests/unit/test_calendar.py — drop the now-dead ?person_id= query param
  from the URLs; remove `test_org_export_as_volunteer_denied` (mocked auth
  always returns an admin so the case is not representable in this tier —
  the equivalent assertion lives in TestOrgExportAuth).
- tests/contract/openapi.snapshot.json — refreshed via
  make update-openapi-snapshot: person_id parameter removed, HTTPBearer
  security added to the operation, description bumped.

Validation:
- poetry run pytest tests/api/test_calendar_auth.py tests/unit/test_calendar.py
  → 32 passed
- make test-unit-fast → 338 passed, 21 skipped
- poetry run pytest tests/api tests/contract → 316 passed
- poetry run pytest tests/cli tests/integration → 59 passed
- poetry run black api tests — clean
- poetry run ruff check api tests — clean

Follow-ups:
- E2E lane will only go green once #227 (fix-e2e-stale-dates) lands and
  this branch is rebased onto the updated main. The stale-date flake is
  unrelated to this change and pre-exists in the suite.
tomqwu pushed a commit that referenced this pull request Jun 25, 2026
Summary: `GET /api/v1/calendar/org/export` previously accepted `person_id`
as a query parameter and used it as the auth proxy — "must refer to an
admin in this org." Person ids are deterministic (often email-derived
slugs), so anyone who could enumerate or guess an admin's id could
download the entire org's events. Auth is now JWT-only via
Depends(get_current_admin_user), with the same-org check delegated to
verify_org_member. This closes the follow-up explicitly named in PR

Changed files:
- api/routers/calendar.py — replace spoofable person_id query param
  with Depends(get_current_admin_user) + verify_org_member(current_admin,
  org_id). The `person_id` query param is dropped from the signature; any
  extra query params remaining on legacy callers are ignored by FastAPI.
- tests/api/test_calendar_auth.py — new TestOrgExportAuth class with 5
  cases: unauth → 401/403, volunteer-in-same-org → 403, admin-cross-org
  → 403, admin-same-org-no-events → 404, and an explicit regression test
  that a volunteer cannot escalate by passing an admin's id as the legacy
  ?person_id= spoof param.
- tests/conftest.py — extend the unit-tier verify_org_member monkey-patch
  list to include api.routers.calendar (it now imports the symbol
  directly so the patch must rebind there too).
- tests/unit/test_calendar.py — drop the now-dead ?person_id= query param
  from the URLs; remove `test_org_export_as_volunteer_denied` (mocked auth
  always returns an admin so the case is not representable in this tier —
  the equivalent assertion lives in TestOrgExportAuth).
- tests/contract/openapi.snapshot.json — refreshed via
  make update-openapi-snapshot: person_id parameter removed, HTTPBearer
  security added to the operation, description bumped.

Validation:
- poetry run pytest tests/api/test_calendar_auth.py tests/unit/test_calendar.py
  → 32 passed
- make test-unit-fast → 338 passed, 21 skipped
- poetry run pytest tests/api tests/contract → 316 passed
- poetry run pytest tests/cli tests/integration → 59 passed
- poetry run black api tests — clean
- poetry run ruff check api tests — clean

Follow-ups:
- E2E lane will only go green once #227 (fix-e2e-stale-dates) lands and
  this branch is rebased onto the updated main. The stale-date flake is
  unrelated to this change and pre-exists in the suite.
tomqwu added a commit that referenced this pull request Jun 25, 2026
Summary: `GET /api/v1/calendar/org/export` previously accepted `person_id`
as a query parameter and used it as the auth proxy — "must refer to an
admin in this org." Person ids are deterministic (often email-derived
slugs), so anyone who could enumerate or guess an admin's id could
download the entire org's events. Auth is now JWT-only via
Depends(get_current_admin_user), with the same-org check delegated to
verify_org_member. This closes the follow-up explicitly named in PR

Changed files:
- api/routers/calendar.py — replace spoofable person_id query param
  with Depends(get_current_admin_user) + verify_org_member(current_admin,
  org_id). The `person_id` query param is dropped from the signature; any
  extra query params remaining on legacy callers are ignored by FastAPI.
- tests/api/test_calendar_auth.py — new TestOrgExportAuth class with 5
  cases: unauth → 401/403, volunteer-in-same-org → 403, admin-cross-org
  → 403, admin-same-org-no-events → 404, and an explicit regression test
  that a volunteer cannot escalate by passing an admin's id as the legacy
  ?person_id= spoof param.
- tests/conftest.py — extend the unit-tier verify_org_member monkey-patch
  list to include api.routers.calendar (it now imports the symbol
  directly so the patch must rebind there too).
- tests/unit/test_calendar.py — drop the now-dead ?person_id= query param
  from the URLs; remove `test_org_export_as_volunteer_denied` (mocked auth
  always returns an admin so the case is not representable in this tier —
  the equivalent assertion lives in TestOrgExportAuth).
- tests/contract/openapi.snapshot.json — refreshed via
  make update-openapi-snapshot: person_id parameter removed, HTTPBearer
  security added to the operation, description bumped.

Validation:
- poetry run pytest tests/api/test_calendar_auth.py tests/unit/test_calendar.py
  → 32 passed
- make test-unit-fast → 338 passed, 21 skipped
- poetry run pytest tests/api tests/contract → 316 passed
- poetry run pytest tests/cli tests/integration → 59 passed
- poetry run black api tests — clean
- poetry run ruff check api tests — clean

Follow-ups:
- E2E lane will only go green once #227 (fix-e2e-stale-dates) lands and
  this branch is rebased onto the updated main. The stale-date flake is
  unrelated to this change and pre-exists in the suite.

Co-authored-by: Claude <noreply@anthropic.com>
tomqwu added a commit that referenced this pull request Jul 13, 2026
* fix(e2e): sweep stale 2026-06-07 seed dates + hardcoded solver windows

Summary:
Follow-up from PR #227. That PR bumped `2026-06-07` in four e2e files
that watch `/v/open` (which filters out past events), but explicitly
left the remaining seven for a separate sweep because their tests also
seed a hardcoded solver window (`from=2026-05-19, to=2026-06-30`) that
had to move in lockstep with the seed event date. Today (2026-07-13) is
already past `2026-06-07`, so a solve-and-publish flow can produce
an empty solution and drop /v/schedule assertions.

Adds `solver_window_around(seed_iso)` to `tests/e2e/_helpers.py`
returning a (from, to) 3-week bracket, then replaces the literal seed
date with `next_sunday_iso()` and the literal window with
`solver_window_around()` across all seven files.

Changed files:
- tests/e2e/_helpers.py — new `solver_window_around()` helper (docstring
  documents the rot pattern it prevents).
- tests/e2e/test_smoke_full_loop.py,
  tests/e2e/test_availability_solver.py,
  tests/e2e/test_bulk_notify.py,
  tests/e2e/test_calendar_subscription.py,
  tests/e2e/test_onboarding_wizard.py — inline seed/window inside the
  single test function.
- tests/e2e/test_notif_assign_unpublish.py,
  tests/e2e/test_analytics_recurring_swapreview.py — module-level
  `EV_DATE` / `FROM_DATE` / `TO_DATE` so the shared `_new_event` /
  `_solve_and_publish` helpers stay parameterless.
- tests/unit/test_e2e_helpers.py — new: 3 invariants for the helpers
  (Sunday-in-future, seed strictly inside window, deterministic).

Validation:
- `poetry run pytest tests/unit/test_e2e_helpers.py tests/unit/test_e2e_lane.py` → 5 passed
- `make test-unit-fast` → 341 passed, 21 skipped
- `poetry run pytest tests/api tests/contract tests/web` → 550 passed
- `poetry run black --check api tests` → clean
- `poetry run ruff check api tests` → clean
- `python -m py_compile` on all touched files → clean

Follow-ups:
- `2026-07-05` in test_analytics_recurring_swapreview.py (recurring-series
  start date, `#rs_sd`) is not on the same solve-and-publish path and is
  still ~2 weeks in the future — leaving alone to keep this sweep narrow.
  Worth bumping alongside a future rot fix.

* chore: re-trigger CI after Service Unavailable infra flake

The prior run's "Lint, type-check, and test" job failed at step 1
(Set up job) with "Failed to resolve action download info. Error:
Service Unavailable" — a GitHub Actions runner-provisioning outage,
not a real test failure. The e2e job on the same commit passed.

* chore: second retrigger after GHA action-metadata outage

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants