Skip to content

test(inbound): HTTP-level InboundEmailController tests (@WebMvcTest)#31

Open
mpge wants to merge 1 commit intofeat/inbound-email-orchestrationfrom
test/inbound-controller
Open

test(inbound): HTTP-level InboundEmailController tests (@WebMvcTest)#31
mpge wants to merge 1 commit intofeat/inbound-email-orchestrationfrom
test/inbound-controller

Conversation

@mpge
Copy link
Copy Markdown
Member

@mpge mpge commented Apr 24, 2026

Summary

Mirrors the Go (escalated-go#34) and .NET (escalated-dotnet#28) patterns at the Spring Boot controller layer. Uses `@WebMvcTest` to boot just the controller with a MockMvc client, MockBean'd `InboundEmailService`, and the real `PostmarkInboundParser` imported so payload parsing isn't mocked.

What's tested

Nine cases:

  1. newTicket_returnsCreatedNewOutcome — new-ticket happy path.
  2. matchedReply_returnsRepliedToExistingOutcome — canonical `<ticket-55@...>` `In-Reply-To` routes + returns `replyId`.
  3. skipped_returnsSkippedOutcome — SNS confirmation → `outcome=skipped`, null `ticketId`.
  4. surfacesProviderHostedAttachments — Mailgun-style provider-hosted attachments surface in `pendingAttachmentDownloads[].downloadUrl`.
  5. missingSecret_returns401 — signature guard, service never called.
  6. badSecret_returns401 — wrong-value header rejected.
  7. unknownAdapter_returns400 — dispatch guard.
  8. missingAdapter_returns400 — no adapter query/header.
  9. adapterHeader_isAcceptedAsFallbackToQueryParam — adapter selection via `X-Escalated-Adapter` header.

Stacked PR

Based on `feat/inbound-email-orchestration` (#29) so the service + outcome enum are available. Merge order: #26#27#28#29 → this PR.

Test plan

  • CI runs `./gradlew test` after rebase onto main (local JDK unavailable in this env).

Mirrors the Go (escalated-go#34) and .NET (escalated-dotnet#28)
patterns at the Spring Boot controller layer. Uses @WebMvcTest
to boot just the controller with a MockMvc client, MockBean'd
InboundEmailService, and the real PostmarkInboundParser imported
so payload parsing isn't mocked.

Nine cases cover:
  - new-ticket happy path → outcome=created_new
  - matched reply via canonical <ticket-55@...> In-Reply-To →
    outcome=replied_to_existing + replyId
  - SNS confirmation skip → outcome=skipped, null ticketId
  - provider-hosted attachments surfaced in
    pendingAttachmentDownloads[].downloadUrl
  - missing/bad secret → 401 + service never called
  - unknown/missing adapter → 400 + service never called
  - adapter selection via X-Escalated-Adapter header (fallback
    to query param)

Closes the HTTP-level coverage gap — the Spring controller was
already wired correctly via #29 but had no MockMvc tests.
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