# `Plushie.Test.SessionPool`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/lib/plushie/test/session_pool.ex#L1)

Session manager for concurrent test renderer sessions.

For `:mock` and `:headless`, owns a single renderer Port and multiplexes
multiple logical sessions over it. For `:windowed`, each logical session
gets its own renderer process because the real iced renderer only supports
one live app session per process.

## Usage

Start the pool once (typically in `test_helper.exs` or `setup_all`):

    {:ok, pool} = SessionPool.start_link(mode: :mock, max_sessions: 8)

The Runtime test backend connects to the pool via a PoolAdapter:

    session = Session.start(MyApp, backend: Backend.Runtime, pool: pool)

The pool assigns session IDs, sends messages to the right renderer,
and forwards replies back to the caller.

# `mode`

```elixir
@type mode() :: :mock | :headless | :windowed
```

# `session_id`

```elixir
@type session_id() :: String.t()
```

# `t`

```elixir
@type t() :: %Plushie.Test.SessionPool{
  closing_windowed: %{
    required(session_id()) =&gt;
      {Plushie.Test.SessionPool.Windowed.close_kind(), port()}
  },
  env: [{String.t(), String.t()}],
  format: Plushie.Test.SessionPool.Transport.format(),
  max_sessions: pos_integer(),
  mode: mode(),
  multiplexed: Plushie.Test.SessionPool.Multiplexed.t() | nil,
  next_id: pos_integer(),
  next_session: pos_integer(),
  renderer: String.t(),
  sessions: %{required(session_id()) =&gt; Plushie.Test.SessionPool.Windowed.t()}
}
```

Pool state for the test renderer session manager.

The top-level struct keeps only pool-wide configuration and counters. Mode-
specific runtime state lives in `Multiplexed` or `Windowed` structs.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `register`

```elixir
@spec register(pool :: GenServer.server()) :: session_id()
```

Register a new session. Returns a unique session ID.

# `send_interact`

```elixir
@spec send_interact(
  pool :: GenServer.server(),
  session_id(),
  msg :: map()
) :: String.t()
```

Send an interact message that may produce intermediate steps.

Unlike `send_message/4`, this does NOT block for the response.
Instead, `interact_step` and `interact_response` messages are
forwarded to the session owner via `{:plushie_pool_event, ...}`.
The caller is responsible for handling them in `handle_info`.

Returns the request ID assigned to this interact.

# `send_message`

```elixir
@spec send_message(
  pool :: GenServer.server(),
  session_id(),
  msg :: map(),
  expect_response :: String.t() | nil
) :: {:ok, map()} | {:error, term()} | :ok
```

Send a message to the renderer for the given session.

The `session` field is injected automatically. Synchronous: waits
for the matching response if `expect_response` is a response type
string (e.g. `"query_response"`). For fire-and-forget messages
(snapshot, patch, subscription), pass `nil`.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Start the pool, spawning a renderer process.

## Options

- `:renderer` - path to the plushie binary (required)
- `:mode` - `:mock` (default), `:headless`, or `:windowed`
- `:format` - `:msgpack` (default) or `:json`
- `:max_sessions` - maximum concurrent sessions (default 8)
- `:name` - optional registered name for the pool

# `unregister`

```elixir
@spec unregister(pool :: GenServer.server(), session_id()) :: :ok
```

Unregister a session. Sends Reset to the renderer to free resources.

# `unregister_async`

```elixir
@spec unregister_async(pool :: GenServer.server(), session_id()) :: :ok
```

Unregister a session without waiting for the renderer reset.

The session is removed from the active map immediately. The reset
is still sent to the renderer, but the caller doesn't block on the
response. Used by terminate/2 to avoid blocking on slow renderers.

---

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