1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
use Mojo::Base -strict;
BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
use Test::More;
BEGIN {
plan skip_all => 'set TEST_ASYNC_AWAIT to enable this test (developer only!)'
unless $ENV{TEST_ASYNC_AWAIT} || $ENV{TEST_ALL};
plan skip_all => 'Future::AsyncAwait 0.36+ required for this test!' unless Mojo::Base->ASYNC;
}
use Mojo::Base -async_await;
use Test::Future::AsyncAwait::Awaitable qw(test_awaitable);
use Test::Mojo;
use Mojo::Promise;
use Mojo::UserAgent;
use Mojolicious::Lite;
# async/await spec
test_awaitable('Mojo::Promise conforms to Awaitable API', class => "Mojo::Promise", force => sub { shift->wait },);
# Silence
app->log->level('fatal');
helper defer_resolve_p => sub {
my ($c, $msg) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->next_tick(sub { $promise->resolve($msg) });
return $promise;
};
helper defer_reject_p => sub {
my ($c, $msg) = @_;
my $promise = Mojo::Promise->new;
Mojo::IOLoop->next_tick(sub { $promise->reject($msg) });
return $promise;
};
get '/one' => {text => 'works!'};
get '/two' => {text => 'also'};
get '/three' => async sub {
my $c = shift;
my $first = await $c->defer_resolve_p('this ');
my $second = await $c->defer_resolve_p('works');
my $third = await $c->defer_resolve_p(' too!');
$c->render(text => "$first$second$third");
};
get '/four' => async sub {
my $c = shift;
my $text = await Mojo::Promise->resolve('fail');
eval { await $c->defer_reject_p('this went perfectly') };
if (my $err = $@) { $c->render(text => $err, status => 500) }
else { $c->render(text => $text) }
};
get '/five' => async sub {
my $c = shift;
my $runaway = $c->defer_reject_p('runaway too');
await $c->defer_resolve_p('fail');
await $runaway;
};
get '/six' => sub {
my $c = shift;
$c->on(
message => async sub {
my ($c, $msg) = @_;
my $first = await $c->defer_resolve_p("One: $msg");
my $second = await $c->defer_resolve_p("Two: $msg");
$c->send("$first $second")->finish;
}
);
};
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
async sub test_one {
await $ua->get_p('/one');
}
async sub test_two {
my $separator = shift;
my $text = '';
my $two = await $ua->get_p('/two');
$text .= $two->res->body;
my $one = await $ua->get_p('/one');
$text .= $separator . $one->res->body;
return $text;
}
async sub test_three {
my $ok = shift;
return Mojo::Promise->new(sub {
my ($resolve, $reject) = @_;
Mojo::IOLoop->next_tick(sub { ($ok ? $resolve : $reject)->('value') });
});
}
my $t = Test::Mojo->new;
# Basic async/await
my $promise = test_one();
isa_ok $promise, 'Mojo::Promise', 'right class';
my $tx;
$promise->then(sub { $tx = shift })->catch(sub { warn @_ });
$promise->wait;
is $tx->res->body, 'works!', 'right content';
# Multiple awaits
my $text;
test_two(' ')->then(sub { $text = shift })->catch(sub { warn @_ })->wait;
is $text, 'also works!', 'right content';
# Application with async/await action
$t->get_ok('/three')->content_is('this works too!');
# Exception handling and async/await
$t->get_ok('/four')->status_is(500)->content_like(qr/this went perfectly/);
# Runaway exception
$t->get_ok('/five')->status_is(500)->content_like(qr/runaway too/);
# Async function body returning a promise
$text = undef;
test_three(1)->then(sub { $text = shift })->catch(sub { warn @_ })->wait;
is $text, 'value', 'right content';
$text = undef;
test_three(0)->then(sub { warn @_ })->catch(sub { $text = shift })->wait;
is $text, 'value', 'right content';
# Async WebSocket
$t->websocket_ok('/six')->send_ok('test')->message_ok->message_is('One: test Two: test')->finish_ok;
done_testing();
|