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

Specialized helpers for GenServer testing patterns.

This module provides utilities specifically designed for testing GenServer behavior,
including state management, concurrent testing, and error scenarios.

## Key Features

- Safe GenServer state access
- Cast and sync patterns for proper async operation testing
- Concurrent testing utilities
- Error testing and crash recovery
- Timeout-aware GenServer operations

## Usage

    import Supertester.GenServerHelpers

    test "genserver state management" do
      {:ok, server} = setup_isolated_genserver(MyGenServer)
      state = get_server_state_safely(server)
      assert state.counter == 0
    end

# `call_with_timeout`

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

Makes a GenServer call with timeout handling.

## Parameters

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

## Returns

`{:ok, response}` if successful, `{:error, reason}` otherwise

## Example

    {:ok, response} = call_with_timeout(server, :get_counter, 5000)

# `cast_and_sync`

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

Sends a cast and then synchronizes with a sync message.

This is crucial for testing async operations - it ensures the cast has been
processed before the test continues.

## Parameters

- `server` - The GenServer pid or name
- `cast_message` - The async message to cast
- `sync_message` - The sync message for synchronization (default: :__supertester_sync__)

## Returns

- `:ok` when the sync handler replies `:ok` (the default `TestableGenServer` behavior).
- `{:ok, reply}` when the sync handler replies with any other value.
- `{:error, :missing_sync_handler}` in non-strict mode when the sync handler is missing.
- Raises `ArgumentError` in strict mode when the sync handler is missing.

## Example

    :ok = cast_and_sync(server, {:increment, 5})
    {:ok, state} = get_server_state_safely(server)
    assert state.counter == 5

# `concurrent_calls`

```elixir
@spec concurrent_calls(GenServer.server(), [term()], pos_integer(), keyword()) ::
  {:ok, [map()]}
```

Performs concurrent calls to a GenServer for stress testing.

## Parameters

- `server` - The GenServer pid or name
- `calls` - List of messages to send concurrently
- `count` - Number of concurrent processes per message (default: 10)

## Returns

`{:ok, results}` with list of {call, result} tuples

## Example

    calls = [:get_counter, {:increment, 1}, :get_counter]
    {:ok, results} = concurrent_calls(server, calls, 5)

# `get_server_state_safely`

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

Safely retrieves the internal state of a GenServer.

This function attempts to get the GenServer state without causing crashes
if the server is not responsive or doesn't support state inspection.

## Parameters

- `server` - The GenServer pid or name

## Returns

`{:ok, state}` if successful, `{:error, reason}` otherwise

## Example

    {:ok, state} = get_server_state_safely(my_server)

# `stress_test_server`

```elixir
@spec stress_test_server(GenServer.server(), [term()], pos_integer(), keyword()) ::
  {:ok,
   %{
     calls: non_neg_integer(),
     casts: non_neg_integer(),
     errors: non_neg_integer(),
     duration_ms: non_neg_integer()
   }}
```

Performs stress testing on a GenServer with various operations.

## Parameters

- `server` - The GenServer pid or name
- `operations` - List of operations to perform randomly
- `duration` - Duration in milliseconds (default: 5000)

## Returns

`{:ok, stats}` with stress test statistics

## Example

    operations = [
      {:call, :get_counter},
      {:cast, {:increment, 1}},
      {:call, {:add, 5}}
    ]
    {:ok, stats} = stress_test_server(server, operations, 10_000)

# `test_invalid_messages`

```elixir
@spec test_invalid_messages(GenServer.server(), [term()]) :: {:ok, [{term(), term()}]}
```

Tests how a GenServer handles invalid messages.

## Parameters

- `server` - The GenServer pid or name
- `invalid_messages` - List of invalid messages to test

## Returns

`{:ok, results}` with test results for each message

## Example

    invalid_messages = [:invalid_call, {:unknown, :message}, "string_message"]
    {:ok, results} = test_invalid_messages(server, invalid_messages)

# `test_server_crash_recovery`

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

Tests GenServer crash recovery behavior.

## Parameters

- `server` - The GenServer pid or name
- `crash_reason` - The reason to crash the server with

## Returns

`{:ok, recovery_info}` if recovery successful, `{:error, reason}` otherwise

## Example

    {:ok, info} = test_server_crash_recovery(server, :test_crash)
    assert info.recovered == true

---

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