Skip to content

Make event timeline tests deterministic when the upcoming catalogue is empty #299

Description

@alexeygrigorev

Parent/reference: #228 (scheduled/full-regression recovery)
Related release evidence: #253, #261, #295
Related Event contracts: #45, #289

Problem

The event timeline tests assume that the checked projection always contains an upcoming
Event. That assumption is false once the wall clock passes the latest checked event. On the
current release base origin/main at
face8e4808d65afbf0374d1ced7a88079950d663, the newest checked Event starts at
2026-08-31T17:00:00+02:00. With the current date at or after that instant,
event_groups() correctly returns an empty upcoming tuple, but several tests index
upcoming[0] or require an explanation pill to be present.

The focused reproduction on this base is:

uv run --frozen pytest content/tests/test_events_timeline.py -q
9 failed, 30 passed, 455 subtests passed

The same deterministic-test defect is visible in scheduled full regression
run 33441614182. The
parallel Django traceback/pickle error is a secondary reporting effect; this issue owns only
the event-timeline test assumptions.

Exact affected tests

All affected tests are in content/tests/test_events_timeline.py:

  • EventTimelineRouteTests.test_clean_hub_is_upcoming_and_past_filter_is_paginated
    (indexes groups.upcoming[0] after rendering with an uncontrolled clock);
  • EventIndexDesignSystemTests.test_rows_are_built_from_the_shared_design_system_primitives;
  • EventKindsExplainerTests.test_both_views_explain_every_kind_a_row_can_carry;
  • EventKindsExplainerTests.test_every_kind_pill_is_bound_to_its_own_explanation;
  • EventDetailDesignSystemTests.test_a_live_podcast_recording_keeps_the_mint_pill_the_index_gives_it;
  • EventDetailDesignSystemTests.test_an_event_without_speakers_or_links_draws_neither_of_them;
  • EventDetailDesignSystemTests.test_detail_carries_its_own_stylesheet_and_loads_no_legacy_css;
  • EventDetailDesignSystemTests.test_the_kind_pill_and_the_state_word_match_the_index; and
  • EventDetailDesignSystemTests.test_the_page_is_built_from_the_shared_design_system_primitives.

Some failures are reported as parametrized subfailures or as follow-on errors after the first
empty-catalogue assumption; the complete list above is the affected contract surface.

Normative authority

Scope

Repair only the event-timeline test fixtures/clock control and add explicit empty-upcoming
coverage. The implementation must make the tests describe the public contract independently of
the calendar date.

The non-empty assertions may use an injected aware instant and a deterministic checked record
or test projection. The route request and the expected EventGroups must use the same instant;
tests must not depend on whichever Event happens to be upcoming on the day they run. Tests that
need a representative event must select one from the controlled fixture rather than indexing a
wall-clock-derived production tuple.

Add an explicit empty-upcoming scenario that supplies EventGroups((), recent) (or an
equivalent deterministic test seam) to the public events view and verifies the existing public
empty state is a successful, bounded response containing No upcoming events. and no fabricated
event/kind assertions. The scenario must also prove that the past archive remains independently
renderable when the upcoming side is empty, if that behavior is exercised by the fixture.

Production event grouping, projection data, templates, URL patterns, cache policy, SEO metadata,
event ordering, lifecycle behavior, and all non-test source files remain unchanged. Do not alter
the latest event date merely to keep a test green.

Explicit non-goals

Dependencies and ordering

Acceptance criteria

  • The nine affected tests above pass on the current origin/main base at a clock after
    2026-08-31T17:00:00+02:00 and at a clock before the newest checked event, without changing
    production event grouping or checked projection data.
  • Every assertion requiring an upcoming record obtains it from a deterministic aware clock
    and fixture/projection seam; no affected test indexes a tuple whose membership depends on the
    host wall clock.
  • The route test uses one controlled instant for grouping and requests, and still verifies
    upcoming/past separation, pagination, escaped titles, canonical output, and status codes.
  • An explicit empty-upcoming test verifies GET /events is a 200 response with the existing
    No upcoming events. state and no exception, fabricated kind explanation, or invalid item
    access; the past archive remains covered independently where applicable.
  • The focused suite passes with an exact recorded command and count:
    uv run --frozen pytest content/tests/test_events_timeline.py -q.
  • The engineer generates and freezes the versioned verification plan for the exact base/head,
    with all component dispositions recorded. The independent tester recomputes that plan and
    validates the focused result plus the graph-selected shared checks; no required check is
    silently skipped.
  • Because this is test-only and has no product-page render change, screenshots are marked
    not_applicable with the graph/render-impact proof. The tester still verifies the Django route
    scenarios and runs the graph-selected Playwright tier if required by the plan.
  • Engineer, independent tester, and PM reports are posted to this issue before any commit;
    only the orchestrator may merge/push after PM acceptance, and on-call alone observes the
    resulting CI run.

Required verification scenarios

  1. Run the affected tests with an injected aware now before the newest checked event and prove
    the representative upcoming record and all kind/detail assertions remain meaningful.
  2. Run the same tests with an injected aware now after the newest checked event and prove the
    empty-upcoming path is intentional and bounded rather than an accidental IndexError.
  3. Exercise the public /events view with controlled non-empty and empty EventGroups; verify
    200 status, the existing empty copy, no invalid controls, and no cross-view leakage.
  4. Run the exact focused command, the generated graph-selected Django/CI checks, and any
    required Playwright tier from the frozen verification plan. Report the known independent
    Reconcile non-reproducible public projection after face8e4 #253/Restore face8e4 formatting, typing, and focused content gates #261/Register the event-slug data migration in the isolation inventory #295 failures separately if they remain on the shared base.

Lifecycle

This is a groomed P0 testing/bug issue. The engineer works in an isolated uncommitted worktree
from the exact current base and posts a frozen handoff. A separate tester reviews the diff,
recomputes the plan, runs the controlled before/after-clock and empty-upcoming scenarios, and
records screenshot not_applicable evidence. PM accepts only after all criteria and evidence
are complete. The engineer then commits with Closes #299; the orchestrator locally merges with
--no-ff, pushes main, and dispatches on-call. No pull request is created.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Must-have or release-blockingbugSomething isn't workingeventsArea: eventstestingArea: testing

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions