# `MailglassInbound.TestAssertions`
[🔗](https://github.com/szTheory/mailglass/blob/v2.3.0/lib/mailglass_inbound/test_assertions.ex#L1)

Inbound test assertions — the inbound mirror of
`Mailglass.TestAssertions`.

Adopters `import MailglassInbound.TestAssertions` (or `use` the
`MailglassInbound.MailboxCase` that imports it) and assert over the capture
tuple emitted by `MailglassInbound.Test.Ingress`:

    send(self(), {:inbound, message, outcome, route})

These assertions read that tuple with `assert_received`/`refute_received`, so
every assertion is **process-local** — `async: true` tests see only their own
captures.

## Matcher styles (`assert_inbound_received`)

    # 1. Presence (bare call)
    assert_inbound_received()

    # 2. Keyword match
    assert_inbound_received(subject: "Re: ticket #42", from: "alice@example.com")

    # 3. Struct pattern (macro — no explicit quoting)
    assert_inbound_received(%{subject: "Re: ticket #42"})

    # 4. Predicate fn
    assert_inbound_received(fn msg -> String.contains?(msg.subject, "ticket") end)

Supported keyword keys: `:subject`, `:from`, `:to`, `:tenant`, `:provider`,
`:envelope_recipient`. `:from`/`:to` match against the address-shaped lists
(`[%{address: ...}]`). Any other key flunks with a clear message.

## Outcome assertions

`assert_inbound_accepted/0`, `_ignored/0`, `_rejected/0`, `_bounced/0` key off
the persisted `ExecutionRun.outcome` enum carried by the captured
`outcome` map (`%{outcome: :accept | :ignore | :reject | :bounce | …}`). The
raw mailbox atoms `:accept | :ignore | {:reject, _} | {:bounce, _}` normalize
to that enum in `Execution.execute/2`, so the assertion can never drift from
what was written.

## Routing assertions

`assert_inbound_routed_to/1` matches the captured route
`%{status: :matched, mailbox: ^expected}`; `assert_inbound_no_match/0` matches
`%{status: :no_match}`.

## Negative assertion

`assert_no_inbound_received/0` refutes any captured `{:inbound, …}` tuple.

## These are ExUnit assertions, not domain errors

Every failure is an `ExUnit.AssertionError` raised by `assert`/`flunk` — NOT a
`%MailglassInbound.Error{}`. The errors-as-contract rule (CLAUDE.md) governs
the library's runtime API surface; test assertions are a test-time tool, so
they speak ExUnit. `ExUnit.Assertions` is imported from the bundled OTP
`:ex_unit` app — no Hex dependency is added.

## PII posture

Failure messages embed caller-supplied matcher values (e.g. a `:subject` or
`:from` address) so a failing adopter test has the context it needs. Those
values appear only in the adopter's own test output — never in telemetry, log
streams, or cross-tenant surfaces (T-47-09). The assertions add no telemetry.

# `assert_inbound_accepted`
*since 0.2.0* *macro* 

Asserts the next captured inbound was accepted (`outcome == :accept`).

Reads the oldest unconsumed capture (`assert_received` is FIFO and consumes
the matched tuple), so drive one message per outcome assertion.

# `assert_inbound_bounced`
*since 0.2.0* *macro* 

Asserts the next captured inbound was bounced (`outcome == :bounce`).

Reads the oldest unconsumed capture (`assert_received` is FIFO and consumes
the matched tuple), so drive one message per outcome assertion.

# `assert_inbound_ignored`
*since 0.2.0* *macro* 

Asserts the next captured inbound was ignored (`outcome == :ignore`).

Reads the oldest unconsumed capture (`assert_received` is FIFO and consumes
the matched tuple), so drive one message per outcome assertion.

# `assert_inbound_no_match`
*since 0.2.0* *macro* 

Asserts the next captured inbound did not match any route
(route `%{status: :no_match}`).

Reads the oldest unconsumed capture (`assert_received` is FIFO and consumes
the matched tuple), so drive one message per routing assertion.

# `assert_inbound_received`
*since 0.2.0* *macro* 

Asserts that at least one inbound message was captured in the current test
process. See the moduledoc for the four matcher styles.

## Style 1: presence (bare call)

    assert_inbound_received()

# `assert_inbound_received`
*since 0.2.0* *macro* 

Asserts that an inbound message matching the given matcher was captured.

## Style 2: keyword list

    assert_inbound_received(subject: "Welcome", from: "alice@example.com")

## Style 3: struct pattern (no quoting needed)

    assert_inbound_received(%{subject: "Welcome"})

## Style 4: predicate function

    assert_inbound_received(fn msg -> msg.tenant_id == "acme" end)

# `assert_inbound_rejected`
*since 0.2.0* *macro* 

Asserts the next captured inbound was rejected (`outcome == :reject`).

Reads the oldest unconsumed capture (`assert_received` is FIFO and consumes
the matched tuple), so drive one message per outcome assertion.

# `assert_inbound_routed_to`
*since 0.2.0* *macro* 

Asserts the next captured inbound was routed to `expected_mailbox`
(route `%{status: :matched, mailbox: ^expected_mailbox}`).

Reads the oldest unconsumed capture (`assert_received` is FIFO and consumes
the matched tuple), so drive one message per routing assertion.

# `assert_no_inbound_received`
*since 0.2.0* *macro* 

Asserts that NO inbound message was captured in the current test process.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
