Sponsoring The Perl Toolchain Summit 2025: Help make this important event another success Learn more

#! perl -I. -w
use Dancer qw/:syntax !pass !warning/;
{
note("default publish => 'pod' ; Batch-mode");
set(plugins => {
'RPC::JSONRPC' => {
'/endpoint' => {
'TestProject::SystemCalls' => {
'system.ping' => 'do_ping',
'system.version' => 'do_version',
},
},
}
});
jsonrpc '/endpoint' => { };
route_exists([POST => '/endpoint'], "/endpoint registered");
my $response = dancer_response(
POST => '/endpoint',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json([
{
jsonrpc => '2.0',
method => 'system.ping',
id => 42,
},
{
jsonrpc => '2.0',
method => 'system.version',
id => 43,
}
]),
}
);
my $ping = from_json('{"response": true}');
if (JSON->VERSION >= 2.90) {
my $t = 1;
$ping->{response} = bless \$t, 'JSON::PP::Boolean';
}
my @results = map $_->{result}, @{from_json($response->{content})};
is_deeply(
\@results,
[
$ping,
{software_version => '1.0'},
],
"system.ping"
) or diag(explain(\@results));
}
{
note("publish is code that returns the dispatch-table");
jsonrpc '/endpoint2' => {
publish => sub {
eval { require TestProject::SystemCalls; };
error("Cannot load: $@") if $@;
return {
'code.ping' => dispatch_item(
code => \&TestProject::SystemCalls::do_ping,
package => 'TestProject::SystemCalls',
),
};
},
callback => sub { return callback_success() },
};
route_exists([POST => '/endpoint2'], "/endpoint2 registered");
my $response = dancer_response(
POST => '/endpoint2',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'code.ping',
id => 42,
}
),
}
);
my $ping = from_json('{"response": true}');
if (JSON->VERSION >= 2.90) {
my $t = 1;
$ping->{response} = bless \$t, 'JSON::PP::Boolean';
}
is_deeply(
from_json($response->{content})->{result},
$ping,
"code.ping"
);
}
{
note("callback fails");
jsonrpc '/endpoint_fail' => {
publish => sub {
eval { require TestProject::SystemCalls; };
error("Cannot load: $@") if $@;
return {
'fail.ping' => dispatch_item(
code => \&TestProject::SystemCalls::do_ping,
package => 'TestProject::SystemCalls',
),
};
},
callback => sub {
return callback_fail(
error_code => -500,
error_message => "Force callback error",
);
},
};
route_exists([POST => '/endpoint_fail'], "/endpoint_fail registered");
my $response = dancer_response(
POST => '/endpoint_fail',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'fail.ping',
id => 42,
}
),
}
);
is_deeply(
from_json($response->{content})->{error},
{code => -500, message =>"Force callback error"},
"fail.ping"
) or diag($response->{content});
}
{
note("callback dies");
jsonrpc '/endpoint_fail2' => {
publish => sub {
eval { require TestProject::SystemCalls; };
error("Cannot load: $@") if $@;
return {
'fail.ping' => dispatch_item(
code => \&TestProject::SystemCalls::do_ping,
package => 'TestProject::SystemCalls',
),
};
},
callback => sub {
die "terrible death\n";
},
};
route_exists([POST => '/endpoint_fail2'], "/endpoint_fail registered");
my $response = dancer_response(
POST => '/endpoint_fail2',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'fail.ping',
id => 42,
}
),
}
);
is_deeply(
from_json($response->{content})->{error},
{code => -32500, message =>"terrible death\n"},
"fail.ping"
);
}
{
note("code_wrapper dies");
jsonrpc '/endpoint_fail3' => {
publish => sub {
eval { require TestProject::SystemCalls; };
error("Cannot load: $@") if $@;
return {
'fail.ping' => dispatch_item(
code => \&TestProject::SystemCalls::do_ping,
package => 'TestProject::SystemCalls',
),
};
},
callback => sub {
return callback_success();
},
code_wrapper => sub {
die "code_wrapper died\n";
},
};
route_exists([POST => '/endpoint_fail3'], "/endpoint_fail3 registered");
my $response = dancer_response(
POST => '/endpoint_fail3',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'fail.ping',
id => 42,
}
),
}
);
my $error = from_json($response->{content})->{error};
is_deeply(
$error,
{code => -32500, message =>"code_wrapper died\n"},
"fail.ping (code_wrapper died)"
) or diag(explain($error));
}
{
note("callback returns unknown object");
jsonrpc '/endpoint_fail4' => {
publish => sub {
eval { require TestProject::SystemCalls; };
error("Cannot load: $@") if $@;
return {
'fail.ping' => dispatch_item(
code => \&TestProject::SystemCalls::do_ping,
package => 'TestProject::SystemCalls',
),
};
},
callback => sub {
bless {easter => 'egg'}, 'SomeRandomClass';
},
code_wrapper => sub {
return 'pang';
},
};
route_exists([POST => '/endpoint_fail4'], "/endpoint_fail4 registered");
my $response = dancer_response(
POST => '/endpoint_fail4',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'fail.ping',
id => 42,
}
),
}
);
my $error = from_json($response->{content})->{error};
is_deeply(
$error,
{
code => -32603,
message => "Internal error: 'callback_result' wrong class SomeRandomClass",
},
"fail.ping (callback wrong class)"
) or diag(explain($error));
}
{
note("code_wrapper returns unknown object");
jsonrpc '/endpoint_fail5' => {
publish => sub {
eval { require TestProject::SystemCalls; };
error("Cannot load: $@") if $@;
return {
'fail.ping' => dispatch_item(
code => \&TestProject::SystemCalls::do_ping,
package => 'TestProject::SystemCalls',
),
};
},
callback => sub {
return callback_success();
},
code_wrapper => sub {
bless {easter => 'egg'}, 'SomeRandomClass';
},
};
route_exists([POST => '/endpoint_fail5'], "/endpoint_fail5 registered");
my $response = dancer_response(
POST => '/endpoint_fail5',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'fail.ping',
id => 42,
}
),
}
);
my $error = from_json($response->{content})->{result};
is_deeply(
$error,
{easter => 'egg'},
"fail.ping (code_wrapper object)"
) or diag(explain($error));
}
{
note("rpc-call fails");
jsonrpc '/endpoint_error' => {
publish => sub {
return {
'fail.error' => dispatch_item(
code => sub { die "Example error code\n" },
package => __PACKAGE__,
),
};
},
};
route_exists([POST => '/endpoint_error'], "/endpoint_error registered");
my $response = dancer_response(
POST => '/endpoint_error',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'fail.error',
id => 42,
}
),
}
);
is_deeply(
from_json($response->{content})->{error},
{code => -32500, message =>"Example error code\n"},
"fail.error"
);
}
{
note("return an error_response()");
jsonrpc '/endpoint_fault' => {
publish => sub {
return {
'fail.error' => dispatch_item(
code => sub { error_response(error_code => 42, error_message => "Boo!") },
package => __PACKAGE__,
),
};
},
};
route_exists([POST => '/endpoint_fault'], "/endpoint_fault registered");
my $response = dancer_response(
POST => '/endpoint_fault',
{
headers => [
'Content-Type' => 'application/json',
],
body => to_json(
{
jsonrpc => '2.0',
method => 'fail.error',
id => 42,
}
),
}
);
is_deeply(
from_json($response->{content})->{error},
{code => 42, message =>"Boo!"},
"fail.error"
) or diag(explain($response));
}
done_testing();