# `Spark.Test`
[🔗](https://github.com/ash-project/spark/blob/v2.7.0/lib/spark/test.ex#L5)

ExUnit helpers for testing Spark DSL verifiers.

Verifier errors raised inside Spark's `@after_verify` hook are converted to
stderr warnings by the framework rather than propagated as exceptions, so
the usual `assert_raise/2` testing pattern is unsuitable. This module
exposes a structured alternative: register the test process as a collector,
define DSL modules, and receive their `Spark.Error.DslError` values as data.

## Helpers

- `dsl_errors/1` — runs a do-block and returns all collected verifier errors
  as `[{module, [%Spark.Error.DslError{}]}, ...]`. The error-side primitive.
- `dsl_warnings/1` — runs a do-block and returns all collected verifier
  warnings as `[{module, [{message, location | nil}, ...]}, ...]`. The
  warning-side primitive.
- `assert_dsl_error/2` — asserts that the do-block produces at least one
  error matching a given pattern and returns the matched error.
- `assert_dsl_error/1` — like `/2` with a wildcard pattern: asserts at
  least one error of any kind.
- `assert_dsl_warning/2` — asserts that the do-block produces at least
  one warning matching a given pattern and returns the matched payload.
- `assert_dsl_warning/1` — like `/2` with a wildcard pattern: asserts at
  least one warning of any kind.
- `refute_dsl_errors/1` — asserts that the do-block produces no errors at
  all. Use for happy-path tests.
- `refute_dsl_warnings/1` — asserts that the do-block produces no
  warnings at all.

## Examples

    iex> Spark.Test.dsl_errors(do: :ok)
    []

    iex> Spark.Test.dsl_warnings(do: :ok)
    []

See `dsl_errors/1`, `dsl_warnings/1`, `assert_dsl_error/2`, and
`refute_dsl_errors/1` for full usage.

## Async safety

All helpers in this module are safe to use from `async: true` ExUnit tests.
The collection mechanism uses per-process state (`Process.put/2` and
point-to-point `send/2`) and does not cross process boundaries.

# `assert_dsl_error`
*macro* 

Asserts that the given do-block produces at least one verifier error and
returns the first one.

Equivalent to `assert_dsl_error/2` with a wildcard pattern: any
`Spark.Error.DslError` matches. See `assert_dsl_error/2` for details on
failure semantics, `refute_dsl_errors/1` for the inverse assertion, and
`assert_dsl_warning/1` for the warning-side counterpart.

# `assert_dsl_error`
*macro* 

Asserts that the given do-block produces at least one verifier error
matching `pattern` and returns the matched error.

`pattern` is matched against each `Spark.Error.DslError` in definition
order via `match?/2`. The first match is returned. If no error matches,
the test fails with a message that includes both the source form of the
pattern and the inspected list of all collected errors.

## Examples

    err =
      assert_dsl_error %Spark.Error.DslError{path: [:fields, :email]} do
        defmodule Elixir.MyExample do
          use MyApp.Validator
          # ... configuration that triggers the verifier
        end
      end

    assert err.message =~ "invalid email"

See `dsl_errors/1` for the underlying collection mechanism, including the
rules for module names defined inside the do-block.

# `assert_dsl_warning`
*macro* 

Asserts that the given do-block produces at least one verifier warning and
returns the first one.

Equivalent to `assert_dsl_warning/2` with a wildcard pattern: any warning
payload matches. See `assert_dsl_warning/2` for details on failure
semantics, and `assert_dsl_error/1` for the error-side counterpart.

# `assert_dsl_warning`
*macro* 

Asserts that the given do-block produces at least one verifier warning
matching `pattern` and returns the matched payload.

Each warning payload is a `{message, location | nil}` tuple. `pattern` is
matched against each payload in definition order via `match?/2`. The first
match is returned. If no payload matches, the test fails with a message
that includes both the source form of the pattern and the inspected list
of all collected warnings.

## Examples

    {message, _location} =
      assert_dsl_warning {_, _} do
        defmodule Elixir.MyExample do
          use MyApp.Validator
          # ... configuration that triggers the warning
        end
      end

    assert message =~ "deprecated"

See `dsl_warnings/1` for the underlying collection mechanism, including
the normalization of bare-string warnings to `{message, nil}` tuples.

# `dsl_errors`
*macro* 

Collects verifier errors from any Spark DSL modules defined inside the
given do-block.

Returns a list of `{module, [%Spark.Error.DslError{}]}` tuples in
definition order. Modules that compile cleanly produce no entry. Stderr
output emitted by verifiers (notably `{:warn, _}` returns) is suppressed
during the call.

## Examples

No DSL modules → empty list:

    errors = dsl_errors do
      :ok
    end
    # => []

A failing DSL module → its errors:

    errors = dsl_errors do
      defmodule Elixir.MyExample do
        use MyApp.Validator

        fields do
          # invalid configuration here
        end
      end
    end

    [{MyExample, [%Spark.Error.DslError{} | _]}] = errors

## Module names defined inside the do-block

Modules whose errors you intend to assert on must be defined with the
fully-qualified `Elixir.SomeName` form. A bare alias inside the macro's
callback is resolved against the surrounding test module's scope, while
the same alias outside the call is resolved at the top level — without
the `Elixir.` prefix the two atoms would not match.

## Cleanup

The collector flag is restored to its prior value (or unset if there was
no prior value) when the call returns or when the block raises. Stale
messages are drained from the test process's mailbox so subsequent calls
are not contaminated.

See `Spark.Dsl.Verifier` for the verifier callback contract.

# `dsl_warnings`
*macro* 

Collects verifier warnings from any Spark DSL modules defined inside the
given do-block.

Returns a list of `{module, [{message, location | nil}, ...]}` tuples in
definition order. Modules that compile cleanly produce no entry. The
payloads are normalized: bare-string warnings become `{message, nil}`,
tuple-form warnings preserve their `:erl_anno` location.

Stderr output emitted from the block is suppressed during the call.

## Examples

No DSL modules → empty list:

    warnings = dsl_warnings do
      :ok
    end
    # => []

A DSL module with a warning verifier → its payloads:

    warnings = dsl_warnings do
      defmodule Elixir.MyExample do
        use MyApp.Validator
        # ... configuration that triggers the warning
      end
    end

    [{MyExample, [{message, _location} | _]}] = warnings

See `dsl_errors/1` for the symmetric error-collection helper, and
`Spark.Dsl.Verifier` for the verifier callback contract — including
the shape of `{:warn, _}` returns.

# `refute_dsl_errors`
*macro* 

Asserts that the given do-block produces no verifier errors.

Returns `:ok` on success. If any `Spark.Error.DslError` values are
collected, the test fails with a message that includes the inspected
`[{module, [errors]}]` list, so the offending modules and their errors
are visible in the failure output.

Use this for happy-path tests where you want to assert that a DSL
definition compiles cleanly through all verifiers.

## Example

    test "valid configuration is accepted" do
      refute_dsl_errors do
        defmodule Elixir.MyExample do
          use MyApp.Validator
          # ... valid configuration
        end
      end
    end

See `dsl_errors/1` if you need access to the full collected list (e.g.
when asserting on multiple distinct errors at once), and
`refute_dsl_warnings/1` for the warning-side counterpart.

# `refute_dsl_warnings`
*macro* 

Asserts that the given do-block produces no verifier warnings.

Returns `:ok` on success. If any warning payloads are collected, the test
fails with a message that includes the inspected
`[{module, [{message, location | nil}, ...]}]` list, so the offending
modules and their warnings are visible in the failure output.

Use this for happy-path tests where you want to assert that a DSL
definition compiles cleanly through all warning-emitting verifiers.

## Example

    test "no deprecated options used" do
      refute_dsl_warnings do
        defmodule Elixir.MyExample do
          use MyApp.Validator
          # ... configuration that uses no deprecated options
        end
      end
    end

See `dsl_warnings/1` if you need access to the full collected list,
and `refute_dsl_errors/1` for the error-side counterpart.

---

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