| 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" ); |
| } |
| } |
| |