xref: /openbsd-src/gnu/usr.bin/perl/cpan/Params-Check/t/01_Params-Check.t (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
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