EV-Websockets

 view release on metacpan or  search on metacpan

t/27-review-coverage.t  view on Meta::CPAN

    my $to = EV::timer(10, 0, sub { diag "Timeout!"; EV::break });
    EV::run;

    ok($got101, "raw client got a 101 response");
    ok($found, "static listen(headers => ...) injected into 101 response");
}

# 5. on_drain does NOT fire once close() has been queued.
{
    my $ctx = EV::Websockets::Context->new();
    my %keep;
    my ($drain_after_close, $closed) = (0, 0);

    my $port = $ctx->listen(
        port       => 0,
        on_connect => sub { $keep{srv} = $_[0] },
        on_message => sub { },
        on_close   => sub { delete $keep{srv} },
    );

    $keep{cli} = $ctx->connect(
        url        => "ws://127.0.0.1:$port",
        on_connect => sub {
            my ($c) = @_;
            $c->send("x" x 1024) for 1 .. 5;   # queue payload
            $c->close(1000);                    # then start closing
        },
        on_drain   => sub { $drain_after_close = 1 },
        on_message => sub { },
        on_close   => sub {
            $closed = 1; delete $keep{cli};
            my $t; $t = EV::timer(0.3, 0, sub { undef $t; EV::break });
        },
        on_error   => sub { delete $keep{cli}; EV::break },
    );

    my $to = EV::timer(10, 0, sub { diag "Timeout!"; EV::break });
    EV::run;

    ok($closed, "connection closed after queue + close()");
    ok(!$drain_after_close, "on_drain suppressed once close() was queued");
}

# 6a. listen() reserved-name guard croaks and cleans up its callback SVs.
{
    my $ctx = EV::Websockets::Context->new();
    my $ok = eval {
        $ctx->listen(port => 0, name => 'default', on_message => sub { });
        1;
    };
    ok(!$ok, "listen(name => 'default') croaked");
    like($@, qr/reserved/i, "croak says the name is reserved");
}

# NOTE: the listen() bind-failure (RETVAL <= 0) path isn't driven here:
# colliding on an explicit port is unreliable under SO_REUSEPORT (the bind
# succeeds, and a 2-vhost-same-port context tickles an lws-internal teardown
# read). The vhost-creation-failure path (free_server_svs) is exercised by an
# unreadable-TLS-cert listen in t/28 (fork-contained) -- that path once
# SIGSEGV'd via OpenSSL's ERR path after a context had been destroyed, but the
# SSL keepalive fix resolved it. free_server_svs also runs on every successful
# listen() teardown via PROTOCOL_DESTROY.

# 7. Saving $conn inside on_error then dropping it must not crash.
#    Exercises the connect() failure path where perl_self is set during a
#    (possibly synchronous) connection error.
{
    my $ctx = EV::Websockets::Context->new();
    my $saved;
    my $err = 0;

    {
        my %keep;
        $keep{cli} = $ctx->connect(
            url        => "ws://127.0.0.1:1",
            on_connect => sub { },
            on_error   => sub { $saved = $_[0]; $err++; EV::break },
        );
        my $to = EV::timer(5, 0, sub { diag "Timeout!"; EV::break });
        EV::run;
    }

    ok($err, "on_error fired for refused connection");
    isa_ok($saved, 'EV::Websockets::Connection', "conn captured in on_error");
    is($saved->state, 'closed', "captured conn reports 'closed'");
    is($saved->send_queue_size, 0, "send_queue_size is 0 on closed conn (no croak)");
    $saved = undef;   # drop the last reference -> DESTROY
    pass("no crash after dropping the saved conn reference");
}

# 8. send_queue_size returns 0 (no croak) after the context is destroyed.
{
    my $conn;
    {
        my $ctx = EV::Websockets::Context->new();
        my %keep;
        my $port = $ctx->listen(
            port       => 0,
            on_connect => sub { $keep{srv} = $_[0] },
            on_message => sub { },
            on_close   => sub { delete $keep{srv} },
        );
        $keep{cli} = $ctx->connect(
            url        => "ws://127.0.0.1:$port",
            on_connect => sub { $conn = $_[0]; EV::break },
            on_error   => sub { EV::break },
        );
        my $to = EV::timer(10, 0, sub { diag "Timeout!"; EV::break });
        EV::run;
        # $ctx and %keep drop here -> Context::DESTROY -> conn becomes "closed"
    }

    ok($conn, "captured a live connection");
    is($conn->state, 'closed', "conn is 'closed' after context destroyed");
    is($conn->send_queue_size, 0, "send_queue_size returns 0 on closed conn");
    $conn = undef;
}

# 9. connections() excludes a still-connecting conn, includes it once open.
#    (get_protocol, peer_address and send_queue_size>0 happy paths are already
#    covered by t/18 and t/24; only the connecting-exclusion is untested.)



( run in 1.840 second using v1.01-cache-2.11-cpan-14f38c9f855 )