# `Mailglass.Adapters.Fake`
[🔗](https://github.com/szTheory/mailglass/blob/v0.1.0/lib/mailglass/adapters/fake.ex#L1)

In-memory, time-advanceable test adapter (TRANS-02, D-01..D-03).

**The merge-blocking release gate (D-13).** Every PR runs the full
pipeline against this adapter. Mirrors `Swoosh.Adapters.Sandbox`:
ownership-by-pid, `$callers` inheritance, `allow/2` for
cross-process delegation (LiveView, Playwright, Oban worker), shared
mode for global tests.

## Stored record shape

    %{
      message: %Mailglass.Message{},
      delivery_id: Ecto.UUID.t(),
      provider_message_id: String.t(),
      recorded_at: DateTime.t()
    }

Records `%Mailglass.Message{}` (NOT raw `%Swoosh.Email{}`) so
`assert_mail_sent(mailable: UserMailer)` can recover the originating
Mailable. Tenant stamped from the message at record time.

`provider_message_id` lets `trigger_event/3` look up the Delivery row
by id and simulate a Phase-4 webhook event via the REAL
`Events.append_multi/3 + Projector.update_projections/2` write path
(D-03). This keeps the Fake in sync with the production write path.

## Public API

- `deliveries/0,1` — list recorded deliveries (optionally filtered)
- `last_delivery/0,1` — most recent (by insertion order)
- `clear/0,1` — wipe current owner's bucket (`:all` wipes every bucket)
- `trigger_event/3` — simulate a webhook-shaped event
- `advance_time/1` — delegates to `Mailglass.Clock.Frozen.advance/1`
- Ownership: `checkout/0`, `checkin/0`, `allow/2`, `set_shared/1`

## Async: true safety

Ownership keys every ETS bucket by owner pid; each test is its own
owner (via `Mailglass.MailerCase` setup, Plan 06). Cross-process
deliveries (LiveView, Task.Supervisor, Oban worker) resolve via
`$callers` or explicit `allow/2`.

# `advance_time`
*since 0.1.0* 

```elixir
@spec advance_time(integer()) :: DateTime.t()
```

Advances the process-local frozen clock. Delegates to `Mailglass.Clock.Frozen.advance/1`.

# `allow`

# `checkin`

# `checkout`

# `clear`

# `clear`

```elixir
@spec clear(keyword() | :all) :: :ok
```

Clears recorded deliveries.

- `clear()` — clears the current owner's bucket.
- `clear([owner: pid])` — clears the specified owner's bucket.
- `clear(:all)` — clears every owner's bucket (flushes the entire ETS table).

# `deliveries`

```elixir
@spec deliveries(keyword()) :: [map()]
```

Returns all recorded deliveries for the current owner (or a specified owner).

## Options

- `:owner` — pid; defaults to `self()`
- `:tenant` — filter by `record.message.tenant_id`
- `:mailable` — filter by `record.message.mailable`
- `:recipient` — filter by any address in `record.message.swoosh_email.to`

# `get_shared`

# `last_delivery`

```elixir
@spec last_delivery(keyword()) :: map() | nil
```

Returns the most recent delivery for the current owner, or `nil`.

# `set_shared`

# `trigger_event`
*since 0.1.0* 

```elixir
@spec trigger_event(String.t(), atom(), keyword()) ::
  {:ok, Mailglass.Events.Event.t()} | {:error, term()}
```

Simulates a webhook-shaped event for a previously-delivered message (D-03).

Looks up the `%Delivery{}` row by `provider_message_id`, builds an
`%Events.Event{}`, and runs it through
`Events.append_multi/3 + Projector.update_projections/2` inside
`Repo.transact/1`. This is the SAME write path Phase 4 webhook ingest
uses — the Fake proves the production write path.

After the transaction commits, broadcasts via
`Projector.broadcast_delivery_updated/3` (D-04).

## Opts

- `:occurred_at` — DateTime; defaults to `Mailglass.Clock.utc_now()`
- `:reject_reason` — atom from the reject_reason closed set
- `:metadata` — map stored in `Event.metadata` (Phase 4: `raw_payload`
  moved to `mailglass_webhook_events`; see D-15)

## Returns

- `{:ok, %Events.Event{}}` on success
- `{:error, :not_found}` if `provider_message_id` has no matching Delivery
- `{:error, term()}` for other failures

---

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