# Net::ZooKeeper - Perl extension for Apache ZooKeeper
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

use File::Spec;
use Test::More tests => 107;

BEGIN { use_ok('Net::ZooKeeper', qw(:all)) };


my $test_dir;
(undef, $test_dir, undef) = File::Spec->splitpath($0);
require File::Spec->catfile($test_dir, 'util.pl');

my($hosts, $root_path, $node_path) = zk_test_setup(1);


## new()

eval {
    Net::ZooKeeper->new();
};
like($@, qr/Usage: Net::ZooKeeper::new\(package, hosts, \.\.\.\)/,
     'new(): no hostname specified');

eval {
    Net::ZooKeeper->new($hosts, 'bar');
};
like($@, qr/invalid number of arguments/,
     'new(): invalid number of arguments');

eval {
    Net::ZooKeeper->new($hosts, 'session_timeout' => -3);
};
like($@, qr/invalid session timeout/,
     'new(): invalid session timeout');

eval {
    Net::ZooKeeper->new($hosts, 'session_timeout' => 0x4000_0000);
};
like($@, qr/invalid session timeout/,
     'new(): invalid session timeout');

eval {
    Net::ZooKeeper->new($hosts, 'session_id' => 'abcdef');
};
like($@, qr/invalid session ID/,
     'new(): invalid session ID');

my $zkh = Net::ZooKeeper->new($hosts);
isa_ok($zkh, 'Net::ZooKeeper',
       'new(): created handle');


## DESTROY()

eval {
    $zkh->DESTROY('foo');
};
like($@, qr/Usage: Net::ZooKeeper::DESTROY\(zkh\)/,
     'DESTROY(): too many arguments');

my $bad_zkh = {};
$bad_zkh = bless($bad_zkh, 'Net::ZooKeeper');

my $ret = $bad_zkh->DESTROY();
ok(!$ret,
   'DESTROY(): no action on invalid handle');


## add_auth()

eval {
    $zkh->add_auth();
};
like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/,
     'add_auth(): no scheme specified');

eval {
    $zkh->add_auth('foo');
};
like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/,
     'add_auth(): no certificate specified');

eval {
    $zkh->add_auth('foo', 'foo', 'bar');
};
like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/,
     'add_auth(): too many arguments');

eval {
    $bad_zkh->add_auth('foo', 'foo');
};
like($@, qr/invalid handle/,
     'add_auth(): invalid handle');

eval {
    Net::ZooKeeper::add_auth(1, 'foo', 'foo');
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'add_auth(): invalid hash reference');


## create()

eval {
    $zkh->create();
};
like($@, qr/Usage: Net::ZooKeeper::create\(zkh, path, buf, \.\.\.\)/,
     'create(): no path specified');

eval {
    $zkh->create($node_path);
};
like($@, qr/Usage: Net::ZooKeeper::create\(zkh, path, buf, \.\.\.\)/,
     'create(): no data buffer specified');

eval {
    $zkh->create($node_path, 'foo', 'bar');
};
like($@, qr/invalid number of arguments/,
     'create(): invalid number of arguments');

eval {
    $zkh->create($node_path, 'foo', 'path_read_len' => -3);
};
like($@, qr/invalid path read length/,
     'create(): invalid path read length');

eval {
    $zkh->create($node_path, 'foo', 'path_read_len' => 1);
};
like($@, qr/invalid path read length/,
     'create(): invalid path read length');

eval {
    $zkh->create($node_path, 'foo', 'flags' => 15);
};
like($@, qr/invalid create flags/,
     'create(): invalid create flags');

eval {
    $zkh->create($node_path, 'foo', 'flags' => ZOO_EPHEMERAL, 'acl', 'foo');
};
like($@, qr/invalid ACL array reference/,
     'create(): invalid ACL array reference');

eval {
    $zkh->create($node_path, 'foo', 'acl', {});
};
like($@, qr/invalid ACL array reference/,
     'create(): invalid ACL array reference to hash');

eval {
    my @acl = ('foo', 'bar');
    $zkh->create($node_path, 'foo', 'acl', \@acl);
};
like($@, qr/invalid ACL entry hash reference/,
     'create(): invalid ACL entry hash reference');

eval {
    my @acl = ({ 'foo' => 'bar' });
    $zkh->create($node_path, 'foo', 'acl', \@acl);
};
like($@, qr/no ACL entry perms element/,
     'create(): no ACL entry perms element');

eval {
    my @acl = (
        {
            'perms'  => -1
        }
    );
    $zkh->create($node_path, 'foo', 'acl', \@acl);
};
like($@, qr/invalid ACL entry perms/,
     'create(): invalid ACL entry perms');

