The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use warnings;
use strict;
use utf8;
use open qw(:std :utf8);
use lib qw(lib ../lib);
use lib qw(blib/lib blib/arch ../blib/lib ../blib/arch);

my $LE = $] > 5.01 ? '<' : '';

use constant PLAN       => 100;
use Test::More;
BEGIN {
    eval "use Coro";
    plan skip_all => "Coro isn't installed" if $@;
    plan tests => PLAN;
}

use Encode qw(decode encode);

BEGIN {
    # Подготовка объекта тестирования для работы с utf8
    my $builder = Test::More->builder;
    binmode $builder->output,         ":utf8";
    binmode $builder->failure_output, ":utf8";
    binmode $builder->todo_output,    ":utf8";

    use_ok 'DR::Tarantool::StartTest';
    use_ok 'DR::Tarantool', ':constant';
    use_ok 'DR::Tarantool::CoroClient';
    use_ok 'File::Spec::Functions', 'catfile';
    use_ok 'File::Basename', 'dirname', 'basename';
    use_ok 'Coro';
    use_ok 'AnyEvent';
}

my $cfg_dir = catfile dirname(__FILE__), 'test-data';
ok -d $cfg_dir, 'directory with test data';
my $tcfg = catfile $cfg_dir, 'llc-easy2.cfg';
ok -r $tcfg, $tcfg;

my $tnt = run DR::Tarantool::StartTest( cfg => $tcfg );

my $spaces = {
    0   => {
        name            => 'first_space',
        fields  => [
            {
                name    => 'id',
                type    => 'NUM',
            },
            {
                name    => 'name',
                type    => 'UTF8STR',
            },
            {
                name    => 'key',
                type    => 'NUM',
            },
            {
                name    => 'password',
                type    => 'STR',
            },
            {
                name    => 'json',
                type    => 'JSON',
            }
        ],
        indexes => {
            0   => 'id',
            1   => 'name',
            2   => [ 'key', 'password' ],
        },
    }
};

SKIP: {
    unless ($tnt->started and !$ENV{SKIP_TNT}) {
        diag $tnt->log unless $ENV{SKIP_TNT};
        skip "tarantool isn't started", PLAN - 9;
    }

    my $client = DR::Tarantool::CoroClient->connect(
        port    => $tnt->primary_port,
        spaces  => $spaces
    );

    isa_ok $client => 'DR::Tarantool::CoroClient';
    is $client->last_code, undef, 'last_code';
    is $client->last_error_string, undef, 'last_error_string';
    ok $client->ping, '* ping';

    my $t = $client->insert(
        first_space => [ 1, 'привет', 2, 'test' ], TNT_FLAG_RETURN
    );

    isa_ok $t => 'DR::Tarantool::Tuple', '* insert tuple packed';
    is $t->id, 1, 'id';
    is $t->name, 'привет', 'name';
    is $t->key, 2, 'key';
    is $t->password, 'test', 'password';

    $t = $client->insert(
        first_space => [ 2, 'медвед', 3, 'test2' ], TNT_FLAG_RETURN
    );

    isa_ok $t => 'DR::Tarantool::Tuple', 'insert tuple packed';
    is $t->id, 2, 'id';
    is $t->name, 'медвед', 'name';
    is $t->key, 3, 'key';
    is $t->password, 'test2', 'password';


    $t = $client->call_lua('box.select' =>
        [ 0, 0, pack "L$LE" => 1 ], 'first_space');
    isa_ok $t => 'DR::Tarantool::Tuple', '* call tuple packed';
    is $t->id, 1, 'id';
    is $t->name, 'привет', 'name';
    is $t->key, 2, 'key';
    is $t->password, 'test', 'password';


    $t = $client->select(first_space => 1);
    isa_ok $t => 'DR::Tarantool::Tuple', '* select tuple packed';
    is $t->id, 1, 'id';
    is $t->name, 'привет', 'name';
    is $t->key, 2, 'key';
    is $t->password, 'test', 'password';

    $t = $client->select(first_space => 'привет', 'i1');
    isa_ok $t => 'DR::Tarantool::Tuple', 'select tuple packed (i1)';
    is $t->id, 1, 'id';
    is $t->name, 'привет', 'name';
    is $t->key, 2, 'key';
    is $t->password, 'test', 'password';

    $t = $client->select(first_space => [[2, 'test']], 'i2');
    isa_ok $t => 'DR::Tarantool::Tuple', 'select tuple packed (i2)';
    is $t->id, 1, 'id';
    is $t->name, 'привет', 'name';
    is $t->key, 2, 'key';
    is $t->password, 'test', 'password';

    $t = $client->update(first_space => 2 => [ name => set => 'привет1' ]);
    is $t, undef, '* update without flags';
    $t = $client->update(
        first_space => 2 => [ name => set => 'привет медвед' ], TNT_FLAG_RETURN
    );
    isa_ok $t => 'DR::Tarantool::Tuple', 'update with flags';
    is $t->name, 'привет медвед', '$t->name';


    $t = $client->insert(first_space => [1, 2, 3, 4, undef], TNT_FLAG_RETURN);
    is $t->json, undef, 'JSON insert: undef';

    $t = $client->insert(first_space => [1, 2, 3, 4, 22], TNT_FLAG_RETURN);
    is $t->json, 22, 'JSON insert: scalar';

    $t = $client->insert(first_space => [1, 2, 3, 4, 'тест'], TNT_FLAG_RETURN);
    is $t->json, 'тест', 'JSON insert: utf8 scalar';

    $t = $client->insert(
        first_space => [ 1, 2, 3, 4, { a => 'b' } ], TNT_FLAG_RETURN
    );
    isa_ok $t->json => 'HASH', 'JSON insert: hash';
    is $t->json->{a}, 'b', 'JSON insert: hash value';

    $t = $client->insert(
        first_space => [ 1, 2, 3, 4, { привет => 'медвед' } ], TNT_FLAG_RETURN
    );
    isa_ok $t->json => 'HASH', 'JSON insert: hash';
    is $t->json->{привет}, 'медвед', 'JSON insert: hash utf8 value';
    
    ok !eval {
        $client->insert(
            first_space => [ 1 .. 10 ], TNT_FLAG_RETURN | TNT_FLAG_ADD
        );
        1
    }, 'raise error';
    like $@, qr{Duplicate key exists|Tuple already exists}, 'error message';

    {
        local $client->{raise_error};
        ok eval {
            $client->insert(
                first_space => [ 1 .. 10 ], TNT_FLAG_RETURN | TNT_FLAG_ADD
            );
            1
        }, 'no raise error';
        like $client->last_error_string,
            qr{Duplicate key exists|Tuple already exists}, 'error message';
    }


    my $start = AnyEvent::now;
    my (@fibers, %tuples);
    for my $n (123 .. 129) {
        push @fibers => async {
            Coro::AnyEvent::sleep(rand);
            $tuples{ $n } =
                $client->insert(
                    'first_space',
                    [ $n, "тест $n", $n, "password $n" ],
                    TNT_FLAG_RETURN
                )
            ;
        };
    }

    $_->join for @fibers;

    cmp_ok AnyEvent::now() - $start, '<', '2', 'all processes were done async';

    for my $n (123 .. 129) {
        ok exists $tuples{$n}, 'result exists';
        isa_ok $tuples{$n} => 'DR::Tarantool::Tuple';
        is $tuples{$n}->id, $n, 'id';
        is $tuples{$n}->name, "тест $n", 'name';
        is $tuples{$n}->key, $n, 'key';
        is $tuples{$n}->password, "password $n", 'password';
    }
}