# Chaperon Tutorial Part 1

## How chaperon works

Basically, all load test scenarios in Chaperon operate on `Chaperon.Session` structs.
Built-in actions, such as for making HTTP and WebSocket requests can be found in the `Chaperon.Action.` module namespace but are accessible via helper functions in the `Chaperon.Session` module.

## API Documentation

To view chaperon's API documentation, run `mix docs` and then open `doc/index.html`.

## How to write load test scenarios

Let's say we want to write a WebSocket load test that connects to a server and sends a message, then awaits a response and we track the duration of all of those ping/pong iterations. We'll write the ping pong logic inside a module that implements the `Chaperon.Scenario` behavior by exposing a `run/1` function. The `init/1` function is optional and can be defined to perform some initial setup logic before running the scenario.

### A WebSocket example scenario: PingPong

```elixir
defmodule Scenario.WS.PingPong do
  use Chaperon.Scenario

  def init(session) do
    # We can add custom session setup logic in an `init/1` function, if we need to.
    # Returns {:ok, session} or {:error, reason} (see `Chaperon.Session` module)

    session
    |> ws_connect("/ping/pong")
    |> ok
  end

  def run(session) do
    # Accessing config values using the `Session.config/2` helper function.
    # Alternatively we could have just accessed `session.config.ping_pong.iterations`
    # but using the helper function as we do here gives us better error messages
    # in case we didn't define the config value for this session.

    iterations = session |> config([:ping_pong, :iterations])

    # This will call `ping_pong/1` repeatedly for `iterations` amount of times
    # and record the duration of calling it in a histogram

    session
    |> repeat_traced(:ping_pong, iterations)
    |> log_info("PingPong finished after #{iterations} iterations")
  end

  def ping_pong(session) do
    session
    |> ws_send("ping")
    |> ws_await_recv("pong") # await until "pong" message is received via WS
  end

  def teardown(session) do
    # We can also define a `teardown/1` function which then gets called with our
    # session after we successfully ran our `run/1` defined above.
    # This is useful for cleaning up resources or performing other logic after
    # we've run our load test scenario.
    # Note that any actions in this code will not be traced and no metrics for
    # them will be recorded in the final metrics histogram output.

    session
    |> ws_close
  end
end
```

### Load test configuration

Once we've defined the Scenario logic above, we define the load test configuration in another module that uses the `Chaperon.LoadTest` module to define everything we need to run the load test.

We provide a default config that is used by all load test scenarios we want to run as part of the load test, which in this case is just the `PingPong` scenario we wrote.

```elixir
defmodule LoadTest.PingPong do
  use Chaperon.LoadTest

  def default_config, do: %{
    base_url: "http://localhost:5000"
  }

  # run 100 PingPong sessions (each with 10 iterations) across the cluster
  # `scenario/0` is expected to return a list of 2-tuples (`{scenario_module, config}`)
  # for each load test.
  # `scenario_module` can be `{concurrency, scenario_module}` if running multiple
  # concurrent instances of the scenario is needed
  def scenarios, do: [
    {{100, Scenario.WS.PingPong}, %{
      ping_pong: %{
        iterations: 10
      }
    }}
  ]
end
```

### Next Steps

Check out the more [in-depth tutorial using more advanced features](Tutorial2.md).

You can also take a look at the `examples/` directory for more example load tests.
