Preconditions
Fixture event has an eligible audience, an empty audience, and scheduled mailings before, inside, and past the edit window.
Happy path
Choose a send_at time.
Date range picker is timezone-honest and refuses past send_at.
Save schedule.
The scheduled row shows recipient count, send time, and edit cutoff.
Cancel or let due time pass.
Cancel drains the queue; due send starts the async job tracker.
Failure modes
Send blocked on validation failure
Trigger: invalid send_at, unresolved token, or invalid audience.
Resolution: 400/409 blocks send before any email leaves.
Bounced recipient tracked and suppressed
Trigger: scheduled send bounces.
Resolution: bounce event is recorded and later scheduled sends skip the address.
Scheduled-message edit window
Trigger: edit is attempted after cutoff.
Resolution: 409 PAST_EDIT_WINDOW and stale row is not patched.
Retry on transient failure
Trigger: provider 5xx at due time.
Resolution: retry uses same Idempotency-Key and sent_count is one.
Idempotency-key on test-send
Trigger: test-send double-click while scheduling.
Resolution: one test email is produced.
Recipient resolution empty
Trigger: saved audience resolves to zero.
Resolution: 409 NO_RECIPIENTS and no queued scheduled message.
Token rendering fallback
Trigger: token becomes undefined when the schedule executes.
Resolution: fallback copy renders for every recipient.
Cancel scheduled before send
Trigger: organizer cancels before send-due.
Resolution: pending count decrements, sends refund, audit row records actor.
Past due edit rejected
Trigger: organizer opens row after send-due has passed.
Resolution: edit controls are disabled and API still returns 409.
Due-time fan-out starts once
Trigger: worker polls the same due scheduled message twice.
Resolution: lease/idempotency creates one async job.
Deployed-runtime gap
Trigger: deployed run on 2026-04-29 observed scheduled mailing create 409 NO_RECIPIENTS; the probe locks this in until the gap is closed.
Resolution: story keeps the documented 409 assertion until deployed audience resolution is proven.
Stable test attributes
Visibility teeth. Each attribute must be effectively visible when active.
scheduled-messages-page | Page | Scheduler |
scheduled-message-date-picker | Form | send_at |
scheduled-message-audience-picker | Form | Audience |
scheduled-message-editor | Form | Message body |
scheduled-message-save-cta | Toolbar | Schedule |
scheduled-message-cancel-cta | Row | Cancel |
scheduled-message-job-tracker | Page | Due fan-out |
scheduled-message-warning | Page | Validation |
scheduled-message-gap-panel | Page | Runtime gap |
Agent test plan
- scheduled-messages-renders
- schedule-message
- cancel-scheduled-message
- deployed-runtime-gap