# `Sigra.Audit.Assertions`
[🔗](https://github.com/sztheory/sigra/blob/v1.20.0/lib/sigra/audit/assertions.ex#L1)

Plain-function helpers for asserting on persisted audit rows in tests.

All functions take an explicit `repo` so host apps can use their own
`Ecto.Repo` (including Sandbox ownership) without hidden globals.

Queries always apply a deterministic `ORDER BY inserted_at DESC, id DESC`
before `LIMIT 1` so the "latest" row is stable under concurrent inserts.

## Optional audit (`:audit_optional`)

Host apps may omit `:audit_schema` in `Sigra.Config`. For tests that only
sometimes run with audit enabled, tag examples with `@tag :audit_optional`
and skip assertions when `audit_schema` is `nil` (see
`test/sigra/audit/audit_assertions_test.exs`).

# `assert_audit_fields`

```elixir
@spec assert_audit_fields(Ecto.Repo.t(), module(), map()) :: :ok
```

Asserts the latest audit row matching filter fields derived from
`required_map` matches every field in the map.

Non-nil entries among `[:action, :outcome, :actor_id, :effective_user_id, :target_id, :organization_id, :target_type]` in `required_map` are
used as `Sigra.Audit.query/1` filters (AND). At least one such field must
be non-nil so the row is uniquely targeted (typically `:action`).

For `:metadata`, `expected` must be a map: every key in `expected` must
exist in the stored metadata with the same value (atoms and strings are
treated as equivalent keys for lookup).

# `latest_audit_event`

```elixir
@spec latest_audit_event(Ecto.Repo.t(), module(), keyword()) :: struct() | nil
```

Returns the latest audit row matching `filters`, or `nil`.

`filters` may only contain keys from `Sigra.Audit.Query.allowed_filters/0`.
`:order_by` and other unsupported keys must not be passed to
`Sigra.Audit.query/1` — ordering is always applied here.

Raises `ArgumentError` if `audit_schema` is `nil`.

---

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