# `Supertester.ConcurrentHarness`
[🔗](https://github.com/nshkrdotcom/supertester/blob/v0.6.0/lib/supertester/concurrent_harness.ex#L1)

Scenario-based concurrency harness for OTP processes.

This module coordinates multi-threaded test scenarios against a target process
(typically a GenServer) while keeping isolation, cleanup, chaos injection,
performance validation, telemetry, and invariant checks in one place.

High level usage:

    scenario =
      Supertester.ConcurrentHarness.simple_genserver_scenario(
        MyServer,
        [:increment, :decrement],
        5,
        chaos: Supertester.ConcurrentHarness.chaos_inject_crash(),
        performance_expectations: [max_time_ms: 100],
        invariant: fn server, _ctx ->
          assert {:ok, state} = Supertester.GenServerHelpers.get_server_state_safely(server)
          assert state.count >= 0
        end
      )

    assert {:ok, report} = Supertester.ConcurrentHarness.run(scenario)

The returned report contains thread events, chaos + mailbox + performance
diagnostics, and emits telemetry for start/stop/chaos/mailbox/perf events.

# `chaos_fun`

```elixir
@type chaos_fun() :: (pid(), map() -&gt; any())
```

# `operation`

```elixir
@type operation() :: {:call, term()} | {:cast, term()} | {:custom, (pid() -&gt; any())}
```

# `scenario`

```elixir
@type scenario() :: Supertester.ConcurrentHarness.Scenario.t() | map() | keyword()
```

# `thread_script`

```elixir
@type thread_script() :: [operation()]
```

# `chaos_inject_crash`

```elixir
@spec chaos_inject_crash(
  Supertester.ChaosHelpers.crash_spec(),
  keyword()
) :: chaos_fun()
```

Chaos hook helper that injects a crash into the primary subject.

# `chaos_kill_children`

```elixir
@spec chaos_kill_children(keyword()) :: chaos_fun()
```

Chaos hook helper that kills supervisor children while the scenario runs.

# `default_invariant`

```elixir
@spec default_invariant(pid(), map()) :: :ok
```

Default invariant that simply returns :ok.

# `from_property_config`

```elixir
@spec from_property_config(module(), map(), keyword()) ::
  Supertester.ConcurrentHarness.Scenario.t()
```

Converts a property helper configuration into a runnable scenario.

# `run`

```elixir
@spec run(scenario()) :: {:ok, map()} | {:error, term()}
```

Runs a concurrent scenario and returns a diagnostic report.

# `run_with_performance`

```elixir
@spec run_with_performance(
  scenario(),
  keyword()
) :: {:ok, map()} | {:error, term()}
```

Runs a scenario while enforcing additional performance bounds externally.

# `simple_genserver_scenario`

```elixir
@spec simple_genserver_scenario(
  module(),
  [term() | operation()],
  pos_integer(),
  keyword()
) ::
  Supertester.ConcurrentHarness.Scenario.t()
```

Builds a scenario for a GenServer module with repeated operation scripts.

## Options

* `:server_opts` - options passed to `start_link/1`
* `:default_operation` - `:call` or `:cast` for bare terms (default: `:call`)
* `:call_timeout_ms` - timeout for `GenServer.call/3` (default: 1_000)
* `:timeout_ms` - overall scenario timeout (default: 5_000)
* `:invariant` - function of `fn pid, ctx -> term end`
* `:cleanup` - custom cleanup callback
* `:metadata` - map merged into report metadata
* `:mailbox` - keyword list passed to mailbox monitoring
* `:chaos` - function `(pid, ctx) -> any` injected while threads execute
* `:performance_expectations` - keyword list forwarded to `assert_expectations/2`

---

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