eval {
    my @acl = (
        {
            'perms'  => ZOO_PERM_ALL
        }
    );
    $zkh->create($node_path, 'foo', 'acl', \@acl);
};
like($@, qr/no ACL entry scheme element/,
     'create(): no ACL entry scheme element');

eval {
    my @acl = (
        {
            'perms'  => ZOO_PERM_ALL,
            'scheme' => 'foo'
        }
    );
    $zkh->create($node_path, 'foo', 'acl', \@acl);
};
like($@, qr/no ACL entry id element/,
     'create(): no ACL entry id element');

eval {
    my @acl = (
        {
            'perms'  => ZOO_PERM_ALL,
            'scheme' => 'foo',
            'id'     => 'bar'
        },
        'bar'
    );
    $zkh->create($node_path, 'foo', 'acl', \@acl);
};
like($@, qr/invalid ACL entry hash reference/,
     'create(): invalid second ACL entry hash reference');

eval {
    $bad_zkh->create($node_path, 'foo');
};
like($@, qr/invalid handle/,
     'create(): invalid handle');

eval {
    Net::ZooKeeper::create(1, $node_path, 'foo');
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'create(): invalid hash reference');


## delete()

eval {
    $zkh->delete();
};
like($@, qr/Usage: Net::ZooKeeper::delete\(zkh, path, \.\.\.\)/,
     'delete(): no path specified');

eval {
    $zkh->delete($node_path, 'bar');
};
like($@, qr/invalid number of arguments/,
     'delete(): invalid number of arguments');

eval {
    $zkh->delete($node_path, 'version' => -3);
};
like($@, qr/invalid version requirement/,
     'delete(): invalid version requirement');

eval {
    $bad_zkh->delete($node_path);
};
like($@, qr/invalid handle/,
     'delete(): invalid handle');

eval {
    Net::ZooKeeper::delete(1, $node_path);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'delete(): invalid hash reference');


## exists()

eval {
    $zkh->exists();
};
like($@, qr/Usage: Net::ZooKeeper::exists\(zkh, path, \.\.\.\)/,
     'exists(): no path specified');

eval {
    $zkh->exists($node_path, 'bar');
};
like($@, qr/invalid number of arguments/,
     'exists(): invalid number of arguments');

eval {
    $zkh->exists($node_path, 'watch', 'bar');
};
like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
     'exists(): invalid watch hash reference');

eval {
    $zkh->exists($node_path, 'watch', []);
};
like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
     'exists(): invalid watch hash reference to array');

eval {
    $zkh->exists($node_path, 'stat', 'bar');
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'exists(): invalid stat hash reference');

eval {
    $zkh->exists($node_path, 'stat', []);
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'exists(): invalid stat hash reference');

eval {
    $bad_zkh->exists($node_path);
};
like($@, qr/invalid handle/,
     'exists(): invalid handle');

eval {
    Net::ZooKeeper::exists(1, $node_path);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'exists(): invalid hash reference');


## get_children()

eval {
    $zkh->get_children();
};
like($@, qr/Usage: Net::ZooKeeper::get_children\(zkh, path, \.\.\.\)/,
     'get_children(): no path specified');

eval {
    $zkh->get_children($node_path, 'bar');
};
like($@, qr/invalid number of arguments/,
     'get_children(): invalid number of arguments');

eval {
    $zkh->get_children($node_path, 'watch', 'bar');
};
like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
     'get_children(): invalid watch hash reference');

eval {
    $zkh->get_children($node_path, 'watch', []);
};
like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
     'get_children(): invalid watch ash reference to array');

eval {
    $bad_zkh->get_children($node_path);
};
like($@, qr/invalid handle/,
     'get_children(): invalid handle');

eval {
    Net::ZooKeeper::get_children(1, $node_path);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'get_children(): invalid hash reference');


## get()

eval {
    $zkh->get();
};
like($@, qr/Usage: Net::ZooKeeper::get\(zkh, path, \.\.\.\)/,
     'get(): no path specified');

eval {
    $zkh->get($node_path, 'bar');
};
like($@, qr/invalid number of arguments/,
     'get(): invalid number of arguments');

eval {
    $zkh->get($node_path, 'data_read_len' => -3);
};
like($@, qr/invalid data read length/,
     'get(): invalid data read length');

eval {
    $zkh->get($node_path, 'data_read_len' => 10, 'watch', 'bar');
};
like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
     'get(): invalid watch hash reference');

eval {
    $zkh->get($node_path, 'watch', []);
};
like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/,
     'get(): invalid watch hash reference to array');

eval {
    $zkh->get($node_path, 'stat', 'bar');
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'get(): invalid stat hash reference');

