The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;

use Test::More;

use IO::Async::Test;
use IO::Async::Loop;

use Net::Async::HTTP;

my $CRLF = "\x0d\x0a"; # because \r\n isn't portable

my $loop = IO::Async::Loop->new();
testing_loop( $loop );

my $http = Net::Async::HTTP->new(
   user_agent => "", # Don't put one in request headers
);

$loop->add( $http );

my $peersock;
no warnings 'redefine';
local *IO::Async::Handle::connect = sub {
   my $self = shift;
   my %args = @_;
   $args{host} eq "localhost" or die "Cannot fake connect - expected host 'localhost'";
   $args{service} eq "5000"   or die "Cannot fake connect - expected service '5000'";

   ( my $selfsock, $peersock ) = IO::Async::OS->socketpair() or die "Cannot create socket pair - $!";
   $self->set_handle( $selfsock );

   return Future->done( $self );
};

# Without on_error
{
   my $f1 = $http->GET( "http://localhost:5000/1" )
      ->on_done( sub { die "Oopsie" } );

   my $f2 = $http->GET( "http://localhost:5000/2" );

   wait_for { defined $peersock };

   my $request_stream = "";

   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
   pass( "First request is made" );

   $request_stream =~ s/^.*$CRLF$CRLF//s;

   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
                        "Content-Length: 0$CRLF" .
                        $CRLF );

   my $e = eval { $loop->loop_once(0) for 1 .. 5; 1 } ? undef : $@;
   like( $e, qr/^Oopsie at \Q$0\E line \d+/,
      'Oopsie exception caught at loop toplevel' );

   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
   pass( "Second request is made after first one dies at ->done" );

   $request_stream =~ s/^.*$CRLF$CRLF//s;

   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
                        "Content-Length: 0$CRLF" .
                        $CRLF );

   wait_for { $f2->is_ready };
   ok( !$f2->failure, '$f2 completes successfully' );
}

# With on_error
{
   my $error;
   $http->configure(
      on_error => sub { ( undef, $error ) = @_; },
   );

   my $f1 = $http->GET( "http://localhost:5000/1" )
      ->on_done( sub { die "Oopsie" } );

   my $f2 = $http->GET( "http://localhost:5000/2" );

   wait_for { defined $peersock };

   my $request_stream = "";

   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
   pass( "First request is made" );

   $request_stream =~ s/^.*$CRLF$CRLF//s;

   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
                        "Content-Length: 0$CRLF" .
                        $CRLF );

   my $e = eval { $loop->loop_once(0) for 1 .. 5; 1 } ? undef : $@;
   ok( !defined $e, 'Loop toplevel does not catch exception' ) or
      diag( "Caught exception was: $e" );

   like( $error, qr/^Oopsie at \Q$0\E line \d+/,
      'Oopsie exception caught by on_error handler' );

   wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream;
   pass( "Second request is made after first one dies at ->done" );

   $request_stream =~ s/^.*$CRLF$CRLF//s;

   $peersock->syswrite( "HTTP/1.1 200 OK$CRLF" .
                        "Content-Length: 0$CRLF" .
                        $CRLF );

   wait_for { $f2->is_ready };
   ok( !$f2->failure, '$f2 completes successfully' );
}

done_testing;