jhthorsen / json-validator

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

Incorrect validation error paths when validating references to same object #244

Closed NGEfreak closed 2 years ago

NGEfreak commented 3 years ago

Steps to reproduce the behavior

The path values in validation errors are incorrect when a data structure contains references to the same object.

Here is a test script:

#!/usr/bin/env perl

use 5.016;
use strict;
use warnings;
use utf8;

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

my $schema = joi->object->props(
    array_of_strings => joi->array->items( joi->string ),
);

subtest different_array_refs => sub {
    my @errors = $schema->validate( { array_of_strings => [ [], [], [], [] ] } );
    is $errors[0]->path, '/array_of_strings/0';
    is $errors[1]->path, '/array_of_strings/1';
    is $errors[2]->path, '/array_of_strings/2';
    is $errors[3]->path, '/array_of_strings/3';
};

subtest same_array_ref => sub {
    my $ref    = [];
    my @errors = $schema->validate( { array_of_strings => [ $ref, $ref, $ref, $ref ] } );
    is $errors[0]->path, '/array_of_strings/0';
    is $errors[1]->path, '/array_of_strings/1';
    is $errors[2]->path, '/array_of_strings/2';
    is $errors[3]->path, '/array_of_strings/3';
};

subtest same_boolean_ref => sub {
    my @errors = $schema->validate( { array_of_strings => [ true, true, true, true ] } );
    is $errors[0]->path, '/array_of_strings/0';
    is $errors[1]->path, '/array_of_strings/1';
    is $errors[2]->path, '/array_of_strings/2';
    is $errors[3]->path, '/array_of_strings/3';
};

done_testing;

# Output:
#
# # Subtest: different_array_refs
#     ok 1
#     ok 2
#     ok 3
#     ok 4
#     1..4
# ok 1 - different_array_refs
# # Subtest: same_array_ref
#     ok 1
#     not ok 2
#     #   Failed test at t/json-validator.t line 28.
#     #          got: '/array_of_strings/0'
#     #     expected: '/array_of_strings/1'
#     not ok 3
#     #   Failed test at t/json-validator.t line 29.
#     #          got: '/array_of_strings/0'
#     #     expected: '/array_of_strings/2'
#     not ok 4
#     #   Failed test at t/json-validator.t line 30.
#     #          got: '/array_of_strings/0'
#     #     expected: '/array_of_strings/3'
#     1..4
#     # Looks like you failed 3 tests of 4.
# not ok 2 - same_array_ref
# #   Failed test 'same_array_ref'
# #   at t/json-validator.t line 31.
# # Subtest: same_boolean_ref
#     ok 1
#     not ok 2
#     #   Failed test at t/json-validator.t line 36.
#     #          got: '/array_of_strings/0'
#     #     expected: '/array_of_strings/1'
#     not ok 3
#     #   Failed test at t/json-validator.t line 37.
#     #          got: '/array_of_strings/0'
#     #     expected: '/array_of_strings/2'
#     not ok 4
#     #   Failed test at t/json-validator.t line 38.
#     #          got: '/array_of_strings/0'
#     #     expected: '/array_of_strings/3'
#     1..4
#     # Looks like you failed 3 tests of 4.
# not ok 3 - same_boolean_ref
# #   Failed test 'same_boolean_ref'
# #   at t/json-validator.t line 39.
# 1..3
# # Looks like you failed 2 tests of 3.

The Problem seems to be line 436 in JSON::Validator. Cloning the errors and overwritting the path fixes the problem:

# Avoid recursion
if ($self->{seen}{$seen_addr}) {
    my $errors_copy = Clone::clone($self->{seen}{$seen_addr});
    $_->path($path) for @{$errors_copy};
    return @{$errors_copy};
}

Expected behavior

All path values in validation errors are correct.

Actual behavior

Some path values in validation errors are incorrect.

jhthorsen commented 2 years ago

This is fixed in #251.