jhthorsen / json-validator

:cop: Validate data against a JSON schema
https://metacpan.org/release/JSON-Validator
56 stars 58 forks source link

Coercion fails in arrays with identical values #147

Closed NGEfreak closed 5 years ago

NGEfreak commented 5 years ago

Steps to reproduce the behavior

When an array contains identical scalar values only the first occurence of each value is coerced.

Here is a test script:

#!/usr/bin/env perl

use 5.010;
use strict;
use warnings;
use utf8;

use JSON::Validator qw(joi);
use Mojo::JSON qw(to_json);
use Test::More;

my $validator = JSON::Validator->new;
$validator->coerce( booleans => 1, numbers => 1, strings => 1 );

################################################################################

my $array_of_different_integers = [ '1', '2', '3' ];
$validator->validate( $array_of_different_integers, joi->array->items( joi->integer )->compile );
is to_json( $array_of_different_integers->[0] ), '1', 'different integers [0]';
is to_json( $array_of_different_integers->[1] ), '2', 'different integers [1]';
is to_json( $array_of_different_integers->[2] ), '3', 'different integers [2]';

my $array_with_identical_integers = [ '1', '1', '2', '2' ];
$validator->validate( $array_with_identical_integers, joi->array->items( joi->integer )->compile );
is to_json( $array_with_identical_integers->[0] ), '1', 'identical integers [0]';
is to_json( $array_with_identical_integers->[1] ), '1', 'identical integers [1]';
is to_json( $array_with_identical_integers->[2] ), '2', 'identical integers [2]';
is to_json( $array_with_identical_integers->[3] ), '2', 'identical integers [3]';

################################################################################

my $array_of_different_strings = [ 1, 2, 3 ];
$validator->validate( $array_of_different_strings, joi->array->items( joi->string )->compile );
is to_json( $array_of_different_strings->[0] ), '"1"', 'different strings [0]';
is to_json( $array_of_different_strings->[1] ), '"2"', 'different strings [1]';
is to_json( $array_of_different_strings->[2] ), '"3"', 'different strings [2]';

my $array_with_identical_strings = [ 1, 1, 2, 2 ];
$validator->validate( $array_with_identical_strings, joi->array->items( joi->string )->compile );
is to_json( $array_with_identical_strings->[0] ), '"1"', 'identical strings [0]';
is to_json( $array_with_identical_strings->[1] ), '"1"', 'identical strings [1]';
is to_json( $array_with_identical_strings->[2] ), '"2"', 'identical strings [2]';
is to_json( $array_with_identical_strings->[3] ), '"2"', 'identical strings [3]';

################################################################################

my $array_of_different_booleans = [ 1, 0 ];
$validator->validate( $array_of_different_booleans, joi->array->items( joi->boolean )->compile );
is to_json( $array_of_different_booleans->[0] ), 'true',  'different booleans [0]';
is to_json( $array_of_different_booleans->[1] ), 'false', 'different booleans [1]';

my $array_with_identical_booleans = [ 1, 1, 0, 0, 1 ];
$validator->validate( $array_with_identical_booleans, joi->array->items( joi->boolean )->compile );
is to_json( $array_with_identical_booleans->[0] ), 'true',  'identical booleans [0]';
is to_json( $array_with_identical_booleans->[1] ), 'true',  'identical booleans [1]';
is to_json( $array_with_identical_booleans->[2] ), 'false', 'identical booleans [2]';
is to_json( $array_with_identical_booleans->[3] ), 'false', 'identical booleans [3]';
is to_json( $array_with_identical_booleans->[4] ), 'true',  'identical booleans [4]';

################################################################################

my $schema = joi->array->items(
    joi->object->props(
        boolean1 => joi->boolean,
        boolean2 => joi->boolean,
        integer1 => joi->integer,
        integer2 => joi->integer,
        string1  => joi->string,
        string2  => joi->string,
    ),
)->compile;

my $array_of_objects = [ {
        boolean1 => 1,
        boolean2 => 1,
        integer1 => '1',
        integer2 => '1',
        string1  => 1,
        string2  => 1,
    }, {
        boolean1 => 1,
        boolean2 => 1,
        integer1 => '1',
        integer2 => '1',
        string1  => 1,
        string2  => 1,
    },
];

$validator->validate( $array_of_objects, $schema );