eval {
    $zkh->get($node_path, 'stat', []);
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'get(): invalid stat hash reference');

eval {
    $bad_zkh->get($node_path);
};
like($@, qr/invalid handle/,
     'get(): invalid handle');

eval {
    Net::ZooKeeper::get(1, $node_path);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'get(): invalid hash reference');


## set()

eval {
    $zkh->set();
};
like($@, qr/Usage: Net::ZooKeeper::set\(zkh, path, buf, \.\.\.\)/,
     'set(): no path specified');

eval {
    $zkh->set($node_path);
};
like($@, qr/Usage: Net::ZooKeeper::set\(zkh, path, buf, \.\.\.\)/,
     'set(): no data buffer specified');

eval {
    $zkh->set($node_path, 'foo', 'bar');
};
like($@, qr/invalid number of arguments/,
     'set(): invalid number of arguments');

eval {
    $zkh->set($node_path, 'foo', 'version' => -3);
};
like($@, qr/invalid version requirement/,
     'set(): invalid version requirement');

eval {
    $zkh->set($node_path, 'foo', 'version', 0, 'stat', 'bar');
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'set(): invalid stat hash reference');

eval {
    $zkh->set($node_path, 'foo', 'stat', []);
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'set(): invalid stat hash reference');

eval {
    $bad_zkh->set($node_path, 'foo');
};
like($@, qr/invalid handle/,
     'set(): invalid handle');

eval {
    Net::ZooKeeper::set(1, $node_path, 'foo');
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'set(): invalid hash reference');


## get_acl()

eval {
    $zkh->get_acl();
};
like($@, qr/Usage: Net::ZooKeeper::get_acl\(zkh, path, \.\.\.\)/,
     'get_acl(): no path specified');

eval {
    $zkh->get_acl($node_path, 'bar');
};
like($@, qr/invalid number of arguments/,
     'get_acl(): invalid number of arguments');

eval {
    $zkh->get_acl($node_path, 'stat', 'bar');
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'get_acl(): invalid stat hash reference');

eval {
    $zkh->get_acl($node_path, 'stat', []);
};
like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/,
     'get_acl(): invalid stat hash reference');

eval {
    $bad_zkh->get_acl($node_path);
};
like($@, qr/invalid handle/,
     'get_acl(): invalid handle');

eval {
    Net::ZooKeeper::get_acl(1, $node_path);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'get_acl(): invalid hash reference');


## set_acl()

eval {
    $zkh->set_acl();
};
like($@, qr/Usage: Net::ZooKeeper::set_acl\(zkh, path, acl_arr, \.\.\.\)/,
     'set_acl(): no path specified');

eval {
    $zkh->set_acl($node_path);
};
like($@, qr/Usage: Net::ZooKeeper::set_acl\(zkh, path, acl_arr, \.\.\.\)/,
     'set_acl(): no data buffer specified');

eval {
    $zkh->set_acl($node_path, 'foo');
};
like($@, qr/acl_arr is not an array reference/i,
     'set_acl(): invalid ACL array reference');

eval {
    $zkh->set_acl($node_path, {});
};
like($@, qr/acl_arr is not an array reference/i,
     'set_acl(): invalid ACL array reference to hash');

eval {
    my @acl = ('foo', 'bar');
    $zkh->set_acl($node_path, \@acl);
};
like($@, qr/invalid ACL entry hash reference/,
     'set_acl(): invalid ACL entry hash reference');

eval {
    my @acl = ({ 'foo' => 'bar' });
    $zkh->set_acl($node_path, \@acl);
};
like($@, qr/no ACL entry perms element/,
     'set_acl(): no ACL entry perms element');

eval {
    my @acl = (
        {
            'perms'  => -1
        }
    );
    $zkh->set_acl($node_path, \@acl);
};
like($@, qr/invalid ACL entry perms/,
     'set_acl(): invalid ACL entry perms');

eval {
    my @acl = (
        {
            'perms'  => ZOO_PERM_ALL
        }
    );
    $zkh->set_acl($node_path, \@acl);
};
like($@, qr/no ACL entry scheme element/,
     'set_acl(): no ACL entry scheme element');

eval {
    my @acl = (
        {
            'perms'  => ZOO_PERM_ALL,
            'scheme' => 'foo'
        }
    );
    $zkh->set_acl($node_path, \@acl);
};
like($@, qr/no ACL entry id element/,
     'set_acl(): no ACL entry id element');

eval {
    my @acl = (
        {
            'perms'  => ZOO_PERM_ALL,
            'scheme' => 'foo',
            'id'     => 'bar'
        },
        'bar'
    );
    $zkh->set_acl($node_path, \@acl);
};
like($@, qr/invalid ACL entry hash reference/,
     'set_acl(): invalid second ACL entry hash reference');

