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

OTP-compliant testing utilities for GenServer, Supervisor, and process management.

This module provides helpers that replace timing-based synchronization (Process.sleep)
with proper OTP synchronization patterns, enabling reliable async testing.

## Key Features

- Unique process naming to prevent conflicts
- OTP-compliant synchronization without Process.sleep
- Automatic resource cleanup
- Process lifecycle management
- Supervision tree testing utilities

## Usage

    import Supertester.OTPHelpers

    test "my genserver test" do
      {:ok, server} = setup_isolated_genserver(MyGenServer, "test_context")
      assert_genserver_responsive(server)
    end

# `cleanup_on_exit`

```elixir
@spec cleanup_on_exit((-&gt; any())) :: :ok
```

Registers a cleanup function to run on test exit.

## Parameters

- `cleanup_fun` - Function to call on test exit

## Example

    cleanup_on_exit(fn -> GenServer.stop(my_server) end)

# `cleanup_processes`

```elixir
@spec cleanup_processes([pid()]) :: :ok
```

Cleans up a list of processes safely.

## Parameters

- `pids` - List of PIDs to clean up

## Example

    cleanup_processes([pid1, pid2, pid3])

# `monitor_process_lifecycle`

```elixir
@spec monitor_process_lifecycle(pid()) :: {reference(), pid()}
```

Monitors a process lifecycle and returns monitoring information.

## Parameters

- `pid` - The process to monitor

## Returns

`{monitor_ref, pid}`

## Example

    {ref, pid} = monitor_process_lifecycle(server_pid)

# `setup_isolated_genserver`

```elixir
@spec setup_isolated_genserver(module(), String.t(), keyword()) ::
  {:ok, pid()} | {:error, term()}
```

Sets up an isolated GenServer with unique naming and automatic cleanup.

## Parameters

- `module` - The GenServer module to start
- `test_name` - Test context for unique naming (optional)
- `opts` - Options passed to GenServer.start_link (optional)

## Returns

`{:ok, server_pid}` or `{:error, reason}`

## Example

    {:ok, server} = setup_isolated_genserver(MyGenServer, "my_test", [initial_state: %{}])

# `setup_isolated_supervisor`

```elixir
@spec setup_isolated_supervisor(module(), String.t(), keyword()) ::
  {:ok, pid()} | {:error, term()}
```

Sets up an isolated Supervisor with unique naming and automatic cleanup.

## Parameters

- `module` - The Supervisor module to start
- `test_name` - Test context for unique naming (optional)
- `opts` - Options passed to Supervisor.start_link (optional)

## Returns

`{:ok, supervisor_pid}` or `{:error, reason}`

## Example

    {:ok, supervisor} = setup_isolated_supervisor(MySupervisor, "my_test")

# `wait_for_genserver_sync`

```elixir
@spec wait_for_genserver_sync(GenServer.server(), timeout()) :: :ok | {:error, term()}
```

Waits for a GenServer to be synchronized and responsive.

Uses GenServer.call with a sync message instead of Process.sleep.

## Parameters

- `server` - The GenServer pid or name
- `timeout` - Timeout in milliseconds (default: 1000)

## Returns

`:ok` if server is responsive, `{:error, reason}` otherwise

## Example

    wait_for_genserver_sync(server, 5000)

# `wait_for_process_death`

```elixir
@spec wait_for_process_death(pid(), timeout()) :: {:ok, term()} | {:error, :timeout}
```

Waits for a process to terminate.

## Parameters

- `pid` - The process to wait for
- `timeout` - Timeout in milliseconds (default: 1000)

## Returns

`{:ok, reason}` if process died, `{:error, :timeout}` if timeout

## Example

    ref = Process.monitor(pid)
    Process.exit(pid, :kill)
    {:ok, :killed} = wait_for_process_death(pid)

# `wait_for_process_restart`

```elixir
@spec wait_for_process_restart(atom(), pid(), timeout()) ::
  {:ok, pid()} | {:error, term()}
```

Waits for a process to restart after termination.

## Parameters

- `process_name` - The registered name of the process
- `original_pid` - The PID before restart
- `timeout` - Timeout in milliseconds (default: 1000)

## Returns

`{:ok, new_pid}` if process restarted, `{:error, reason}` otherwise

## Example

    original_pid = GenServer.whereis(MyServer)
    GenServer.stop(MyServer)
    {:ok, new_pid} = wait_for_process_restart(MyServer, original_pid)

# `wait_for_supervisor_restart`

```elixir
@spec wait_for_supervisor_restart(Supervisor.supervisor(), timeout()) ::
  {:ok, pid()} | {:error, term()}
```

Waits for a supervisor to finish starting all its children.

## Parameters

- `supervisor` - The supervisor pid or name
- `timeout` - Timeout in milliseconds (default: 1000)

## Returns

`:ok` if supervisor is ready, `{:error, reason}` otherwise

## Example

    {:ok, supervisor} = setup_isolated_supervisor(MySupervisor)
    wait_for_supervisor_restart(supervisor)

---

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