#! perl -I. -w use t::Test::abeltje; use Dancer qw/:syntax !pass !warning/; use Dancer::Plugin::RPC::JSONRPC; use Dancer::RPCPlugin::CallbackResult; use Dancer::RPCPlugin::DispatchItem; use Dancer::RPCPlugin::ErrorResponse; use Dancer::Test; { 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();