is to_json( $array_of_objects->[0]{boolean1} ), 'true', 'object boolean1 [0]';
is to_json( $array_of_objects->[0]{boolean2} ), 'true', 'object boolean2 [0]';
is to_json( $array_of_objects->[0]{integer1} ), '1',    'object integer1 [0]';
is to_json( $array_of_objects->[0]{integer2} ), '1',    'object integer2 [0]';
is to_json( $array_of_objects->[0]{string1} ),  '"1"',  'object string1 [0]';
is to_json( $array_of_objects->[0]{string2} ),  '"1"',  'object string2 [0]';
is to_json( $array_of_objects->[1]{boolean1} ), 'true', 'object boolean1 [1]';
is to_json( $array_of_objects->[1]{boolean2} ), 'true', 'object boolean2 [1]';
is to_json( $array_of_objects->[1]{integer1} ), '1',    'object integer1 [1]';
is to_json( $array_of_objects->[1]{integer2} ), '1',    'object integer2 [1]';
is to_json( $array_of_objects->[1]{string1} ),  '"1"',  'object string1 [1]';
is to_json( $array_of_objects->[1]{string2} ),  '"1"',  'object string2 [1]';

done_testing;

I get the following results running this script:

ok 1 - different integers [0]
ok 2 - different integers [1]
ok 3 - different integers [2]
ok 4 - identical integers [0]
not ok 5 - identical integers [1]
#   Failed test 'identical integers [1]'
#   at test2.pl line 26.
#          got: '"1"'
#     expected: '1'
ok 6 - identical integers [2]
not ok 7 - identical integers [3]
#   Failed test 'identical integers [3]'
#   at test2.pl line 28.
#          got: '"2"'
#     expected: '2'
ok 8 - different strings [0]
ok 9 - different strings [1]
ok 10 - different strings [2]
ok 11 - identical strings [0]
not ok 12 - identical strings [1]
#   Failed test 'identical strings [1]'
#   at test2.pl line 41.
#          got: '1'
#     expected: '"1"'
ok 13 - identical strings [2]
not ok 14 - identical strings [3]
#   Failed test 'identical strings [3]'
#   at test2.pl line 43.
#          got: '2'
#     expected: '"2"'
ok 15 - different booleans [0]
ok 16 - different booleans [1]
ok 17 - identical booleans [0]
not ok 18 - identical booleans [1]
#   Failed test 'identical booleans [1]'
#   at test2.pl line 55.
#          got: '1'
#     expected: 'true'
ok 19 - identical booleans [2]
not ok 20 - identical booleans [3]
#   Failed test 'identical booleans [3]'
#   at test2.pl line 57.
#          got: '0'
#     expected: 'false'
not ok 21 - identical booleans [4]
#   Failed test 'identical booleans [4]'
#   at test2.pl line 58.
#          got: '1'
#     expected: 'true'
ok 22 - object boolean1 [0]
ok 23 - object boolean2 [0]
ok 24 - object integer1 [0]
ok 25 - object integer2 [0]
ok 26 - object string1 [0]
ok 27 - object string2 [0]
not ok 28 - object boolean1 [1]
#   Failed test 'object boolean1 [1]'
#   at test2.pl line 98.
#          got: '1'
#     expected: 'true'
not ok 29 - object boolean2 [1]
#   Failed test 'object boolean2 [1]'
#   at test2.pl line 99.
#          got: '1'
#     expected: 'true'
not ok 30 - object integer1 [1]
#   Failed test 'object integer1 [1]'
#   at test2.pl line 100.
#          got: '"1"'
#     expected: '1'
not ok 31 - object integer2 [1]
#   Failed test 'object integer2 [1]'
#   at test2.pl line 101.
#          got: '"1"'
#     expected: '1'
not ok 32 - object string1 [1]
#   Failed test 'object string1 [1]'
#   at test2.pl line 102.
#          got: '1'
#     expected: '"1"'
not ok 33 - object string2 [1]
#   Failed test 'object string2 [1]'
#   at test2.pl line 103.
#          got: '1'
#     expected: '"1"'
1..33
# Looks like you failed 13 tests of 33.

Expected behavior

All values are coerced in arrays.

Actual behavior

Only the first occurence of each value is coerced in an array.

jhthorsen commented 5 years ago

Oh. That’s terrible. I think it might be because of the recursion guard inside _validate(). I’m a bit swamped this week, but I’ll try to prioritize it.

A PR is also welcomed ;)

jhthorsen commented 5 years ago

This should be fixed in 3.06.

Thanks for the bug report!