eval {
    $zkh->set_acl($node_path, [], 'bar');
};
like($@, qr/invalid number of arguments/,
     'set_acl(): invalid number of arguments');

eval {
    $zkh->set_acl($node_path, [], 'version' => -3);
};
like($@, qr/invalid version requirement/,
     'set_acl(): invalid version requirement');

eval {
    $bad_zkh->set_acl($node_path, []);
};
like($@, qr/invalid handle/,
     'set_acl(): invalid handle');

eval {
    Net::ZooKeeper::set_acl(1, $node_path, []);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'set_acl(): invalid hash reference');


## stat()

eval {
    $zkh->stat('bar');
};
like($@, qr/Usage: Net::ZooKeeper::stat\(zkh\)/,
     'stat(): too many arguments');

eval {
    $bad_zkh->stat();
};
like($@, qr/invalid handle/,
     'stat(): invalid handle');

eval {
    Net::ZooKeeper::stat(1);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'stat(): invalid hash reference');

my $stat = $zkh->stat();
isa_ok($stat, 'Net::ZooKeeper::Stat',
       'stat(): created stat handle');


## stat DESTROY()

eval {
    $stat->DESTROY('foo');
};
like($@, qr/Usage: Net::ZooKeeper::Stat::DESTROY\(zksh\)/,
     'stat DESTROY(): too many arguments');

my $bad_stat = {};
$bad_stat = bless($bad_stat, 'Net::ZooKeeper::Stat');

$ret = $bad_stat->DESTROY();
ok(!$ret,
   'stat DESTROY(): no action on invalid handle');


## watch()

eval {
    $zkh->watch('bar');
};
like($@, qr/invalid number of arguments/,
     'watch(): invalid number of arguments');

eval {
    $bad_zkh->watch();
};
like($@, qr/invalid handle/,
     'watch(): invalid handle');

eval {
    Net::ZooKeeper::watch(1);
};
like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/,
     'watch(): invalid hash reference');

my $watch = $zkh->watch();
isa_ok($watch, 'Net::ZooKeeper::Watch',
       'watch(): created watch handle');


## watch DESTROY()

eval {
    $watch->DESTROY('foo');
};
like($@, qr/Usage: Net::ZooKeeper::Watch::DESTROY\(zkwh\)/,
     'watch DESTROY(): too many arguments');

my $bad_watch = {};
$bad_watch = bless($bad_watch, 'Net::ZooKeeper::Watch');

$ret = $bad_watch->DESTROY();
ok(!$ret,
   'watch DESTROY(): no action on invalid handle');


## wait()

eval {
    $watch->wait('bar');
};
like($@, qr/invalid number of arguments/,
     'wait(): invalid number of arguments');

eval {
    $bad_watch->wait();
};
like($@, qr/invalid handle/,
     'wait(): invalid watch handle');

eval {
    Net::ZooKeeper::Watch::wait(1);
};
like($@, qr/zkwh is not a hash reference of type Net::ZooKeeper::Watch/,
     'wait(): invalid watch hash reference');


## set_log_level()

eval {
    my $f = \&Net::ZooKeeper::set_log_level;
    &$f();
};
like($@, qr/Usage: Net::ZooKeeper::set_log_level\(level\)/,
     'set_log_level(): no level specified');

eval {
    my $f = \&Net::ZooKeeper::set_log_level;
    &$f(ZOO_LOG_LEVEL_OFF, 'foo');
};
like($@, qr/Usage: Net::ZooKeeper::set_log_level\(level\)/,
     'set_log_level(): too many arguments');

eval {
    Net::ZooKeeper::set_log_level((ZOO_LOG_LEVEL_OFF) - 1);
};
like($@, qr/invalid log level/,
     'set_log_level(): invalid low log level');

eval {
    Net::ZooKeeper::set_log_level((ZOO_LOG_LEVEL_DEBUG) + 1);
};
like($@, qr/invalid log level/,
     'set_log_level(): invalid high log level');


## set_deterministic_conn_order()

eval {
    my $f = \&Net::ZooKeeper::set_deterministic_conn_order;
    &$f();
};
like($@, qr/Usage: Net::ZooKeeper::set_deterministic_conn_order\(flag\)/,
     'set_deterministic_conn_order(): no flag specified');

eval {
    my $f = \&Net::ZooKeeper::set_deterministic_conn_order;
    &$f(1, 'foo');
};
like($@, qr/Usage: Net::ZooKeeper::set_deterministic_conn_order\(flag\)/,
     'set_deterministic_conn_order(): too many arguments');

