blob: f17d1cdef7cc857cc60a0a59a957a5510ffead8f [file] [log] [blame]
use strict;
use Test::More 'no_plan';
### use && import ###
BEGIN {
use_ok( 'Params::Check' );
Params::Check->import(qw|check last_error allow|);
}
### verbose is good for debugging ###
$Params::Check::VERBOSE = $Params::Check::VERBOSE = $ARGV[0] ? 1 : 0;
### basic things first, allow function ###
use constant FALSE => sub { 0 };
use constant TRUE => sub { 1 };
### allow tests ###
{ ok( allow( 42, qr/^\d+$/ ), "Allow based on regex" );
ok( allow( $0, $0), " Allow based on string" );
ok( allow( 42, [0,42] ), " Allow based on list" );
ok( allow( 42, [50,sub{1}])," Allow based on list containing sub");
ok( allow( 42, TRUE ), " Allow based on constant sub" );
ok(!allow( $0, qr/^\d+$/ ), "Disallowing based on regex" );
ok(!allow( 42, $0 ), " Disallowing based on string" );
ok(!allow( 42, [0,$0] ), " Disallowing based on list" );
ok(!allow( 42, [50,sub{0}])," Disallowing based on list containing sub");
ok(!allow( 42, FALSE ), " Disallowing based on constant sub" );
### check that allow short circuits where required
{ my $sub_called;
allow( 1, [ 1, sub { $sub_called++ } ] );
ok( !$sub_called, "Allow short-circuits properly" );
}
### check if the subs for allow get what you expect ###
for my $thing (1,'foo',[1]) {
allow( $thing,
sub { is_deeply(+shift,$thing, "Allow coderef gets proper args") }
);
}
}
### default tests ###
{
my $tmpl = {
foo => { default => 1 }
};
### empty args first ###
{ my $args = check( $tmpl, {} );
ok( $args, "check() call with empty args" );
is( $args->{'foo'}, 1, " got default value" );
}
### now provide an alternate value ###
{ my $try = { foo => 2 };
my $args = check( $tmpl, $try );
ok( $args, "check() call with defined args" );
is_deeply( $args, $try, " found provided value in rv" );
}
### now provide a different case ###
{ my $try = { FOO => 2 };
my $args = check( $tmpl, $try );
ok( $args, "check() call with alternate case" );
is( $args->{foo}, 2, " found provided value in rv" );
}
### now see if we can strip leading dashes ###
{ local $Params::Check::STRIP_LEADING_DASHES = 1;
my $try = { -foo => 2 };
my $get = { foo => 2 };
my $args = check( $tmpl, $try );
ok( $args, "check() call with leading dashes" );
is_deeply( $args, $get, " found provided value in rv" );
}
}
### preserve case tests ###
{ my $tmpl = { Foo => { default => 1 } };
for (1,0) {
local $Params::Check::PRESERVE_CASE = $_;
my $expect = $_ ? { Foo => 42 } : { Foo => 1 };
my $rv = check( $tmpl, { Foo => 42 } );
ok( $rv, "check() call using PRESERVE_CASE: $_" );
is_deeply($rv, $expect, " found provided value in rv" );
}
}
### unknown tests ###
{
### disallow unknowns ###
{
my $rv = check( {}, { foo => 42 } );
is_deeply( $rv, {}, "check() call with unknown arguments" );
like( last_error(), qr/^Key 'foo' is not a valid key/,
" warning recorded ok" );
}
### allow unknown ###
{
local $Params::Check::ALLOW_UNKNOWN = 1;
my $rv = check( {}, { foo => 42 } );
is_deeply( $rv, { foo => 42 },
"check call() with unknown args allowed" );
}
}
### store tests ###
{ my $foo;
my $tmpl = {
foo => { store => \$foo }
};
### with/without store duplicates ###
for( 1, 0 ) {
local $Params::Check::NO_DUPLICATES = $_;
my $expect = $_ ? undef : 42;
my $rv = check( $tmpl, { foo => 42 } );
ok( $rv, "check() call with store key, no_dup: $_" );
is( $foo, 42, " found provided value in variable" );
is( $rv->{foo}, $expect, " found provided value in variable" );
}
}
### no_override tests ###
{ my $tmpl = {
foo => { no_override => 1, default => 42 },
};
my $rv = check( $tmpl, { foo => 13 } );
ok( $rv, "check() call with no_override key" );
is( $rv->{'foo'}, 42, " found default value in rv" );
like( last_error(), qr/^You are not allowed to override key/,
" warning recorded ok" );
}
### strict_type tests ###
{ my @list = (
[ { strict_type => 1, default => [] }, 0 ],
[ { default => [] }, 1 ],
);
### check for strict_type global, and in the template key ###
for my $aref (@list) {
my $tmpl = { foo => $aref->[0] };
local $Params::Check::STRICT_TYPE = $aref->[1];
### proper value ###
{ my $rv = check( $tmpl, { foo => [] } );
ok( $rv, "check() call with strict_type enabled" );
is( ref $rv->{foo}, 'ARRAY',
" found provided value in rv" );
}
### improper value ###
{ my $rv = check( $tmpl, { foo => {} } );
ok( !$rv, "check() call with strict_type violated" );
like( last_error(), qr/^Key 'foo' needs to be of type 'ARRAY'/,
" warning recorded ok" );
}
}
}
### required tests ###
{ my $tmpl = {
foo => { required => 1 }
};
### required value provided ###
{ my $rv = check( $tmpl, { foo => 42 } );
ok( $rv, "check() call with required key" );
is( $rv->{foo}, 42, " found provided value in rv" );
}
### required value omitted ###
{ my $rv = check( $tmpl, { } );
ok( !$rv, "check() call with required key omitted" );
like( last_error, qr/^Required option 'foo' is not provided/,
" warning recorded ok" );
}
}
### defined tests ###
{ my @list = (
[ { defined => 1, default => 1 }, 0 ],
[ { default => 1 }, 1 ],
);
### check for strict_type global, and in the template key ###
for my $aref (@list) {
my $tmpl = { foo => $aref->[0] };
local $Params::Check::ONLY_ALLOW_DEFINED = $aref->[1];
### value provided defined ###
{ my $rv = check( $tmpl, { foo => 42 } );
ok( $rv, "check() call with defined key" );
is( $rv->{foo}, 42, " found provided value in rv" );
}
### value provided undefined ###
{ my $rv = check( $tmpl, { foo => undef } );
ok( !$rv, "check() call with defined key undefined" );
like( last_error, qr/^Key 'foo' must be defined when passed/,
" warning recorded ok" );
}
}
}
### check + allow tests ###
{ ### check if the subs for allow get what you expect ###
for my $thing (1,'foo',[1]) {
my $tmpl = {
foo => { allow =>
sub { is_deeply(+shift,$thing,
" Allow coderef gets proper args") }
}
};
my $rv = check( $tmpl, { foo => $thing } );
ok( $rv, "check() call using allow key" );
}
}
### invalid key tests
{ my $tmpl = { foo => { allow => sub { 0 } } };
for my $val ( 1, 'foo', [], bless({},__PACKAGE__) ) {
my $rv = check( $tmpl, { foo => $val } );
my $text = "Key 'foo' ($val) is of invalid type";
my $re = quotemeta $text;
ok(!$rv, "check() fails with unallowed value" );
like(last_error(), qr/$re/, " $text" );
}
}
### warnings [rt.cpan.org #69626]
{
local $Params::Check::WARNINGS_FATAL = 1;
eval { check() };
ok( $@, "Call dies with fatal toggled" );
like( $@, qr/expects two arguments/,
" error stored ok" );
}
### warnings fatal test
{ my $tmpl = { foo => { allow => sub { 0 } } };
local $Params::Check::WARNINGS_FATAL = 1;
eval { check( $tmpl, { foo => 1 } ) };
ok( $@, "Call dies with fatal toggled" );
like( $@, qr/invalid type/,
" error stored ok" );
}
### store => \$foo tests
{ ### quell warnings
local $SIG{__WARN__} = sub {};
my $tmpl = { foo => { store => '' } };
check( $tmpl, {} );
my $re = quotemeta q|Store variable for 'foo' is not a reference!|;
like(last_error(), qr/$re/, "Caught non-reference 'store' variable" );
}
### edge case tests ###
{ ### if key is not provided, and value is '', will P::C treat
### that correctly?
my $tmpl = { foo => { default => '' } };
my $rv = check( $tmpl, {} );
ok( $rv, "check() call with default = ''" );
ok( exists $rv->{foo}, " rv exists" );
ok( defined $rv->{foo}, " rv defined" );
ok( !$rv->{foo}, " rv false" );
is( $rv->{foo}, '', " rv = '' " );
}
### big template test ###
{
my $lastname;
### the template to check against ###
my $tmpl = {
firstname => { required => 1, defined => 1 },
lastname => { required => 1, store => \$lastname },
gender => { required => 1,
allow => [qr/M/i, qr/F/i],
},
married => { allow => [0,1] },
age => { default => 21,
allow => qr/^\d+$/,
},
id_list => { default => [],
strict_type => 1
},
phone => { allow => sub { 1 if +shift } },
bureau => { default => 'NSA',
no_override => 1
},
};
### the args to send ###
my $try = {
firstname => 'joe',
lastname => 'jackson',
gender => 'M',
married => 1,
age => 21,
id_list => [1..3],
phone => '555-8844',
};
### the rv we expect ###
my $get = { %$try, bureau => 'NSA' };
my $rv = check( $tmpl, $try );
ok( $rv, "elaborate check() call" );
is_deeply( $rv, $get, " found provided values in rv" );
is( $rv->{lastname}, $lastname,
" found provided values in rv" );
}
### $Params::Check::CALLER_DEPTH test
{
sub wrapper { check ( @_ ) };
sub inner { wrapper( @_ ) };
sub outer { inner ( @_ ) };
outer( { dummy => { required => 1 }}, {} );
like( last_error, qr/for .*::wrapper by .*::inner$/,
"wrong caller without CALLER_DEPTH" );
local $Params::Check::CALLER_DEPTH = 1;
outer( { dummy => { required => 1 }}, {} );
like( last_error, qr/for .*::inner by .*::outer$/,
"right caller with CALLER_DEPTH" );
}
### test: #23824: Bug concerning the loss of the last_error
### message when checking recursively.
{ ok( 1, "Test last_error() on recursive check() call" );
### allow sub to call
my $clear = sub { check( {}, {} ) if shift; 1; };
### recursively call check() or not?
for my $recurse ( 0, 1 ) {
check(
{ a => { defined => 1 },
b => { allow => sub { $clear->( $recurse ) } },
},
{ a => undef, b => undef }
);
ok( last_error(), " last_error() with recurse: $recurse" );
}
}