1b39c5158Smillertuse strict; 2b39c5158Smillertuse Test::More 'no_plan'; 3b39c5158Smillert 4b39c5158Smillert### use && import ### 5b39c5158SmillertBEGIN { 6b39c5158Smillert use_ok( 'Params::Check' ); 7b39c5158Smillert Params::Check->import(qw|check last_error allow|); 8b39c5158Smillert} 9b39c5158Smillert 10b39c5158Smillert### verbose is good for debugging ### 11b39c5158Smillert$Params::Check::VERBOSE = $Params::Check::VERBOSE = $ARGV[0] ? 1 : 0; 12b39c5158Smillert 13b39c5158Smillert### basic things first, allow function ### 14b39c5158Smillert 15b39c5158Smillertuse constant FALSE => sub { 0 }; 16b39c5158Smillertuse constant TRUE => sub { 1 }; 17b39c5158Smillert 18b39c5158Smillert### allow tests ### 19b39c5158Smillert{ ok( allow( 42, qr/^\d+$/ ), "Allow based on regex" ); 20b39c5158Smillert ok( allow( $0, $0), " Allow based on string" ); 21b39c5158Smillert ok( allow( 42, [0,42] ), " Allow based on list" ); 22b39c5158Smillert ok( allow( 42, [50,sub{1}])," Allow based on list containing sub"); 23b39c5158Smillert ok( allow( 42, TRUE ), " Allow based on constant sub" ); 24b39c5158Smillert ok(!allow( $0, qr/^\d+$/ ), "Disallowing based on regex" ); 25b39c5158Smillert ok(!allow( 42, $0 ), " Disallowing based on string" ); 26b39c5158Smillert ok(!allow( 42, [0,$0] ), " Disallowing based on list" ); 27b39c5158Smillert ok(!allow( 42, [50,sub{0}])," Disallowing based on list containing sub"); 28b39c5158Smillert ok(!allow( 42, FALSE ), " Disallowing based on constant sub" ); 29b39c5158Smillert 30b39c5158Smillert ### check that allow short circuits where required 31b39c5158Smillert { my $sub_called; 32b39c5158Smillert allow( 1, [ 1, sub { $sub_called++ } ] ); 33b39c5158Smillert ok( !$sub_called, "Allow short-circuits properly" ); 34b39c5158Smillert } 35b39c5158Smillert 36b39c5158Smillert ### check if the subs for allow get what you expect ### 37b39c5158Smillert for my $thing (1,'foo',[1]) { 38b39c5158Smillert allow( $thing, 39b39c5158Smillert sub { is_deeply(+shift,$thing, "Allow coderef gets proper args") } 40b39c5158Smillert ); 41b39c5158Smillert } 42b39c5158Smillert} 43b39c5158Smillert### default tests ### 44b39c5158Smillert{ 45b39c5158Smillert my $tmpl = { 46b39c5158Smillert foo => { default => 1 } 47b39c5158Smillert }; 48b39c5158Smillert 49b39c5158Smillert ### empty args first ### 50b39c5158Smillert { my $args = check( $tmpl, {} ); 51b39c5158Smillert 52b39c5158Smillert ok( $args, "check() call with empty args" ); 53b39c5158Smillert is( $args->{'foo'}, 1, " got default value" ); 54b39c5158Smillert } 55b39c5158Smillert 56b39c5158Smillert ### now provide an alternate value ### 57b39c5158Smillert { my $try = { foo => 2 }; 58b39c5158Smillert my $args = check( $tmpl, $try ); 59b39c5158Smillert 60b39c5158Smillert ok( $args, "check() call with defined args" ); 61b39c5158Smillert is_deeply( $args, $try, " found provided value in rv" ); 62b39c5158Smillert } 63b39c5158Smillert 64b39c5158Smillert ### now provide a different case ### 65b39c5158Smillert { my $try = { FOO => 2 }; 66b39c5158Smillert my $args = check( $tmpl, $try ); 67b39c5158Smillert ok( $args, "check() call with alternate case" ); 68b39c5158Smillert is( $args->{foo}, 2, " found provided value in rv" ); 69b39c5158Smillert } 70b39c5158Smillert 71b39c5158Smillert ### now see if we can strip leading dashes ### 72b39c5158Smillert { local $Params::Check::STRIP_LEADING_DASHES = 1; 73b39c5158Smillert my $try = { -foo => 2 }; 74b39c5158Smillert my $get = { foo => 2 }; 75b39c5158Smillert 76b39c5158Smillert my $args = check( $tmpl, $try ); 77b39c5158Smillert ok( $args, "check() call with leading dashes" ); 78b39c5158Smillert is_deeply( $args, $get, " found provided value in rv" ); 79b39c5158Smillert } 80b39c5158Smillert} 81b39c5158Smillert 82b39c5158Smillert### preserve case tests ### 83b39c5158Smillert{ my $tmpl = { Foo => { default => 1 } }; 84b39c5158Smillert 85b39c5158Smillert for (1,0) { 86b39c5158Smillert local $Params::Check::PRESERVE_CASE = $_; 87b39c5158Smillert 88b39c5158Smillert my $expect = $_ ? { Foo => 42 } : { Foo => 1 }; 89b39c5158Smillert 90b39c5158Smillert my $rv = check( $tmpl, { Foo => 42 } ); 91b39c5158Smillert ok( $rv, "check() call using PRESERVE_CASE: $_" ); 92b39c5158Smillert is_deeply($rv, $expect, " found provided value in rv" ); 93b39c5158Smillert } 94b39c5158Smillert} 95b39c5158Smillert 96b39c5158Smillert 97b39c5158Smillert### unknown tests ### 98b39c5158Smillert{ 99b39c5158Smillert ### disallow unknowns ### 100b39c5158Smillert { 101b39c5158Smillert my $rv = check( {}, { foo => 42 } ); 102b39c5158Smillert 103b39c5158Smillert is_deeply( $rv, {}, "check() call with unknown arguments" ); 104b39c5158Smillert like( last_error(), qr/^Key 'foo' is not a valid key/, 105b39c5158Smillert " warning recorded ok" ); 106b39c5158Smillert } 107b39c5158Smillert 108b39c5158Smillert ### allow unknown ### 109b39c5158Smillert { 110b39c5158Smillert local $Params::Check::ALLOW_UNKNOWN = 1; 111b39c5158Smillert my $rv = check( {}, { foo => 42 } ); 112b39c5158Smillert 113b39c5158Smillert is_deeply( $rv, { foo => 42 }, 114b39c5158Smillert "check call() with unknown args allowed" ); 115b39c5158Smillert } 116b39c5158Smillert} 117b39c5158Smillert 118b39c5158Smillert### store tests ### 119b39c5158Smillert{ my $foo; 120b39c5158Smillert my $tmpl = { 121b39c5158Smillert foo => { store => \$foo } 122b39c5158Smillert }; 123b39c5158Smillert 124b39c5158Smillert ### with/without store duplicates ### 125b39c5158Smillert for( 1, 0 ) { 126b39c5158Smillert local $Params::Check::NO_DUPLICATES = $_; 127b39c5158Smillert 128b39c5158Smillert my $expect = $_ ? undef : 42; 129b39c5158Smillert 130b39c5158Smillert my $rv = check( $tmpl, { foo => 42 } ); 131b39c5158Smillert ok( $rv, "check() call with store key, no_dup: $_" ); 132b39c5158Smillert is( $foo, 42, " found provided value in variable" ); 133b39c5158Smillert is( $rv->{foo}, $expect, " found provided value in variable" ); 134b39c5158Smillert } 135b39c5158Smillert} 136b39c5158Smillert 137b39c5158Smillert### no_override tests ### 138b39c5158Smillert{ my $tmpl = { 139b39c5158Smillert foo => { no_override => 1, default => 42 }, 140b39c5158Smillert }; 141b39c5158Smillert 142b39c5158Smillert my $rv = check( $tmpl, { foo => 13 } ); 143b39c5158Smillert ok( $rv, "check() call with no_override key" ); 144b39c5158Smillert is( $rv->{'foo'}, 42, " found default value in rv" ); 145b39c5158Smillert 146b39c5158Smillert like( last_error(), qr/^You are not allowed to override key/, 147b39c5158Smillert " warning recorded ok" ); 148b39c5158Smillert} 149b39c5158Smillert 150b39c5158Smillert### strict_type tests ### 151b39c5158Smillert{ my @list = ( 152b39c5158Smillert [ { strict_type => 1, default => [] }, 0 ], 153b39c5158Smillert [ { default => [] }, 1 ], 154b39c5158Smillert ); 155b39c5158Smillert 156b39c5158Smillert ### check for strict_type global, and in the template key ### 157b39c5158Smillert for my $aref (@list) { 158b39c5158Smillert 159b39c5158Smillert my $tmpl = { foo => $aref->[0] }; 160b39c5158Smillert local $Params::Check::STRICT_TYPE = $aref->[1]; 161b39c5158Smillert 162b39c5158Smillert ### proper value ### 163b39c5158Smillert { my $rv = check( $tmpl, { foo => [] } ); 164b39c5158Smillert ok( $rv, "check() call with strict_type enabled" ); 165b39c5158Smillert is( ref $rv->{foo}, 'ARRAY', 166b39c5158Smillert " found provided value in rv" ); 167b39c5158Smillert } 168b39c5158Smillert 169b39c5158Smillert ### improper value ### 170b39c5158Smillert { my $rv = check( $tmpl, { foo => {} } ); 171b39c5158Smillert ok( !$rv, "check() call with strict_type violated" ); 172b39c5158Smillert like( last_error(), qr/^Key 'foo' needs to be of type 'ARRAY'/, 173b39c5158Smillert " warning recorded ok" ); 174b39c5158Smillert } 175b39c5158Smillert } 176b39c5158Smillert} 177b39c5158Smillert 178b39c5158Smillert### required tests ### 179b39c5158Smillert{ my $tmpl = { 180b39c5158Smillert foo => { required => 1 } 181b39c5158Smillert }; 182b39c5158Smillert 183b39c5158Smillert ### required value provided ### 184b39c5158Smillert { my $rv = check( $tmpl, { foo => 42 } ); 185b39c5158Smillert ok( $rv, "check() call with required key" ); 186b39c5158Smillert is( $rv->{foo}, 42, " found provided value in rv" ); 187b39c5158Smillert } 188b39c5158Smillert 189b39c5158Smillert ### required value omitted ### 190b39c5158Smillert { my $rv = check( $tmpl, { } ); 191b39c5158Smillert ok( !$rv, "check() call with required key omitted" ); 192b39c5158Smillert like( last_error, qr/^Required option 'foo' is not provided/, 193b39c5158Smillert " warning recorded ok" ); 194b39c5158Smillert } 195b39c5158Smillert} 196b39c5158Smillert 197b39c5158Smillert### defined tests ### 198b39c5158Smillert{ my @list = ( 199b39c5158Smillert [ { defined => 1, default => 1 }, 0 ], 200b39c5158Smillert [ { default => 1 }, 1 ], 201b39c5158Smillert ); 202b39c5158Smillert 203b39c5158Smillert ### check for strict_type global, and in the template key ### 204b39c5158Smillert for my $aref (@list) { 205b39c5158Smillert 206b39c5158Smillert my $tmpl = { foo => $aref->[0] }; 207b39c5158Smillert local $Params::Check::ONLY_ALLOW_DEFINED = $aref->[1]; 208b39c5158Smillert 209b39c5158Smillert ### value provided defined ### 210b39c5158Smillert { my $rv = check( $tmpl, { foo => 42 } ); 211b39c5158Smillert ok( $rv, "check() call with defined key" ); 212b39c5158Smillert is( $rv->{foo}, 42, " found provided value in rv" ); 213b39c5158Smillert } 214b39c5158Smillert 215b39c5158Smillert ### value provided undefined ### 216b39c5158Smillert { my $rv = check( $tmpl, { foo => undef } ); 217b39c5158Smillert ok( !$rv, "check() call with defined key undefined" ); 218b39c5158Smillert like( last_error, qr/^Key 'foo' must be defined when passed/, 219b39c5158Smillert " warning recorded ok" ); 220b39c5158Smillert } 221b39c5158Smillert } 222b39c5158Smillert} 223b39c5158Smillert 224b39c5158Smillert### check + allow tests ### 225b39c5158Smillert{ ### check if the subs for allow get what you expect ### 226b39c5158Smillert for my $thing (1,'foo',[1]) { 227b39c5158Smillert my $tmpl = { 228b39c5158Smillert foo => { allow => 229b39c5158Smillert sub { is_deeply(+shift,$thing, 230b39c5158Smillert " Allow coderef gets proper args") } 231b39c5158Smillert } 232b39c5158Smillert }; 233b39c5158Smillert 234b39c5158Smillert my $rv = check( $tmpl, { foo => $thing } ); 235b39c5158Smillert ok( $rv, "check() call using allow key" ); 236b39c5158Smillert } 237b39c5158Smillert} 238b39c5158Smillert 239b39c5158Smillert### invalid key tests 240b39c5158Smillert{ my $tmpl = { foo => { allow => sub { 0 } } }; 241b39c5158Smillert 242b39c5158Smillert for my $val ( 1, 'foo', [], bless({},__PACKAGE__) ) { 243b39c5158Smillert my $rv = check( $tmpl, { foo => $val } ); 244b39c5158Smillert my $text = "Key 'foo' ($val) is of invalid type"; 245b39c5158Smillert my $re = quotemeta $text; 246b39c5158Smillert 247*898184e3Ssthen ok(!$rv, "check() fails with unallowed value" ); 248b39c5158Smillert like(last_error(), qr/$re/, " $text" ); 249b39c5158Smillert } 250b39c5158Smillert} 251b39c5158Smillert 252*898184e3Ssthen### warnings [rt.cpan.org #69626] 253*898184e3Ssthen{ 254*898184e3Ssthen local $Params::Check::WARNINGS_FATAL = 1; 255*898184e3Ssthen 256*898184e3Ssthen eval { check() }; 257*898184e3Ssthen 258*898184e3Ssthen ok( $@, "Call dies with fatal toggled" ); 259*898184e3Ssthen like( $@, qr/expects two arguments/, 260*898184e3Ssthen " error stored ok" ); 261*898184e3Ssthen} 262*898184e3Ssthen 263b39c5158Smillert### warnings fatal test 264b39c5158Smillert{ my $tmpl = { foo => { allow => sub { 0 } } }; 265b39c5158Smillert 266b39c5158Smillert local $Params::Check::WARNINGS_FATAL = 1; 267b39c5158Smillert 268b39c5158Smillert eval { check( $tmpl, { foo => 1 } ) }; 269b39c5158Smillert 270b39c5158Smillert ok( $@, "Call dies with fatal toggled" ); 271b39c5158Smillert like( $@, qr/invalid type/, 272b39c5158Smillert " error stored ok" ); 273b39c5158Smillert} 274b39c5158Smillert 275b39c5158Smillert### store => \$foo tests 276b39c5158Smillert{ ### quell warnings 277b39c5158Smillert local $SIG{__WARN__} = sub {}; 278b39c5158Smillert 279b39c5158Smillert my $tmpl = { foo => { store => '' } }; 280b39c5158Smillert check( $tmpl, {} ); 281b39c5158Smillert 282b39c5158Smillert my $re = quotemeta q|Store variable for 'foo' is not a reference!|; 283b39c5158Smillert like(last_error(), qr/$re/, "Caught non-reference 'store' variable" ); 284b39c5158Smillert} 285b39c5158Smillert 286b39c5158Smillert### edge case tests ### 287b39c5158Smillert{ ### if key is not provided, and value is '', will P::C treat 288b39c5158Smillert ### that correctly? 289b39c5158Smillert my $tmpl = { foo => { default => '' } }; 290b39c5158Smillert my $rv = check( $tmpl, {} ); 291b39c5158Smillert 292b39c5158Smillert ok( $rv, "check() call with default = ''" ); 293b39c5158Smillert ok( exists $rv->{foo}, " rv exists" ); 294b39c5158Smillert ok( defined $rv->{foo}, " rv defined" ); 295b39c5158Smillert ok( !$rv->{foo}, " rv false" ); 296b39c5158Smillert is( $rv->{foo}, '', " rv = '' " ); 297b39c5158Smillert} 298b39c5158Smillert 299b39c5158Smillert### big template test ### 300b39c5158Smillert{ 301b39c5158Smillert my $lastname; 302b39c5158Smillert 303b39c5158Smillert ### the template to check against ### 304b39c5158Smillert my $tmpl = { 305b39c5158Smillert firstname => { required => 1, defined => 1 }, 306b39c5158Smillert lastname => { required => 1, store => \$lastname }, 307b39c5158Smillert gender => { required => 1, 308b39c5158Smillert allow => [qr/M/i, qr/F/i], 309b39c5158Smillert }, 310b39c5158Smillert married => { allow => [0,1] }, 311b39c5158Smillert age => { default => 21, 312b39c5158Smillert allow => qr/^\d+$/, 313b39c5158Smillert }, 314b39c5158Smillert id_list => { default => [], 315b39c5158Smillert strict_type => 1 316b39c5158Smillert }, 317b39c5158Smillert phone => { allow => sub { 1 if +shift } }, 318b39c5158Smillert bureau => { default => 'NSA', 319b39c5158Smillert no_override => 1 320b39c5158Smillert }, 321b39c5158Smillert }; 322b39c5158Smillert 323b39c5158Smillert ### the args to send ### 324b39c5158Smillert my $try = { 325b39c5158Smillert firstname => 'joe', 326b39c5158Smillert lastname => 'jackson', 327b39c5158Smillert gender => 'M', 328b39c5158Smillert married => 1, 329b39c5158Smillert age => 21, 330b39c5158Smillert id_list => [1..3], 331b39c5158Smillert phone => '555-8844', 332b39c5158Smillert }; 333b39c5158Smillert 334b39c5158Smillert ### the rv we expect ### 335b39c5158Smillert my $get = { %$try, bureau => 'NSA' }; 336b39c5158Smillert 337b39c5158Smillert my $rv = check( $tmpl, $try ); 338b39c5158Smillert 339b39c5158Smillert ok( $rv, "elaborate check() call" ); 340b39c5158Smillert is_deeply( $rv, $get, " found provided values in rv" ); 341b39c5158Smillert is( $rv->{lastname}, $lastname, 342b39c5158Smillert " found provided values in rv" ); 343b39c5158Smillert} 344b39c5158Smillert 345b39c5158Smillert### $Params::Check::CALLER_DEPTH test 346b39c5158Smillert{ 347b39c5158Smillert sub wrapper { check ( @_ ) }; 348b39c5158Smillert sub inner { wrapper( @_ ) }; 349b39c5158Smillert sub outer { inner ( @_ ) }; 350b39c5158Smillert outer( { dummy => { required => 1 }}, {} ); 351b39c5158Smillert 352b39c5158Smillert like( last_error, qr/for .*::wrapper by .*::inner$/, 353b39c5158Smillert "wrong caller without CALLER_DEPTH" ); 354b39c5158Smillert 355b39c5158Smillert local $Params::Check::CALLER_DEPTH = 1; 356b39c5158Smillert outer( { dummy => { required => 1 }}, {} ); 357b39c5158Smillert 358b39c5158Smillert like( last_error, qr/for .*::inner by .*::outer$/, 359b39c5158Smillert "right caller with CALLER_DEPTH" ); 360b39c5158Smillert} 361b39c5158Smillert 362*898184e3Ssthen### test: #23824: Bug concerning the loss of the last_error 363b39c5158Smillert### message when checking recursively. 364b39c5158Smillert{ ok( 1, "Test last_error() on recursive check() call" ); 365b39c5158Smillert 366b39c5158Smillert ### allow sub to call 367b39c5158Smillert my $clear = sub { check( {}, {} ) if shift; 1; }; 368b39c5158Smillert 369b39c5158Smillert ### recursively call check() or not? 370b39c5158Smillert for my $recurse ( 0, 1 ) { 371b39c5158Smillert 372b39c5158Smillert check( 373b39c5158Smillert { a => { defined => 1 }, 374b39c5158Smillert b => { allow => sub { $clear->( $recurse ) } }, 375b39c5158Smillert }, 376b39c5158Smillert { a => undef, b => undef } 377b39c5158Smillert ); 378b39c5158Smillert 379b39c5158Smillert ok( last_error(), " last_error() with recurse: $recurse" ); 380b39c5158Smillert } 381b39c5158Smillert} 382b39c5158Smillert 383