# `ArchTest.Assertions`
[🔗](https://github.com/yoavgeva/arch_test/blob/v0.2.0/lib/arch_test/assertions.ex#L1)

Core assertion functions for architecture rules.

All assertion functions accept a `ModuleSet` as their first argument
(supporting pipe-based DSL usage) and evaluate against the current
dependency graph from `ArchTest.Collector`.

Violations cause an `ExUnit.AssertionError` with a detailed message
listing all offending dependencies, grouped by module and annotated
with the patterns that were checked.

# `assert_no_violations_public`

# `assert_no_violations_public`

# `assert_no_violations_public`

# `satisfying`

```elixir
@spec satisfying(
  ArchTest.ModuleSet.t(),
  (ArchTest.Collector.graph(), module() -&gt; [ArchTest.Violation.t()]),
  keyword()
) :: :ok
```

Applies a custom check function to all modules in `subject`.

The function receives `(graph, module)` and must return a list of
`ArchTest.Violation` structs (empty list = no violation).

## Example

    modules_matching("MyApp.**")
    |> satisfying(fn graph, mod ->
      # custom check logic
      []
    end)

# `should_be_free_of_cycles`

```elixir
@spec should_be_free_of_cycles(
  ArchTest.ModuleSet.t(),
  keyword()
) :: :ok
```

Asserts that there are no circular dependencies among modules in `subject`.

## Example

    modules_matching("MyApp.Orders.**") |> should_be_free_of_cycles()

# `should_export`

```elixir
@spec should_export(ArchTest.ModuleSet.t(), atom(), non_neg_integer(), keyword()) ::
  :ok
```

Asserts all modules in `subject` export the given function.

## Example

    modules_matching("**.*Handler")
    |> should_export(:handle, 2)

    modules_matching("MyApp.**.*")
    |> should_export(:child_spec, 1)

# `should_have_attribute`

```elixir
@spec should_have_attribute(ArchTest.ModuleSet.t(), atom(), keyword()) :: :ok
```

Asserts all modules in `subject` have the given module attribute.

Checks `mod.__info__(:attributes)` for the presence of the key.

## Example

    # All plug modules must declare their behaviour
    modules_matching("MyApp.Plugs.**")
    |> should_have_attribute(:behaviour)

# `should_have_attribute_value`

```elixir
@spec should_have_attribute_value(ArchTest.ModuleSet.t(), atom(), term(), keyword()) ::
  :ok
```

Asserts all modules in `subject` have the given attribute with the given value.

## Example

    modules_matching("**.*Schema")
    |> should_have_attribute_value(:behaviour, [Ecto.Schema])

    modules_matching("MyApp.**")
    |> should_have_attribute_value(:moduledoc, false)
    # (fails for modules with @moduledoc false -- use should_not_have_attribute_value instead)

# `should_have_module_count`

```elixir
@spec should_have_module_count(
  ArchTest.ModuleSet.t(),
  keyword()
) :: :ok
```

Asserts that the number of modules matching `subject` satisfies the given constraints.

Supported constraint keys: `:exactly`, `:at_least`, `:at_most`, `:less_than`, `:more_than`.
Multiple constraints can be combined.

Useful to enforce complexity budgets on bounded contexts, or to ensure
a pattern is not accidentally empty (which would make rules trivially pass).

## Options

In addition to constraint keys, the following options are supported:

- `:graph` — a pre-built dependency graph map (useful for testing)
- `:app` — OTP app atom for graph resolution (default: `:all`)
- `:message` — custom hint appended to the error message on failure

## Examples

    # Context must not grow beyond 20 modules
    modules_matching("MyApp.Orders.**")
    |> should_have_module_count(less_than: 20)

    # Pattern must match at least 1 module (catches typos)
    modules_matching("MyApp.Orders.**")
    |> should_have_module_count(at_least: 1)

    # Range constraint
    modules_matching("MyApp.Orders.**")
    |> should_have_module_count(at_least: 2, at_most: 15)

# `should_have_name_matching`

```elixir
@spec should_have_name_matching(ArchTest.ModuleSet.t(), String.t(), keyword()) :: :ok
```

Asserts that all modules in `subject` have names matching `name_pattern`.

## Example

    modules_matching("MyApp.Repo.**") |> should_have_name_matching("**.*Repo")

# `should_have_public_functions_matching`

```elixir
@spec should_have_public_functions_matching(
  ArchTest.ModuleSet.t(),
  String.t(),
  keyword()
) :: :ok
```

Asserts all modules in `subject` have at least one public function whose name
matches the given glob pattern.

Pattern is matched against the function name only (not arity).

## Example

    modules_matching("**.*Repo")
    |> should_have_public_functions_matching("get*")

# `should_implement_behaviour`

```elixir
@spec should_implement_behaviour(ArchTest.ModuleSet.t(), module(), keyword()) :: :ok
```

Asserts that all modules in `subject` implement the given behaviour.

A module implements a behaviour if it declares `@behaviour BehaviourModule`,
which appears in `mod.__info__(:attributes)` as `behaviour: [BehaviourModule]`.

## Example

    modules_matching("**.*Handler")
    |> should_implement_behaviour(MyApp.Handler)

    modules_matching("**.*Server")
    |> should_implement_behaviour(GenServer)

# `should_implement_protocol`

```elixir
@spec should_implement_protocol(ArchTest.ModuleSet.t(), module(), keyword()) :: :ok
```

Asserts that all modules in `subject` implement the given protocol.

A module `Mod` implements protocol `P` if the module `P.Mod` is loadable
(i.e. a `defimpl P, for: Mod` block exists somewhere in the codebase).

## Example

    modules_matching("**.*Entity")
    |> should_implement_protocol(String.Chars)

# `should_not_be_called_by`

```elixir
@spec should_not_be_called_by(
  ArchTest.ModuleSet.t(),
  ArchTest.ModuleSet.t(),
  keyword()
) :: :ok
```

Asserts that no module in `object` is called by any module in `callers`.

This is the reverse direction of `should_not_depend_on`.

## Example

    modules_matching("MyApp.Repo.*")
    |> should_not_be_called_by(modules_matching("MyApp.Web.*"))

# `should_not_depend_on`

```elixir
@spec should_not_depend_on(ArchTest.ModuleSet.t(), ArchTest.ModuleSet.t(), keyword()) ::
  :ok
```

Asserts that no module in `subject` directly depends on any module in `object`.

## Example

    modules_matching("**.*Controller")
    |> should_not_depend_on(modules_matching("**.*Repo"))

# `should_not_exist`

```elixir
@spec should_not_exist(
  ArchTest.ModuleSet.t(),
  keyword()
) :: :ok
```

Asserts that none of the modules matched by `subject` exist in the codebase.

Used to ban naming conventions (e.g., no `*Manager` modules).

## Example

    modules_matching("**.*Manager") |> should_not_exist()

# `should_not_export`

```elixir
@spec should_not_export(ArchTest.ModuleSet.t(), atom(), non_neg_integer(), keyword()) ::
  :ok
```

Asserts no module in `subject` exports the given function.

## Example

    modules_matching("MyApp.Domain.**")
    |> should_not_export(:__struct__, 0)

    modules_matching("**.*Controller")
    |> should_not_export(:handle_info, 2)

# `should_not_have_attribute`

```elixir
@spec should_not_have_attribute(ArchTest.ModuleSet.t(), atom(), keyword()) :: :ok
```

Asserts all modules in `subject` do NOT have the given module attribute.

A violation is raised for every module that DOES have the attribute key.

## Example

    modules_matching("MyApp.Web.**")
    |> should_not_have_attribute(:deprecated)

# `should_not_have_attribute_value`

```elixir
@spec should_not_have_attribute_value(
  ArchTest.ModuleSet.t(),
  atom(),
  term(),
  keyword()
) :: :ok
```

Asserts all modules in `subject` do NOT have the given attribute with the given value.

A violation is raised for every module whose attribute matches the forbidden value exactly.

## Example

    modules_matching("MyApp.**")
    |> should_not_have_attribute_value(:moduledoc, false)

# `should_not_have_public_functions_matching`

```elixir
@spec should_not_have_public_functions_matching(
  ArchTest.ModuleSet.t(),
  String.t(),
  keyword()
) :: :ok
```

Asserts no module in `subject` has public functions whose names match the pattern.

Pattern is matched against the function name only.

## Example

    modules_matching("MyApp.Domain.**")
    |> should_not_have_public_functions_matching("_*")

# `should_not_implement_behaviour`

```elixir
@spec should_not_implement_behaviour(ArchTest.ModuleSet.t(), module(), keyword()) ::
  :ok
```

Asserts that no module in `subject` implements the given behaviour.

This is the inverse of `should_implement_behaviour/3` — a violation is
raised for every module that DOES implement the behaviour.

## Example

    modules_matching("**.*Worker")
    |> should_not_implement_behaviour(GenServer)

# `should_not_implement_protocol`

```elixir
@spec should_not_implement_protocol(ArchTest.ModuleSet.t(), module(), keyword()) ::
  :ok
```

Asserts that no module in `subject` implements the given protocol.

This is the inverse of `should_implement_protocol/3` — a violation is
raised for every module that DOES implement the protocol.

## Example

    modules_matching("**.*Internal")
    |> should_not_implement_protocol(Jason.Encoder)

# `should_not_transitively_depend_on`

```elixir
@spec should_not_transitively_depend_on(
  ArchTest.ModuleSet.t(),
  ArchTest.ModuleSet.t(),
  keyword()
) ::
  :ok
```

Asserts that none of the modules in `subject` transitively depend on
any module in `object`.

## Example

    modules_matching("MyApp.Orders.*")
    |> should_not_transitively_depend_on(modules_matching("MyApp.Billing.*"))

# `should_not_use`

```elixir
@spec should_not_use(ArchTest.ModuleSet.t(), module(), keyword()) :: :ok
```

Asserts no module in `subject` uses the given module (via `use ModuleName`).

This is the inverse of `should_use/3` -- a violation is raised for every
module that DOES appear to use the given module.

## Example

    modules_matching("MyApp.Web.**")
    |> should_not_use(GenServer)

# `should_only_be_called_by`

```elixir
@spec should_only_be_called_by(
  ArchTest.ModuleSet.t(),
  ArchTest.ModuleSet.t(),
  keyword()
) :: :ok
```

Asserts that only modules in `allowed_callers` may call modules in `object`.

Any module outside `allowed_callers` that calls a module in `object` is a violation.
This is the whitelist form of `should_not_be_called_by/3`.

## Example

    # Only Services and Controllers may call Repo modules
    modules_matching("**.*Repo")
    |> should_only_be_called_by(
         modules_matching("**.*Service")
         |> union(modules_matching("**.*Controller"))
       )

    # The Accounts module may only be called by the Orders context
    modules_matching("MyApp.Accounts")
    |> should_only_be_called_by(modules_matching("MyApp.Orders.**"),
         message: "Use the Orders public API to access Accounts — see ADR-012")

# `should_only_depend_on`

```elixir
@spec should_only_depend_on(ArchTest.ModuleSet.t(), ArchTest.ModuleSet.t(), keyword()) ::
  :ok
```

Asserts that every module in `subject` only depends on modules in `allowed`.

Any dependency outside `allowed` is a violation.

## Example

    modules_matching("**.*Controller")
    |> should_only_depend_on(
         modules_matching("**.*Service")
         |> union(modules_matching("**.*View"))
       )

# `should_reside_under`

```elixir
@spec should_reside_under(ArchTest.ModuleSet.t(), String.t(), keyword()) :: :ok
```

Asserts that all modules in `subject` reside under the given namespace pattern.

## Example

    modules_matching("**.*Schema") |> should_reside_under("MyApp.**.Schemas")

# `should_use`

```elixir
@spec should_use(ArchTest.ModuleSet.t(), module(), keyword()) :: :ok
```

Asserts all modules in `subject` use the given module (via `use ModuleName`).

This is detected heuristically: when a module does `use Foo`, Elixir typically
adds `:behaviour` or other attributes. This check looks for `module` appearing
anywhere in the module's flattened attribute values.

For reliable results with framework modules (GenServer, Ecto.Schema etc.),
prefer `should_implement_behaviour/2` instead.

## Example

    modules_matching("**.*Schema")
    |> should_use(Ecto.Schema)

---

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