Skip to content

test(inbound): HTTP-level InboundEmailController tests#28

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

test(inbound): HTTP-level InboundEmailController tests#28
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 handler tests (escalated-go#34) at the .NET controller layer. The .NET inbound controller was already correctly wired via #26, but had no HTTP-level test coverage — this PR closes that gap.

What's tested

Instantiates the real `InboundEmailController` with:

  • In-memory EF Core `EscalatedDbContext` (via `TestHelpers.CreateInMemoryDb`)
  • Real `TicketService` + `InboundEmailRouter` + `InboundEmailService`
  • Real `PostmarkInboundParser` + `MailgunInboundParser`
  • `DefaultHttpContext` with a MemoryStream body + the `X-Escalated-Inbound-Secret` header

Six cases:

  1. NewTicket_ReturnsCreatedOutcome — new-sender path produces `outcome=creatednew` + `status=created` + persisted Ticket row.
  2. MatchedReply_ReturnsRepliedToExisting — canonical `<ticket-55@...>` `In-Reply-To` routes to the pre-seeded ticket, returns `outcome=repliedtoexisting` + `replyId > 0`, no second ticket created.
  3. Skipped_ReturnsSkipped — SNS subscription confirmation gets `outcome=skipped`; DB unchanged.
  4. MissingSecret_Returns401 — signature guard rejects requests with no header.
  5. BadSecret_Returns401 — signature guard rejects wrong-value headers.
  6. UnknownAdapter_Returns400 — dispatch guard.

Stacked PR

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

Test plan

  • CI runs `dotnet test` after rebase onto main (local dotnet CLI not available in this env).

Mirrors the Go handler tests (escalated-go#34) at the .NET
controller layer. Instantiates the real InboundEmailController
with an in-memory EF Core DbContext, real TicketService + Router
+ Service, and the Postmark + Mailgun parsers — then exercises:

  - NewTicket_ReturnsCreatedOutcome: new-sender path produces
    outcome=creatednew + status=created + persisted Ticket.
  - MatchedReply_ReturnsRepliedToExisting: canonical
    <ticket-55@...> In-Reply-To routes to the pre-seeded ticket
    and returns outcome=repliedtoexisting + replyId — no second
    ticket created.
  - Skipped_ReturnsSkipped: SNS subscription confirmation is
    skipped; DB unchanged.
  - MissingSecret_Returns401 / BadSecret_Returns401: signature
    guard rejects both no-header and wrong-header cases.
  - UnknownAdapter_Returns400: dispatch guard.

Closes the same "service built but HTTP boundary untested" gap
that Go had; .NET's controller was already wired correctly via
#26 but had no HTTP-level coverage.
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