The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;

use Test::More tests => 92;

use RPC::ExtDirect::Test::Util qw/ cmp_json is_deep /;
use RPC::ExtDirect::Config;

use RPC::ExtDirect::Router;

# Test modules are simple
use RPC::ExtDirect::Test::Pkg::Qux;
use RPC::ExtDirect::Test::Pkg::Meta;

my $tests = eval do { local $/; <DATA>; }           ## no critic
    or die "Can't eval DATA: $@";

my %only_tests = map { $_ => 1 } @ARGV;

TEST:
for my $test ( @$tests ) {
    my $name   = $test->{name};
    my $debug  = $test->{debug};
    my $input  = $test->{input};
    my $expect = $test->{output};
    
    next TEST if %only_tests && !$only_tests{$name};

    my $config = RPC::ExtDirect::Config->new(
        debug_router => $debug,
    );

    my $router = RPC::ExtDirect::Router->new(
        config => $config,
    );

    my $result = eval { $router->route($input) };

    # Remove reference addresses. On different platforms
    # stringified reference has different length so we're
    # trying to compensate for that here.
    # Additionally, JSON error output may change (again) and
    # that will break this test (again), so we cheat instead.
    if ( $result->[2]->[0] =~ /HASH\(/ ) {
        s/HASH\([^\)]+\)[^"]+/HASH(blessed)'/g
            for ( $expect->[2]->[0], $result->[2]->[0] );

        $result->[1]->[3] = $expect->[1]->[3] = length $expect->[2]->[0];
    };

    my $want_response = (pop @$expect)->[0];
    my $have_response = (pop @$result)->[0];

    my ($want_json, $have_json);

    # It was a form request; extract JSON
    if ( $want_response =~ /<textarea>/i ) {
        ($want_json) = $want_response =~ /<textarea>(.*)<\/textarea>/i;
        ($have_json) = $have_response =~ /<textarea>(.*)<\/textarea>/i;
    }
    else {
        $want_json = $want_response;
        $have_json = $have_response;
    }

    is       $@,         '',      "$name eval $@";
    is ref   $result,    'ARRAY', "$name result ARRAY";
    is_deep  $result,    $expect, "$name result headers";
    cmp_json $have_json, $want_json, "$name result body";
};

__DATA__
#line 60
[
    { name   => 'Invalid result', debug => 1,
      input  => '{"type":"rpc","tid":1,"action":"Foo","method":"foo_blessed",'.
                ' "data":{}}',
      output => [
                    200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 222,
                    ],
                [
                q|{"action":"Foo","message":"encountered object |.
                q|'foo=HASH(0x10088fca0)', but neither allow_blessed|.
                q| nor convert_blessed settings are enabled","method"|.
                q|:"foo_blessed","tid":1,"type":"exception","where":|.
                q|"RPC::ExtDirect::Serializer"}|,
                ],
                ],
    },
    { name   => 'Invalid POST', debug => 1,
      input  => '{"something":"invalid":"here"}',
      output => [ 200,
                  [ 'Content-Type', 'application/json',
                    'Content-Length', 249,
                  ],
                  [ q|{"action":null,|.
                    q|"message":"ExtDirect error decoding POST data: |.
                    q|', or } expected while parsing object/hash, at |.
                    q|character offset 22 (before \":\"here\"}\")'",|.
                    q|"method":null,"tid":null,|.
                    q|"type":"exception",|.
                    q|"where":"RPC::ExtDirect::Serializer->decode_post"}|
                  ],
                ],
    },
    { name   => 'Valid POST, single request', debug => 1,
      input  => '{"type":"rpc","tid":1,"action":"Qux","method":"foo_foo",'.
                ' "data":["bar"]}',
      output => [ 200,
                  [ 'Content-Type', 'application/json',
                    'Content-Length', 78,
                  ],
                  [ q|{"action":"Qux","method":"foo_foo",|.
                  q|"result":"foo! 'bar'","tid":1,"type":"rpc"}| ],
                ],
    },
    { name   => 'Valid POST, multiple requests', debug => 1,
      input  => q|[{"tid":1,"action":"Qux","method":"foo_foo",|.
                q|  "data":["foo"],"type":"rpc"},|.
                q| {"tid":2,"action":"Qux","method":"foo_bar",|.
                q|  "data":["bar1","bar2"],"type":"rpc"},|.
                q| {"tid":3,"action":"Qux","method":"foo_baz",|.
                q|  "data":{"foo":"baz1","bar":"baz2","baz":"baz3"},|.
                q|          "type":"rpc"}]|,
      output => [ 200, 
                  [ 'Content-Type', 'application/json',
                    'Content-Length', 304,
                  ],
                  [
                  q|[{"action":"Qux","method":"foo_foo",|.
                  q|"result":"foo! 'foo'","tid":1,"type":"rpc"},|.
                  q|{"action":"Qux","method":"foo_bar",|.
                  q|"result":["foo! bar!","bar1","bar2"],"tid":2,|.
                  q|"type":"rpc"},|.
                  q|{"action":"Qux","method":"foo_baz",|.
                  q|"result":{"bar":"baz2","baz":"baz3","foo":"baz1",|.
                  q|"msg":"foo! bar! baz!"},"tid":3,"type":"rpc"}]|
                  ],
                ],
    },
    {
        name   => 'Valid POST with invalid metadata 1', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"arg0","data":null,|.
                  q|"metadata":[42]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 213,
                    ],
                    [
                        q|{"action":"Meta",|.
                        q|"message":"ExtDirectMethodMeta.arg0requires|.
                        q|2metadatavalue(s)butonly1areprovided",|.
                        q|"method":"arg0","tid":1,"type":"exception",|.
                        q|"where":"RPC::ExtDirect::API::Method->check_method_metadata"}|,
                    ],
                  ],
            
    },
    {
        name   => 'Valid POST with metadata 1', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"arg0","data":null,|.
                  q|"metadata":[42,"foo"]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 83,
                    ],
                    [
                        q|{"action":"Meta","method":"arg0",|.
                        q|"result":{"meta":[42,"foo"]},"tid":1,|.
                        q|"type":"rpc"}|,
                    ],
                  ],
            
    },
    {
        name   => 'Valid POST with metadata 2', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"arg1_last","data":["foo"],|.
                  q|"metadata":[42]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 95,
                    ],
                    [
                        q|{"action":"Meta","method":"arg1_last",|.
                        q|"result":{"arg1":"foo","meta":[42]},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 3', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"arg1_first","data":["foo"],|.
                  q|"metadata":[42,43]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 99,
                    ],
                    [
                        q|{"action":"Meta","method":"arg1_first",|.
                        q|"result":{"arg1":"foo","meta":[42,43]},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 4', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"arg2_last","data":[42,43],|.
                  q|"metadata":["foo","bar"]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 105,
                    ],
                    [
                        q|{"action":"Meta","method":"arg2_last",|.
                        q|"result":{"arg1":42,"arg2":43,"meta":["foo"]},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 5', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"arg2_middle","data":[44,45],|.
                  q|"metadata":["fred","bonzo","qux"]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 116,
                    ],
                    [
                        q|{"action":"Meta","method":"arg2_middle",|.
                        q|"result":{"arg1":44,"arg2":45,"meta":|.
                        q|["fred","bonzo"]},"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 6', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"named_default",|.
                  q|"data":{"foo":"bar","fred":"bonzo"},|.
                  q|"metadata":[42]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 113,
                    ],
                    [
                        q|{"action":"Meta","method":"named_default",|.
                        q|"result":{"foo":"bar","fred":"bonzo",|.
                        q|"meta":[42]},"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 7', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"named_arg",|.
                  q|"data":{"qux":"fred"},|.
                  q|"metadata":["blerg"]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 100,
                    ],
                    [
                        q|{"action":"Meta","method":"named_arg",|.
                        q|"result":{"meta":["blerg"],"qux":"fred"},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 8', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"named_arg",|.
                  q|"data":{"foo":"bar"},|.
                  q|"metadata":["blerg"]}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 87,
                    ],
                    [
                        q|{"action":"Meta","method":"named_arg",|.
                        q|"result":{"meta":["blerg"]},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 9', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"named_strict",|.
                  q|"data":{"frob":"dux","frogg":"bonzo"},|.
                  q|"metadata":{"foo":{"bar":{"baz":42}}}}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 136,
                    ],
                    [
                        q|{"action":"Meta","method":"named_strict",|.
                        q|"result":{"frob":"dux","frogg":"bonzo",|.
                        q|"meta":{"foo":{"bar":{"baz":42}}}},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with metadata 10', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"named_unstrict",|.
                  q|"data":{"qux":null},"metadata":{}}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 96,
                    ],
                    [
                        q|{"action":"Meta","method":"named_unstrict",|.
                        q|"result":{"meta":{},"qux":null},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Valid POST with ancillary properties', debug => 1,
        input  => q|{"type":"rpc","tid":1,"action":"Meta",|.
                  q|"method":"aux","data":null,"foo":"bar",|.
                  q|"token":"kaboom!"}|,
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 102,
                    ],
                    [
                        q|{"action":"Meta","method":"aux",|.
                        q|"result":{"aux":{"foo":"bar","token":"kaboom!"}},|.
                        q|"tid":1,"type":"rpc"}|,
                    ],
                  ],
    },
    { name   => 'Invalid form request', debug => 1,
      input  => { extTID => 100, action => 'Bar', method => 'bar_baz',
                  type => 'rpc', data => undef, },
      output => [ 200, [ 'Content-Type', 'application/json',
                         'Content-Length', 208, ],
                  [
                  q|{"action":"Bar",|.
                  q|"message":"ExtDirect formHandler method |.
                  q|Bar.bar_baz should only be called with form submits",|.
                  q|"method":"bar_baz","tid":100,|.
                  q|"type":"exception",|.
                  q|"where":"RPC::ExtDirect::Request->check_arguments"}|,
                  ],
                ],
    },
    { name   => 'Form request, no upload', debug => 1,
      input  => { action => '/router_action', method => 'POST',
                  extAction => 'Bar', extMethod => 'bar_baz',
                  extTID => 123, field1 => 'foo', field2 => 'bar', },
      output => [ 200, [ 'Content-Type', 'application/json',
                         'Content-Length', 99 ],
                  [
                  q|{"action":"Bar","method":"bar_baz",|.
                  q|"result":{"field1":"foo","field2":"bar"},|.
                  q|"tid":123,"type":"rpc"}|,
                  ],
                ],
    },
    {
        name   => 'Form request with decode_params',
        input  => { action => '/router_action', method => 'POST',
                    extAction => 'Bar', extType => 'rpc', extTID => 432,
                    extAction => 'Bar', extMethod => 'bar_baz',
                    blerg => '["bar","baz"]',
                    frob => '{"throbbe":["vita","voom"]}', },
        output => [ 200, [ 'Content-Type', 'application/json',
                           'Content-Length', 132, ],
                    [
                        q|{"action":"Bar","method":"bar_baz","type":"rpc",|.
                        q|"result":{"blerg":"[\"bar\",\"baz\"]",|.
                        q|"frob":{"throbbe":["vita","voom"]}},|.
                        q|"tid":432}|,
                    ],
                  ],
    },
    {
        name   => 'Form request with ordered metadata', debug => 1,
        input  => {
                    action => '/router_action', method => 'POST',
                    extAction => 'Meta', extMethod => 'form_ordered',
                    extTID => 42, extType => 'rpc',
                    fred => 'frob', metadata => [42],
                  },
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 104,
                    ],
                    [
                        q|{"action":"Meta","method":"form_ordered",|.
                        q|"result":{"fred":"frob","metadata":[42]},|.
                        q|"tid":42,"type":"rpc"}|,
                    ],
                  ],
    },
    {
        name   => 'Form request with named metadata', debug => 1,
        input  => {
                    action => '/router_action', method => 'POST',
                    extAction => 'Meta', extMethod => 'form_named',
                    extTID => 58, extType => 'rpc',
                    boogaloo => 1916, frogg => 'splurge',
                    metadata => { foo => 1, bar => 2, baz => 3 },
                  },
        output => [ 200,
                    [
                        'Content-Type', 'application/json',
                        'Content-Length', 137,
                    ],
                    [
                        q|{"action":"Meta","method":"form_named",|.
                        q|"result":{"_m":{"bar":2,"baz":3,"foo":1},|.
                        q|"boogaloo":1916,"frogg":"splurge"},|.
                        q|"tid":58,"type":"rpc"}|,
                    ],
                  ],
    },
    { name   => 'Form request, upload one file', debug => 1,
      input  => { action => '/router.cgi', method => 'POST',
                    extAction => 'Bar', extMethod => 'bar_baz',
                    extTID => 7, foo_field => 'foo', bar_field => 'bar',
                    extUpload => 'true',
                    _uploads => [{ basename => 'foo.txt',
                        type => 'text/plain', handle => {},     # dummy
                        filename => 'C:\Users\nohuhu\foo.txt',
                        path => '/tmp/cgi-upload/foo.txt', size => 123 }],
                },
      output => [ 200, [ 'Content-Type', 'text/html',
                         'Content-Length', 232, ],
                  [
                  q|<html><body><textarea>|.
                  q|{"action":"Bar","method":"bar_baz",|.
                  q|"result":{"bar_field":"bar",|.
                  q|"foo_field":"foo",|.
                  q|"upload_response":"The following files were |.
                  q|processed:\n|.
                  q|foo.txt text/plain 123\n"|.
                  q|},"tid":7,|.
                  q|"type":"rpc"}|.
                  q|</textarea></body></html>|,
                  ],
                ],
    },
    { name   => 'Form request, multiple uploads', debug => 1,
      input  => { action => '/router_action', method => 'POST',
                    extAction => 'Bar', extMethod => 'bar_baz',
                    extTID => 8, field => 'value', extUpload => 'true',
                    _uploads => [
                        { basename => 'bar.jpg', handle => {},
                          type => 'image/jpeg', filename => 'bar.jpg',
                          path => 'C:\Windows\tmp\bar.jpg', size => 123123, },
                        { basename => 'qux.png', handle => {},
                          type => 'image/png', filename => '/tmp/qux.png',
                          path => 'C:\Windows\tmp\qux.png', size => 54321, },
                        { basename => 'script.js', handle => undef,
                          type => 'application/javascript', size => 1000,
                          filename => '/Users/nohuhu/Documents/script.js',
                          path => 'C:\Windows\tmp\script.js', }, ],
                },
      output => [ 200, [ 'Content-Type', 'text/html',
                         'Content-Length', 279, ],
                  [
                  q|<html><body><textarea>|.
                  q|{"action":"Bar","method":"bar_baz",|.
                  q|"result":{|.
                  q|"field":"value",|.
                  q|"upload_response":"The following files were |.
                  q|processed:\n|.
                  q|bar.jpg image/jpeg 123123\n|.
                  q|qux.png image/png 54321\n|.
                  q|script.js application/javascript 1000\n"|.
                  q|},"tid":8,"type":"rpc"}|.
                  q|</textarea></body></html>|,
                  ],
                ],
    },
]