NAME

PAGI::Test::Client - Test client for PAGI applications

SYNOPSIS

use PAGI::Test::Client;

my $client = PAGI::Test::Client->new(app => $app);

# Simple GET
my $res = $client->get('/');
is $res->status, 200;
is $res->text, 'Hello World';

# GET with query parameters
my $res = $client->get('/search', query => { q => 'perl' });

# POST with JSON body
my $res = $client->post('/api/users', json => { name => 'John' });

# POST with form data
my $res = $client->post('/login', form => { user => 'admin' });

# Custom headers
my $res = $client->get('/api', headers => { Authorization => 'Bearer xyz' });

# Session cookies persist across requests
$client->post('/login', form => { user => 'admin', pass => 'secret' });
my $res = $client->get('/dashboard');  # authenticated!

DESCRIPTION

PAGI::Test::Client allows you to test PAGI applications without starting a real server. It invokes your app directly by constructing the PAGI protocol messages ($scope, $receive, $send), making tests fast and simple.

This is inspired by Starlette's TestClient but adapted for Perl and PAGI's specific features like first-class SSE support.

CONSTRUCTOR

new

my $client = PAGI::Test::Client->new(
    app      => $app,           # Required: PAGI app coderef
    headers  => { ... },        # Optional: default headers
    lifespan => 1,              # Optional: enable lifespan (default: 0)
);

Options

app (required)

The PAGI application coderef to test.

headers

Default headers to include in every request. Request-specific headers override these.

lifespan

If true, the client will send lifespan.startup when started and lifespan.shutdown when stopped. Default is false (most tests don't need it).

HTTP METHODS

All HTTP methods return a PAGI::Test::Response object.

get

my $res = $client->get($path, %options);

post

my $res = $client->post($path, %options);

put

my $res = $client->put($path, %options);

patch

my $res = $client->patch($path, %options);

delete

my $res = $client->delete($path, %options);
my $res = $client->head($path, %options);

options

my $res = $client->options($path, %options);

Request Options

headers => { ... }

Additional headers for this request.

query => { ... }

Query string parameters. Appended to the path.

json => { ... }

JSON request body. Automatically sets Content-Type to application/json.

form => { ... }

Form-encoded request body. Sets Content-Type to application/x-www-form-urlencoded.

body => $bytes

Raw request body bytes.

SESSION METHODS

cookies

my $hashref = $client->cookies;

Returns all current session cookies.

my $value = $client->cookie('session_id');

Returns a specific cookie value.

$client->set_cookie('theme', 'dark');

Manually sets a cookie.

clear_cookies

$client->clear_cookies;

Clears all session cookies.

WEBSOCKET

websocket

# Callback style (auto-close)
$client->websocket('/ws', sub {
    my ($ws) = @_;
    $ws->send_text('hello');
    is $ws->receive_text, 'echo: hello';
});

# Explicit style
my $ws = $client->websocket('/ws');
$ws->send_text('hello');
is $ws->receive_text, 'echo: hello';
$ws->close;

See PAGI::Test::WebSocket for the WebSocket connection API.

SSE (Server-Sent Events)

sse

# Callback style (auto-close)
$client->sse('/events', sub {
    my ($sse) = @_;
    my $event = $sse->receive_event;
    is $event->{data}, 'connected';
});

# Explicit style
my $sse = $client->sse('/events');
my $event = $sse->receive_event;
$sse->close;

See PAGI::Test::SSE for the SSE connection API.

LIFESPAN

start

$client->start;

Triggers lifespan.startup. Only needed if lifespan = 1> was passed to the constructor.

stop

$client->stop;

Triggers lifespan.shutdown.

state

my $state = $client->state;

Returns the shared state hashref from lifespan.

run

PAGI::Test::Client->run($app, sub {
    my ($client) = @_;
    # ... tests ...
});

Class method that creates a client with lifespan enabled, calls start, runs your callback, then calls stop. Exceptions propagate.

SEE ALSO

PAGI::Test::Response, PAGI::Test::WebSocket, PAGI::Test::SSE