PAGI

 view release on metacpan or  search on metacpan

t/request-body-stream.t  view on Meta::CPAN

        { type => 'http.request', body => 'Hello', more => 1 },
        { type => 'http.request', body => ' World', more => 0 },
    );

    my $stream = PAGI::Request::BodyStream->new(receive => $receive);

    my $chunk1 = $stream->next_chunk->get;
    is $chunk1, 'Hello', 'first chunk';
    ok !$stream->is_done, 'not done yet';

    my $chunk2 = $stream->next_chunk->get;
    is $chunk2, ' World', 'second chunk';
    ok $stream->is_done, 'done after last chunk';

    is $stream->bytes_read, 11, 'bytes_read correct';
};

subtest 'max_bytes limit' => sub {
    my $receive = mock_receive(
        { type => 'http.request', body => 'Hello World', more => 0 },
    );

    my $stream = PAGI::Request::BodyStream->new(
        receive => $receive,
        max_bytes => 5,
    );

    like dies { $stream->next_chunk->get }, qr/max_bytes exceeded/, 'throws on limit exceeded';
};

subtest 'UTF-8 decoding' => sub {
    my $receive = mock_receive(
        { type => 'http.request', body => "caf\xc3\xa9", more => 0 },
    );

    my $stream = PAGI::Request::BodyStream->new(
        receive => $receive,
        decode => 'UTF-8',
    );

    my $chunk = $stream->next_chunk->get;
    is $chunk, "café", 'UTF-8 decoded';
};

subtest 'disconnect handling' => sub {
    my $receive = mock_receive(
        { type => 'http.request', body => 'Hello', more => 1 },
        { type => 'http.disconnect' },
    );

    my $stream = PAGI::Request::BodyStream->new(receive => $receive);

    my $chunk1 = $stream->next_chunk->get;
    is $chunk1, 'Hello', 'first chunk';

    my $chunk2 = $stream->next_chunk->get;
    is $chunk2, undef, 'undef on disconnect';
    ok $stream->is_done, 'done after disconnect';
};

subtest 'UTF-8 boundary handling - split multi-byte sequence' => sub {
    # Split café (caf\xc3\xa9) across chunks: "caf\xc3" and "\xa9"
    my $receive = mock_receive(
        { type => 'http.request', body => "caf\xc3", more => 1 },
        { type => 'http.request', body => "\xa9", more => 0 },
    );

    my $stream = PAGI::Request::BodyStream->new(
        receive => $receive,
        decode => 'UTF-8',
    );

    my $chunk1 = $stream->next_chunk->get;
    is $chunk1, "caf", 'first chunk without incomplete sequence';

    my $chunk2 = $stream->next_chunk->get;
    is $chunk2, "é", 'second chunk completes the sequence';

    ok $stream->is_done, 'stream done';
};

subtest 'empty chunks are handled' => sub {
    my $receive = mock_receive(
        { type => 'http.request', body => '', more => 1 },
        { type => 'http.request', body => 'data', more => 1 },
        { type => 'http.request', body => '', more => 0 },
    );

    my $stream = PAGI::Request::BodyStream->new(receive => $receive);

    my $chunk1 = $stream->next_chunk->get;
    is $chunk1, '', 'empty first chunk';

    my $chunk2 = $stream->next_chunk->get;
    is $chunk2, 'data', 'non-empty chunk';

    my $chunk3 = $stream->next_chunk->get;
    is $chunk3, '', 'empty last chunk';

    ok $stream->is_done, 'stream done';
    is $stream->bytes_read, 4, 'bytes_read only counts non-empty';
};

subtest 'stream_to custom sink' => sub {
    my $receive = mock_receive(
        { type => 'http.request', body => 'Hello', more => 1 },
        { type => 'http.request', body => ' World', more => 0 },
    );

    my $stream = PAGI::Request::BodyStream->new(receive => $receive);

    my $collected = '';
    my $bytes = $stream->stream_to(sub {
        my ($chunk) = @_;
        $collected .= $chunk;
    })->get;

    is $collected, 'Hello World', 'all chunks collected';
    is $bytes, 11, 'bytes_processed correct';
    ok $stream->is_done, 'stream done';
};



( run in 0.627 second using v1.01-cache-2.11-cpan-71847e10f99 )