# `PaperTiger.Test`
[🔗](https://github.com/EnaiaInc/paper_tiger/blob/v1.2.1/lib/paper_tiger/test.ex#L1)

Test helpers for running PaperTiger tests concurrently.

Provides a sandbox mechanism similar to Ecto.Adapters.SQL.Sandbox that
isolates test data by namespace, allowing tests to run with `async: true`.

## Usage

    defmodule MyApp.StripeTest do
      use ExUnit.Case, async: true

      setup :checkout_paper_tiger

      test "creates a customer" do
        # Data is isolated to this test process
        {:ok, customer} = PaperTiger.TestClient.create_customer(%{...})
      end
    end

## How It Works

When `checkout_paper_tiger/1` is called:

1. Stores the test process PID as a namespace in the process dictionary
2. All subsequent PaperTiger operations scope data to that namespace
3. On test exit, only that namespace's data is cleaned up

This allows multiple tests to run concurrently without interfering
with each other's data.

# `assert_request`

```elixir
@spec assert_request(String.t() | atom(), String.t(), map() | keyword() | nil) :: [
  map()
]
```

Asserts a matching request exists.

This helper supports partial matching for body/params.

# `assert_webhook_delivered`

```elixir
@spec assert_webhook_delivered(String.t()) :: [map()]
```

Asserts that a webhook was delivered with the given event type.

This is a convenience helper that combines getting deliveries and asserting.
Returns the matching deliveries for further assertions.

## Example

    test "customer creation triggers webhook" do
      {:ok, customer} = Stripe.Customer.create(%{email: "test@example.com"})

      [delivery] = PaperTiger.Test.assert_webhook_delivered("customer.created")
      assert delivery.event_data.object.email == "test@example.com"
    end

# `auth_headers`

```elixir
@spec auth_headers(keyword()) :: [{String.t(), String.t()}]
```

Returns HTTP headers for authenticated sandbox requests.

Combines authorization header with sandbox namespace headers.
Use this helper for most HTTP requests to PaperTiger.

## Options

- `:api_key` - Override the default API key (default: "sk_test_mock")

## Example

    Req.post(base_url("/v1/customers"),
      form: [email: "test@example.com"],
      headers: auth_headers()
    )

    # With custom API key
    Req.get(url, headers: auth_headers(api_key: "sk_test_custom"))

# `base_url`

```elixir
@spec base_url(String.t()) :: String.t()
```

Returns the base URL for PaperTiger HTTP requests.

Uses the configured port from application config.

## Example

    iex> PaperTiger.Test.base_url()
    "http://localhost:4001"

    iex> PaperTiger.Test.base_url("/v1/customers")
    "http://localhost:4001/v1/customers"

# `checkout_paper_tiger`

```elixir
@spec checkout_paper_tiger(map()) :: :ok
```

Checks out a PaperTiger sandbox for the current test.

Use as a setup callback:

    setup :checkout_paper_tiger

Or call directly in setup block:

    setup do
      PaperTiger.Test.checkout_paper_tiger(%{})
      :ok
    end

## Child Process Support

This function also sets a shared namespace via Application env, which
allows child processes (like Phoenix LiveView) to use the same sandbox.
This is essential for integration tests where Stripe calls happen in
spawned processes.

Returns `:ok` for use with ExUnit's setup callbacks.

# `cleanup_namespace`

```elixir
@spec cleanup_namespace(pid() | :global) :: :ok
```

Cleans up all data for the given namespace.

Called automatically on test exit when using `checkout_paper_tiger/1`.

# `clear_delivered_webhooks`

```elixir
@spec clear_delivered_webhooks() :: :ok
```

Clears all collected webhook deliveries for the current namespace.

Useful when testing multiple operations and wanting to verify
webhooks from a specific action.

## Example

    test "verifies webhooks for second operation only" do
      {:ok, _} = Stripe.Customer.create(%{email: "first@example.com"})
      PaperTiger.Test.clear_delivered_webhooks()

      {:ok, _} = Stripe.Customer.create(%{email: "second@example.com"})

      # Only sees the second customer's webhook
      assert [%{event_type: "customer.created"}] = PaperTiger.Test.get_delivered_webhooks()
    end

# `clear_requests`

```elixir
@spec clear_requests() :: :ok
```

Clears captured inbound requests for the current namespace.

# `current_namespace`

```elixir
@spec current_namespace() :: pid() | :global
```

Returns the current namespace, or `:global` if not in a sandboxed test.

# `enable_webhook_collection`

```elixir
@spec enable_webhook_collection() :: :ok
```

Enables webhook collection mode for the current test.

Call this in your test setup to capture webhooks instead of delivering them.
Automatically restores the previous mode on test exit.

## Example

    setup do
      :ok = checkout_paper_tiger(%{})
      :ok = enable_webhook_collection()
      :ok
    end

    test "creates customer and triggers webhook" do
      {:ok, _customer} = Stripe.Customer.create(%{email: "test@example.com"})
      [delivery] = PaperTiger.Test.assert_webhook_delivered("customer.created")
      assert delivery.event_data.object.email == "test@example.com"
    end

# `get_delivered_webhooks`

```elixir
@spec get_delivered_webhooks() :: [map()]
```

Gets all webhook deliveries collected during the test.

Only works when `webhook_mode: :collect` is configured.

Returns a list of delivery records sorted by creation time (oldest first).

## Example

    setup do
      Application.put_env(:paper_tiger, :webhook_mode, :collect)
      on_exit(fn -> Application.delete_env(:paper_tiger, :webhook_mode) end)
      :ok
    end

    test "creates customer and triggers webhook" do
      {:ok, _customer} = Stripe.Customer.create(%{email: "test@example.com"})

      deliveries = PaperTiger.Test.get_delivered_webhooks()
      assert [%{event_type: "customer.created"}] = deliveries
    end

# `get_delivered_webhooks`

```elixir
@spec get_delivered_webhooks(String.t()) :: [map()]
```

Gets webhook deliveries filtered by event type.

Supports wildcard patterns like "customer.*" or "invoice.payment_*".

## Examples

    # Get all customer.created events
    get_delivered_webhooks("customer.created")

    # Get all customer events
    get_delivered_webhooks("customer.*")

    # Get all invoice payment events
    get_delivered_webhooks("invoice.payment_*")

# `refute_request`

```elixir
@spec refute_request(String.t() | atom(), String.t(), map() | keyword() | nil) :: :ok
```

Asserts no request matching the given method/path/params exists.

# `refute_webhook_delivered`

```elixir
@spec refute_webhook_delivered(String.t()) :: :ok
```

Asserts that no webhook was delivered with the given event type.

## Example

    test "soft delete doesn't trigger delete webhook" do
      {:ok, customer} = Stripe.Customer.create(%{email: "test@example.com"})
      PaperTiger.Test.clear_delivered_webhooks()

      soft_delete_customer(customer)

      PaperTiger.Test.refute_webhook_delivered("customer.deleted")
    end

# `requests`

```elixir
@spec requests() :: [map()]
```

Returns all captured inbound requests for the current namespace.

Requests are recorded after routing/parsing and before the response is sent,
and include method/path/params and normalized headers.

# `requests`

```elixir
@spec requests(keyword()) :: [map()]
```

Returns captured requests filtered by method/path/idempotency and params.

# `sandbox_headers`

```elixir
@spec sandbox_headers() :: [{String.t(), String.t()}]
```

Returns HTTP headers needed for sandbox isolation.

Include these headers in HTTP requests to PaperTiger to ensure
data is scoped to the current test's namespace.

## Example

    Req.post(url, headers: PaperTiger.Test.sandbox_headers())

    # Or merge with other headers:
    Req.get(url,
      headers: [{"authorization", "Bearer sk_test_mock"}] ++ PaperTiger.Test.sandbox_headers()
    )

# `signed_webhook_request`

```elixir
@spec signed_webhook_request(map() | PaperTiger.WebhookDelivery.Request.t()) :: %{
  body: String.t(),
  headers: [{String.t(), String.t()}],
  signature_header: String.t()
}
```

Returns the raw webhook request fields from a collected delivery or adapter
request.

Use this when testing a webhook controller that verifies the raw body and
`Stripe-Signature` header. The returned `:body` is the exact JSON byte
string that PaperTiger signed.

## Example

    [delivery] = assert_webhook_delivered("customer.created")
    signed = signed_webhook_request(delivery)

    {:ok, event} =
      Stripe.Webhook.construct_event(
        signed.body,
        signed.signature_header,
        Application.fetch_env!(:stripity_stripe, :webhook_signing_key),
        response_as: :map
      )

---

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