Skip to content

test(api): cover replay and batch send endpoints#19

Merged
voyvodka merged 1 commit intomainfrom
test/replay-batch-coverage
May 5, 2026
Merged

test(api): cover replay and batch send endpoints#19
voyvodka merged 1 commit intomainfrom
test/replay-batch-coverage

Conversation

@voyvodka
Copy link
Copy Markdown
Owner

@voyvodka voyvodka commented May 5, 2026

Summary

  • Adds 6 integration tests on the public message endpoints under `/api/v1/messages`.
  • Reuses the existing `TestWebApplicationFactory` (InMemory database) — no Testcontainers required.

Coverage

Batch send (`POST /api/v1/messages/batch`):

  • All-valid path: 3/3 accepted, per-item results, totalEnqueuedMessages=3
  • Mixed path: 2 valid + 1 unknown event type → 2 accepted + 1 rejected (rejected item carries error code)
  • Empty messages: rejected with 422 by the validator

Replay (`POST /api/v1/messages/replay`):

  • Happy path: 2 in-range Failed messages replayed, 1 out-of-range skipped, new rows created with status=pending
  • Empty range: sourceCount = replayedCount = 0
  • Missing eventType/eventTypeId: 422

Why

Replay and batch are in production today (since v0.1.0) but only had unit-level coverage on the underlying repository — no end-to-end HTTP coverage. These tests guard against regressions in controller logic, validator behavior, and queue interaction.

Out of scope

  • Per-endpoint rate-limit + DeliveryWorker integration (separate Cleanup C task — DeliveryWorker e2e with Testcontainers).

Labels

`enhancement` `api`

Test plan

  • CI: `dotnet test` green
  • All 6 new tests pass on Linux runner

Adds 6 integration tests on the public message endpoints, reusing the
existing TestWebApplicationFactory (InMemory database, no Testcontainers
required).

Batch send:
- All valid items accepted (3/3) and reported per-item.
- Mixed batch with one unknown event type returns 2 accepted + 1
  rejected, with the failed item carrying an error code.
- Empty messages array is rejected with 422 by the validator.

Replay:
- Source candidates inside the date range are replayed as new pending
  rows; out-of-range messages are skipped.
- A range with no candidates returns sourceCount=replayedCount=0.
- A request without an eventType / eventTypeId is rejected with 422.

These flows previously had unit-level coverage on the underlying
repository but no end-to-end HTTP coverage; the tests guard against
regressions in the controller, validator, and queue interaction.
@voyvodka voyvodka added enhancement New feature or request api API layer and endpoints labels May 5, 2026
@voyvodka voyvodka enabled auto-merge (squash) May 5, 2026 07:40
@voyvodka voyvodka merged commit b02e434 into main May 5, 2026
7 checks passed
@voyvodka voyvodka deleted the test/replay-batch-coverage branch May 5, 2026 07:42
voyvodka added a commit that referenced this pull request May 8, 2026
…skeleton, no flash, awaited refetches (#68)

Six findings from the dashboard-expert audit, all in one PR before F12.

a11y on Modal (audit findings #9 + #10) — the dialog was an unlabelled,
focus-leaky panel: no role="dialog", no aria-modal, no aria-labelledby,
and Tab/Shift+Tab walked right out of the modal into the document
underneath. Fixed all four:
- Panel now carries role="dialog" + aria-modal="true" with useId-stable
  aria-labelledby (and aria-describedby when a description is passed).
- Close button got an explicit aria-label="Close dialog".
- New keydown handler implements a Tab/Shift+Tab focus trap that wraps
  focus between the first/last focusable descendants (or pins to the
  panel itself when nothing inside is focusable).

useDeliveryFeed reconnect cleanup (audit finding #19) — onreconnected
now resets lastHealthChange to null. Without this, an EndpointsPage that
re-subscribed after a disconnect would re-apply the pre-disconnect
snapshot from cache, masking whatever the server may have transitioned
to during the gap.

DeliveryLogPage skeleton (audit finding #22) — replaced the bare "Loading..."
text with a layout-preserving skeleton card (header strip + two attempt
blocks) plus aria-busy on the wrapper. No layout shift on completion.

EndpointsPage flash (audit finding #23) — the page was checking
applications.length === 0 before the apps fetch had returned, briefly
flashing "Create an application first" on every cold load. New
applicationsLoading state gates that branch so the spinner shows until
the apps fetch completes; only then does the empty / endpoints branch
render.

ApplicationsPage floating promise (audit finding #26) — both handleCreate
and the delete-confirm onConfirm called fetchApps() without awaiting it,
so any refetch rejection became an unhandled promise. Both now await,
landing failures inside the surrounding try-catch / setError flow.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api API layer and endpoints enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant