### ----------------------------------------------------
### -- Cro::RPC::JSON
### -- Licenses: Artistic-2.0
### -- Authors: Vadim Belman <vrurg@lflat.org>
### -- File: t/005-trait.rakutest
### ----------------------------------------------------
use Test;
use Cro::RPC::JSON:api<2>;
use Cro::RPC::JSON::Metamodel::ClassHOW;

subtest "Basic" => {
    plan 7;

    my $inst;

    my class Foo {
        method foo (:$param1) is json-rpc { "method foo" }
        method jrpc-test (:$param1) is json-rpc("bar") { "method bar" }
        method event-emitter($) is json-rpc(:async) { }
    }

    $inst = Foo.new;

    for <foo bar> -> $jmethod {
        my $m = $inst.^json-rpc-find-method($jmethod);
        ok so $m, "method $jmethod exists";
        is $inst.&$m, "method $jmethod", "valid return from $jmethod";
    }

    my $async = Foo.^json-rpc-adhoc-methods('async');
    isa-ok $async, List, "list of adhoc received";
    is $async.elems, 1, "one async found";
    is $async[0].name, "event-emitter", "async method found";
}

subtest "Role" => {
    plan 5;

    my $inst;

    my role Bar[::T] {
        method foo is json-rpc { "method foo" }
        method jrpc-bar is json-rpc("bar") { "method bar" }
        method non-jrpc { die "oops, shouldn't be used!" }
    }

    my class Foo does Bar[Int] {
    }

    isa-ok Foo.HOW, Cro::RPC::JSON::Metamodel::ClassHOW, "actor role consumption results in an actor class";

    $inst = Foo.new;

    for <foo bar> -> $jmethod {
        my $m = $inst.^json-rpc-find-method($jmethod);
        ok $m.defined, "method $jmethod exists";
        is $inst.&$m, "method $jmethod", "valid return from $jmethod";
    }

}

subtest "Inheritance" => {
    plan 9;
    my $inst;
    my role Bar {
        method foo is json-rpc { "method foo" }
        method jrpc-bar is json-rpc("bar") { "method bar" }
        method fubar is json-rpc { "method fubar" }
        method emitter1 is json-rpc(:async) { "emitter1" }
        multi method multi-emitter is json-rpc(:async) { "multi-emitter" }
    }

    my class Baz does Bar {
    }

    my class Foo is Baz {
        # Override fubar and make it unavailable for JSON-RPC
        method fubar { "non-RPC" }
        method emitter2 is json-rpc(:async) { "emitter2" }
    }

    my class Fubar is Foo {
        method baz is json-rpc { "method baz" }
        method emitter3 is json-rpc(:async) { "emitter3" }
    }

    $inst = Fubar.new;

    for <foo bar baz> -> $jmethod {
        my $m = $inst.^json-rpc-find-method($jmethod);
        ok so $m, "method $jmethod exists";
        is $inst.&$m, "method $jmethod", "valid return from $jmethod";
    }

    my $m = $inst.^json-rpc-find-method('fubar');
    isa-ok $m, Failure, "overriden method is not available for JSON-RPC";

    my @async = $inst.^json-rpc-adhoc-methods("async");
    is +@async, 4, "all :async methods found";
    is-deeply @async.map(*.name).List, <emitter3 emitter2 emitter1 multi-emitter>, ":async method names";
}

subtest "Multi" => {
    plan 3;
    my $inst;
    my role Bar {
        proto method foo (|) is json-rpc { * }
        multi method foo () { "method foo" }
        method jrpc-bar is json-rpc("bar") { "method bar" }
        multi method fubar(Str:D $s) { "method fubar( $s )" }
    }

    my class Baz does Bar {
        multi method foo ( Str $s ) { "method foo( $s )" }
        multi method foo ( Num $r ) is json-rpc { "method foo( $r )" }
    }

    my class Foo is Baz {
    }

    my class Fubar is Foo {
        method baz is json-rpc { "method baz" }
        multi method foo ( Int $i, Str $s ) { "method foo( $i, $s )" }
        multi method fubar(Int:D $i) is json-rpc { "method fubar( $i )" }
    }

    $inst = Fubar.new;

#    my $foo = Fubar.^lookup('foo');
#    note "&Fubar::foo: ", $foo.raku, " // ", $foo.WHICH,
#         "\n - candidates: ", $foo.candidates.elems,
#         "\n - package: ", $foo.package.^name
#        ;

    my &m = $inst.^json-rpc-find-method("foo");
#    note "---- ", &m.raku, " // ", &m.WHICH;
#    note " . Candidates: ", &m.candidates.elems;
#    note " . Proto: ", &m.dispatcher.raku;
#    note " . Multi: ", &m.multi;
#    note " . Package: ", &m.package.^name;
    is $inst.&m(), "method foo", "no params";
    is $inst.&m( π ), "method foo( {π} )", "Num";
    is $inst.&m( 314, "pi" ), "method foo( 314, pi )", "Int, Str";
}

done-testing;

# vim: ft=perl6


