Validate: improved arrayref-schema semantics

This allows all built-in options to be duplicated inside a single
schema, the semantics of which are the same as the kind of merging
done as part of inheriting options from custom validations.

This also causes all 'keys' and 'values' validation schemas to be
merged, which changes error messages a bit but is great for
introspection. Probably slightly improves performance as well.
This commit is contained in:
Yorhel 2025-03-14 14:28:26 +01:00
parent cea691dd55
commit fa24ca53e3
2 changed files with 114 additions and 116 deletions

View file

@ -38,8 +38,8 @@ sub t($schema, $input, $output) {
my $schema_copy = dclone([$schema])->[0];
my $input_copy = dclone([$input])->[0];
#diag explain FU::Validate->compile($schema, \%validations) if $line == 95;
my $res = FU::Validate->compile($schema, \%validations)->validate($input);
#diag explain FU::Validate->compile($schema, \%validations) if $line == 98;
is_deeply $schema, $schema_copy, "schema modification $line";
is_deeply $input, $input_copy, "input modification $line";
is_deeply $res, $output, "data ok $line";
@ -51,8 +51,8 @@ sub f($schema, $input, $error, @msg) {
my $schema_copy = dclone([$schema])->[0];
my $input_copy = dclone([$input])->[0];
#diag explain FU::Validate->compile($schema, \%validations) if $line == 176;
ok !eval { FU::Validate->compile($schema, \%validations)->validate($input); 1 }, "eval $line";
#diag explain FU::Validate->compile($schema, \%validations) if $line == 162;
is_deeply $schema, $schema_copy, "schema modification $line";
is_deeply $input, $input_copy, "input modification $line";
delete $@->{longmess};
@ -67,6 +67,7 @@ f {}, '', { validation => 'required' }, 'required value missing';
f {}, undef, { validation => 'required' }, 'required value missing';
t { default => undef }, undef, undef;
t { default => undef }, '', undef;
f { default => \'required' }, '', { validation => 'required' }, 'required value missing';
t { defaultsub1 => 1 }, undef, 2;
t { defaultsub2 => 1 }, undef, '';
t { defaultsub2 => 1 }, '', 1;
@ -115,6 +116,10 @@ t { type => 'hash', keys => { a=>{} }, unknown => 'pass' }, { a=>1,b=>1 }, { a=>
t { type => 'hash', setundef => 1 }, {}, undef;
t { type => 'hash', unknown => 'reject', keys => { a=>{ type => 'any', setundef => 1}} }, {a=>[]}, {a=>undef};
t [ keys => { a => {} }, keys => { b => {} } ], {a=>1, b=>2}, {a=>1, b=>2};
f [ keys => { a => {} }, keys => { b => {} } ], {a=>1}, { validation => 'keys', errors => [{ key => 'b', validation => 'required' }] }, '.b: required value missing';
f [ keys => { a => {} }, keys => { a => { int => 1 } } ], {a=>'abc'}, { validation => 'keys', errors => [{ key => 'a', validation => 'int', got => 'abc' }] }, ".a: failed validation 'int'";
# default validations
f { minlength => 3 }, 'ab', { validation => 'minlength', expected => 3, got => 2 }, "failed validation 'minlength'";
t { minlength => 3 }, 'abc', 'abc';
@ -171,9 +176,9 @@ t { collapsews => 1 }, ' x ', ' x ';
t { collapsews => 1, trim => 1 }, ' x ', 'x';
f { person => 1 }, 1, { validation => 'type', expected => 'hash', got => 'scalar' }, "invalid type, expected 'hash' but got 'scalar'";
t { person => 1, default => 1 }, undef, 1;
f { person => 1 }, { sex => 1 }, { validation => 'person', error => { validation => 'keys', errors => [{ key => 'name', validation => 'required' }] } }, "validation 'person'.name: required value missing";
f { person => 1 }, { sex => 1 }, { validation => 'keys', errors => [{ key => 'name', validation => 'required' }] }, ".name: required value missing";
t { person => 1 }, { sex => undef, name => 'y' }, { sex => 1, name => 'y' };
f { person => 1, keys => {age => {default => \'required'}} }, {name => 'x', sex => 'y'}, { validation => 'keys', errors => [{ key => 'age', validation => 'required' }] }, '.age: required value missing';
f { person => 1, keys => {age => {missing => 'reject'}} }, {name => 'x', sex => 'y'}, { key => 'age', validation => 'missing' }, '.age: required key missing';
t { person => 1, keys => {extra => {}} }, {name => 'x', sex => 'y', extra => 1}, { name => 'x', sex => 'y', extra => 1 };
f { person => 1, keys => {extra => {}} }, {name => 'x', sex => 'y', extra => ''}, { validation => 'keys', errors => [{ key => 'extra', validation => 'required' }] }, '.extra: required value missing';
t { person => 1 }, {name => 'x', sex => 'y', extra => 1}, {name => 'x', sex => 'y', extra => 1};