JSON-Schema-Modern-0.609/000770 000766 000024 00000000000 15001302761 015164 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/Build.PL000644 000766 000024 00000000513 15001302761 016462 0ustar00etherstaff000000 000000 # This Build.PL for JSON-Schema-Modern was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.019. use strict; use warnings; if ("$]" < 5.038) { die "This distribution will not install where builtin::Backport exists.\n" if eval { +require builtin::Backport; 1 }; } use v5.20.0; use Module::Build::Tiny 0.034; Build_PL(); JSON-Schema-Modern-0.609/inc/000770 000766 000024 00000000000 15001302761 015735 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/INSTALL000644 000766 000024 00000004710 15001302761 016222 0ustar00etherstaff000000 000000 This is the Perl distribution JSON-Schema-Modern. Installing JSON-Schema-Modern is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm JSON::Schema::Modern If it does not have permission to install modules to the current perl, cpanm will automatically set up and install to a local::lib in your home directory. See the local::lib documentation (https://metacpan.org/pod/local::lib) for details on enabling it in your environment. ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan JSON::Schema::Modern ## Manual installation As a last resort, you can manually install it. If you have not already downloaded the release tarball, you can find the download link on the module's MetaCPAN page: https://metacpan.org/pod/JSON::Schema::Modern Untar the tarball, install configure prerequisites (see below), then build it: % perl Build.PL % ./Build && ./Build test Then install it: % ./Build install Or the more portable variation: % perl Build.PL % perl Build % perl Build test % perl Build install If your perl is system-managed, you can create a local::lib in your home directory to install modules to. For details, see the local::lib documentation: https://metacpan.org/pod/local::lib The prerequisites of this distribution will also have to be installed manually. The prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated by running the manual build process described above. ## Configure Prerequisites This distribution requires other modules to be installed before this distribution's installer can be run. They can be found under the "configure_requires" key of META.yml or the "{prereqs}{configure}{requires}" key of META.json. ## Other Prerequisites This distribution may require additional modules to be installed after running Build.PL. Look for prerequisites in the following phases: * to run ./Build, PHASE = build * to use the module code itself, PHASE = runtime * to run tests, PHASE = test They can all be found in the "PHASE_requires" key of MYMETA.yml or the "{prereqs}{PHASE}{requires}" key of MYMETA.json. ## Documentation JSON-Schema-Modern documentation is available as POD. You can run `perldoc` from a shell to read the documentation: % perldoc JSON::Schema::Modern For more information on installing Perl modules via CPAN, please see: https://www.cpan.org/modules/INSTALL.html JSON-Schema-Modern-0.609/CONTRIBUTING000644 000766 000024 00000007463 15001302761 017033 0ustar00etherstaff000000 000000 CONTRIBUTING Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. PLEASE NOTE that if you have any questions or difficulties, you can reach the maintainer(s) through the bug queue described later in this document (preferred), or by emailing the releaser directly. You are not required to follow any of the steps in this document to submit a patch or bug report; these are just recommendations, intended to help you (and help us help you faster). The distribution is managed with Dist::Zilla (https://metacpan.org/release/Dist-Zilla). This means than many of the usual files you might expect are not in the repository, but are generated at release time (e.g. Makefile.PL). However, you can run tests directly using the 'prove' tool: $ prove -l $ prove -lv t/some_test_file.t $ prove -lvr t/ In most cases, 'prove' is entirely sufficient for you to test any patches you have. You may need to satisfy some dependencies. The easiest way to satisfy dependencies is to install the last release -- this is available at https://metacpan.org/release/JSON-Schema-Modern If you use cpanminus, you can do it without downloading the tarball first: $ cpanm --reinstall --installdeps --with-recommends JSON::Schema::Modern Dist::Zilla is a very powerful authoring tool, but requires a number of author-specific plugins. If you would like to use it for contributing, install it from CPAN, then run one of the following commands, depending on your CPAN client: $ cpan `dzil authordeps --missing` or $ dzil authordeps --missing | cpanm You should then also install any additional requirements not needed by the dzil build but may be needed by tests or other development: $ cpan `dzil listdeps --author --missing` or $ dzil listdeps --author --missing | cpanm Or, you can use the 'dzil stale' command to install all requirements at once: $ cpan Dist::Zilla::App::Command::stale $ cpan `dzil stale --all` or $ cpanm Dist::Zilla::App::Command::stale $ dzil stale --all | cpanm You can also do this via cpanm directly: $ cpanm --reinstall --installdeps --with-develop --with-recommends JSON::Schema::Modern Once installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil test --release $ dzil xtest $ dzil listdeps --json $ dzil build --notgz You can learn more about Dist::Zilla at http://dzil.org/. The code for this distribution is hosted at GitHub. The repository is: https://github.com/karenetheridge/JSON-Schema-Modern You can submit code changes by forking the repository, pushing your code changes to your clone, and then submitting a pull request. Please include a suitable end-user-oriented entry in the Changes file describing your change. Detailed instructions for doing that is available here: https://help.github.com/articles/creating-a-pull-request Generated files such as README, CONTRIBUTING, Makefile.PL, LICENSE etc should *not* be included in your pull request, as they will be updated automatically during the next release. If you have found a bug, but do not have an accompanying patch to fix it, you can submit an issue report here: https://github.com/karenetheridge/JSON-Schema-Modern/issues This is a good place to send your questions about the usage of this distribution. If you send me a patch or pull request, your name and email address will be included in the documentation as a contributor (using the attribution on the commit or patch), unless you specifically request for it not to be. If you wish to be listed under a different name or address, you should submit a pull request to the .mailmap file to contain the correct mapping. This file was generated via Dist::Zilla::Plugin::GenerateFile::FromShareDir 0.015 from a template file originating in Dist-Zilla-PluginBundle-Author-ETHER-0.166. JSON-Schema-Modern-0.609/Changes000640 000766 000024 00000071603 15001302761 016465 0ustar00etherstaff000000 000000 Revision history for JSON-Schema-Modern 0.609 2025-04-20 23:40:09Z - validate_schema() (and the json-schema-eval --validate-schema option) now detect more errors 0.608 2025-04-11 20:57:16Z - documentation now makes a note of the special licence for JSON Schema schema files - documentation fixes for JSM and JSM::Document regarding handling of uris - introduction of 'original_uri' for JSON::Schema::Modern::Document, which document subclasses may wish to use for logic of their own after changing the canonical uri of the document 0.607 2025-04-01 19:35:50Z - now performing stricter email address validation for the 'email' and 'idn-email' formats - some improvements to the handling of unimplemented formats (only missing core formats are checked for at traverse time; custom formats can be added at any time and errors can be avoided with 'short-circuit') 0.606 2025-03-23 00:16:35Z - bump required version for optional module Data::Validate::Domain (used by the 'hostname' and 'idn-hostname' formats) 0.605 2025-03-09 16:31:05Z - update 'hostname' and 'idn-hostname' format validation to be more lax with respect to domain names; fixes some acceptance tests 0.604 2025-03-08 23:23:05Z - mark some failing format tests as todo that will be added in Test::JSON::Schema::Acceptance 1.028 0.603 2025-02-28 23:35:43Z - fix Sereal serialization hooks - various performance improvements when dealing with base URIs 0.602 2025-02-21 00:11:32Z - more checks at traverse and evaluation time for inconsistent results - fix Sereal deserialization hooks 0.601 2025-02-02 00:23:38Z - check for builtin::Backport at install time 0.600 2025-01-31 23:23:21Z BREAKING CHANGES: OpenAPI::Modern must be updated to 0.079 after installing this version. - the specification version can now be overridden when constructing Documents directly (rather than via the evaluator), if not specified by the "$schema" keyword - some fixes for $id handling in earlier drafts - rework of document management and URI resolution: $jsm->add_schema($uri, $schema) no longer sets the $uri in the document object, allowing the document to be added to the index under multiple URIs. Instead, we resolve the document's indexed resources against the base URI and only store the resolved form in the evaluator itself. This allows the same document to be reused with different base URIs if it only contains relative $ids internally. add_document($uri, $document) now resolves all URI resources against the provided URI, rather than adding document resources as-is and adding $uri as one more resource URI. - further restructuring of internal resource indexes, anchor management, and traversal of subschemas from embedded non-JSON Schema documents (e.g. OpenAPI documents) 0.599 2025-01-26 22:54:02Z - the boolean overload in JSON::Schema::Modern::Result is now deprecated: it will warn on use, and will be removed no sooner than 2026-02-01. 0.598 2025-01-19 18:30:32Z - fix format acceptance tests that would fail when some optional modules are not installed - fix some acceptance tests that used the wrong validate_formats mode (since v0.592) 0.597 2024-12-07 23:30:38Z - report installed version of builtin 0.596 2024-11-24 00:57:13Z - properly handle changing of dialects (including swapping out vocabularies and keywords) when evaluating a local subschema (without a $ref) - now collecting identifiers within contentSchema keywords as we do for any other subschema 0.595 2024-11-07 18:57:16Z - bump required version of builtin::compat 0.594 2024-11-03 17:01:42Z - improved handling of numeric type checking in draft4 - numeric type checking is relaxed, now allowing for dualvars arising from the simple case of using a number in string context or a string in numeric context (only a problem in perls <5.36) 0.593 2024-10-14 18:43:23Z - fix new integer type tests that fail on perls < 5.35.9 0.592 2024-10-13 19:43:20Z - support added for the draft4, draft6 specification versions 0.591 2024-10-06 21:06:15Z - new --dump-identifiers option in json-schema-eval executable - the document form of 'add_schema' has been extracted out into 'add_document', with the old form deprecated - is_equal() utility function, and the const and enum keywords, now provide more detailed error diagnostics - fix "strict" mode for draft7 documents 0.590 2024-09-07 00:48:50Z - "strict" mode is now recognized by validate_schema(), to report on any unrecognized keywords used in the schema being validated 0.589 2024-07-06 21:07:44Z - skip unpassable tests when ivsize < 8 0.588 2024-06-28 17:14:22Z - bump required version of Math::BigInt for bdiv, bmod fixes 0.587 2024-06-26 22:55:11Z - fix multipleOf test that fails on some peculiar architectures - document the use of Sereal for caching large evaluator objects 0.586 2024-06-23 18:46:01Z - simplification of calculation for "multipleOf" keyword 0.585 2024-06-19 04:03:36Z - make use of core bool functionality where it exists (perl 5.36+) 0.584 2024-05-18 21:06:35Z - add 'get_entity_locations' helper sub to ::Document 0.583 2024-03-30 17:56:17Z - further optimization of error and annotation construction, which should significantly improve evaluation performance of large documents which heavily use the 'unevaluated*' keywords (such as a schema evaluated against its metaschema, or an OpenAPI document evaluated against its schema) 0.582 2024-01-23 03:18:31Z - change the status of some format tests that rely on optional modules so they are not reported as specification failures - fix forking test that failed on MSWin32 0.581 2024-01-18 05:35:16Z - make automated tests much quieter when some optional modules (e.g. used for format tests) are not installed 0.580 2024-01-18 04:38:33Z - update a format test to be more amenable to 32-bit architectures 0.579 2024-01-15 03:32:27Z - improve performance by checking for duplicates by comparing the checksum of a schema rather than the content itself - fix error occurring when using the FormatAssertion vocabulary in an evaluator object that was loaded from a serialized object - fixed custom format definitions to not allow 'integer' types, as per the specification - support custom format definitions that operate on more than one core data type - fixed handling of unrecognized formats in draft2020-12 and later (but only when the FormatAssertion vocabulary is explicitly requested, not with validate_formats=1) - the format keyword now respects the "stringy_numbers" option 0.578 2023-12-29 23:13:44Z - remove use of JSON::MaybeXS, to avoid potential use of JSON::XS; now we use Cpanel::JSON::XS or JSON::PP directly, using the same environment variables as in Mojo::JSON for customization. - new helper interface, get_document() 0.577 2023-12-19 05:27:04Z - new attribute on Error and Result objects: "recommended_response", for use when validating HTTP requests 0.576 2023-12-10 06:10:57Z - the "stringy_numbers" feature now also applies to the "enum", "const", and "uniqueItems" keywords 0.575 2023-11-26 05:11:10Z - properly handle some edge cases where the "$schema" keyword can change the dialect to a different specification version with different Core vocabulary keywords - evaluation at non-schema locations is now prohibited 0.574 2023-11-13 00:50:55Z - better detection of schema locations, for use by Document subclasses - fixed vocabulary ordering (from v0.567) - properly handle "$dynamicRef", "$recursiveRef" and "$schema" referencing a boolean schema - bundled metaschemas have been updated to their latest versions from https://json-schema.org 0.573 2023-10-21 23:49:03Z - fix construction of default values of some attributes e.g. media_types, encodings - fix list context of has_errors, error_count, annotation_count methods 0.572 2023-10-14 22:04:52Z - boost runtime performance by removing uses of MooX::HandlesVia 0.571 2023-09-17 01:14:47Z - removed duration and uuid formats for draft7 (they were not defined until the next spec version) - properly default format validation to true in draft7 0.570 2023-09-02 20:42:03Z - small performance improvements to 'date-time' and 'date' format validation - new stringy_numbers option, for validating numbers more loosely 0.569 2023-07-08 23:36:06Z - fixed some edge cases with ipv6 format validation 0.568 2023-06-17 07:16:54Z - add media-type support for application/x-ndjson and application/x-www-form-urlencoded 0.567 2023-06-03 22:11:23Z - vocabularies are now evaluated in a different order: Validation and Format vocabularies now come before Applicator, in order to allow faster short circuiting when errors are encountered. 0.566 2023-05-11 03:34:59Z - treat ambiguous types as a normal error, rather than an exception which may provide incorrect location data 0.565 2023-03-12 21:19:27Z - traverse and evaluate callbacks can now produce errors, which are incorporated into the overall evaluation results - fix bad handling of empty patterns in "pattern", "patternProperties" keywords 0.564 2023-03-04 00:43:42Z - further tweak performance by short-circuiting inside some subschemas (but not when annotations must be collected for "unevaluated" keywords) - added support for 'base64url' encoding 0.563 2023-02-04 23:38:13Z - documentation update: improve language around data types and JSON decoder recommendations 0.562 2023-01-22 00:49:07Z - bump a test prereq to fix a mismatched exception message 0.561 2023-01-07 20:57:37Z - further tweak performance by only collecting annotations when explicitly requested, or needed by the current evaluation scope 0.560 2022-12-20 20:00:05Z - fix a test that depended on optional prereqs 0.559 2022-12-16 04:29:55Z - fix regression where formats do not validate beyond a $ref (since v0.556) 0.558 2022-11-26 02:43:15Z - add fallback media type handling for text/* - performance is (hopefully) improved by delaying some calculations in annotations until they are needed 0.557 2022-10-30 21:59:04Z - improvements to processing of keywords in the Content vocabulary (contentEncoding, contentMediaType, contentSchema) - LICENSE now provided with bundled metaschema files 0.556 2022-09-18 22:41:50Z - some performance optimizations for schema traversal and evaluation 0.555 2022-09-10 21:43:11Z - the "iri-reference" format is now supported, sort of (all strings will be accepted as valid) - "enum" no longer incorrectly errors if elements are not unique - new experimental output format: "data_only", which encapsulates what is produced when a JSON::Schema::Modern::Result object is stringified 0.554 2022-07-24 00:08:54Z - use new Slurpy type in Types::Standard 0.553 2022-06-25 03:27:14Z - expanded on the documentation for serializing results. - updated IETF URI references from draft-bhutton-json-schema-00 to draft-bhutton-json-schema-01, to reflect the updates to specification draft2020-12 (implementation updates have already been reflected in earlier releases, notably 0.548 and 0.550) 0.552 2022-05-03 03:31:59Z - fix result serialization from exceptions (broken in 0.550) 0.551 2022-05-01 01:29:51Z - the "specification_version" configuration option now accepts values without "draft" in the name, to facilitate a new naming convention used for future specification versions - new "formatted_annotations" option for JSON::Schema::Modern::Result, to allow for omitting annotations from result output 0.550 2022-04-14 04:32:32Z - added 'dump' method to Result, Error and Annotation objects, for easier debugging and generating test output - adjusted syntax checks for $vocabulary keyword to allow for bundled metaschemas - new validate_schema() method, for easily validating a schema against its metaschema 0.549 2022-03-22 03:55:15Z - properly detect the metaschema in json-schema-eval --validate-schema 0.548 2022-03-09 06:27:10Z - "annotate_unknown_keywords" option removed; behaviour is now on for draft2020-12 and off otherwise - annotation behaviour for applicator keywords is fixed per the spec, resulting in fewer redundant errors from unevaluatedItems, unevaluatedProperties keywords 0.547 2022-03-03 06:08:51Z - improved error stringification on document error 0.546 2022-02-23 01:33:03Z - avoid use of newly-experimental signature syntax on 5.35.9 0.545 2022-02-22 04:30:36Z - avoid new experimental warning on 5.35.9 - skip unresolvable identifiers for future drafts in acceptance tests (added in TJSA 1.016) 0.544 2022-02-16 05:53:08Z - add_schema() now has more consistent exception handling 0.543 2022-02-11 04:11:55Z - now allowing runtime overriding of the "strict" configuration - add "effective_base_uri", for adjusting the locations of errors and annotations against a dynamic base 0.542 2022-01-23 08:17:08Z - new "strict" option (and --strict flag to json-schema-eval), for disallowing unknown keywords 0.541 2022-01-17 23:57:35Z - add --add-schema option to json-schema-eval to allow for the reference of additional schemas during evaluation 0.540 2022-01-17 18:58:36Z - make "unimplemented format" errors more visible - fixed serializing of results in acceptance tests - add --validate-schema option to json-schema-eval to provide an easy way to validate a schema against its meta-schema 0.539 2022-01-06 04:07:20Z - updated error message for the "type" keyword to include the actual type, as well as the expected type(s) 0.538 2021-12-31 21:18:21Z - remove no-longer-needed TODO in tests that caused warnings in small-int systems 0.537 2021-12-30 22:49:02Z - fix number/integer differentiation on small-int systems - improve division calculations if either argument is a non-integer 0.536 2021-12-30 05:32:44Z - very large/small numbers are now properly accomodated in all cases, including from JSON-serialized data 0.535 2021-12-28 06:38:37Z - mark more tests TODO (temporarily!) for small-int systems 0.534 2021-12-27 21:05:34Z - clarify exit statuses for 'json-schema-eval' - fix numeric tests for architectures with small int size - fix handling of unsigned ints that cannot be represented in a signed int 0.533 2021-12-23 19:45:11Z - fix exit statuses in json-schema-eval 0.532 2021-12-22 19:14:06Z - add media_type decoders for application/schema+json, application/schema-instance+json - add 'json-schema-eval' executable, for ad-hoc evaluation 0.531 2021-12-06 05:39:53Z - add method to add format implementations after construction - treat media_type names case-insensitively, as per RFC6838, and lookups support wildcards (get_media_type('text/plain') will match an entry for 'text/*' or '*/*') 0.530 2021-12-03 16:44:23Z - fix hash slice syntax that is not available before perl 5.28 0.529 2021-12-03 04:00:26Z - added FREEZE and THAW callbacks to assist with serialization 0.528 2021-11-30 06:17:29Z - evaluation callback sub signature has changed, to add $data - minor performance improvement during evaluation 0.527 2021-11-26 00:53:42Z - fixes to base64 and json decoders used in the Content vocabulary 0.526 2021-11-23 05:11:46Z - fix evaluate() callbacks for keywords that have no runtime-specific actions - optional support for contentEncoding, contentMediaType and contentSchema with the validate_content_schemas option and "encoding", "media_type" handler registries - a boolean flag "unknown" has been added to Annotation objects to indicate they correspond to an unknown keyword in the schema 0.525 2021-11-17 05:35:24Z - minimum Perl version raised to 5.20 - dropped bundling hyper-schema files, as they are not validatable without vocabulary support (see issue #44) - & overload added to JSON::Schema::Modern::Result, for combining results - add callbacks to evaluate(), to enable finding certain positions of interest in a document or schema 0.524 2021-11-10 04:36:49Z - some refactoring of vocabulary and document methods, to faciliate re-use - new utility functions: is_uri_reference, assert_keyword_exists - allow specifying the metaschema_uri for a schema document - add "validate" method to Document object - updated draft2019-09 and added draft2020-12 hyperschema schemas 0.523 2021-10-24 05:58:27Z - fix tests that were relying on an optional prereq - properly gate experimental features by version 0.522 2021-10-22 22:26:29Z - 'date-time' format now properly handles leap seconds 0.521 2021-10-04 05:36:10Z - fix issues when referencing a schema in a metaschema: whose vocabulary is not known, but its keywords can still be validated, or which does not use the $vocabulary keyword at all 0.520 2021-09-27 05:56:11Z - support arbitrary metaschemas in the "$schema" keyword - support custom vocabulary classes, for use in metaschemas that use the "$vocabulary" keyword 0.519 2021-09-21 03:56:13Z - fix tests that were relying on an optional prereq 0.518 2021-09-18 22:00:42Z - skip some regex tests when Unicode library is too old for those character classes being tested - specification versions can now change, via the $schema keyword, within schema resources in a single document or via $ref to another resource 0.517 2021-08-28 04:34:17Z - restore some optional modules used for format validation to the prereq list that were mistakenly dropped in version 0.515 - date-time, date and time formats no longer match non-ascii digits 0.516 2021-08-14 19:53:16Z - fix email format tests on older prereqs - avoid errors when enabling validate_formats before evaluating but after loading a schema 0.515 2021-08-03 04:07:02Z - no longer calling a keyword's callback during traverse() if that keyword has an error - better handling of blessed data types and other references - new config option scalarref_booleans, which will treat \0, \1 in data as json booleans - support for the most recent specification version, draft2020-12 0.514 2021-07-22 05:17:08Z - add_schema() now dies with the errors themselves, rather than an object that serializes to an unhelpful value in uncaught die()s. 0.513 2021-06-26 19:45:51Z - skip acceptance test for integer overflow when nvsize is too large to produce the expected error - support for specification version draft7, through the "$schema" keyword and the new 'specification_version' constructor option 0.512 2021-06-09 02:29:23Z - distribution has been renamed from JSON-Schema-Draft201909 to JSON-Schema-Modern. JSON::Schema::Draft201909 lives on as a compatibility wrapper. 0.028 2021-06-08 02:48:07Z - fix validation regex for the $anchor keyword - unevaluatedItems and unevaluatedProperties keywords are now applied after all other keywords in the applicator vocabulary (true/false results are not affected, but the order of annotations and errors will change) - calculate the canonical uri correctly after navigating a $ref: using the closest resource identifier to the destination, not the one that was used in the $ref 0.027 2021-05-15 18:13:21Z - fixed error strings used for failing "dependentRequired" - in terse output format, do not discard non-summary errors from schema-form of items - keywords in the applicator vocabulary are now applied before the keywords in the validation vocabulary (true/false results are not affected, but the order of annotations and errors will change) - improved validation of the "date-time", "date" and "time" formats 0.026 2021-04-08 20:13:27Z - fix scoping of annotations from uncle keywords (siblings of the schema's parent) that were improperly visible to unevaluatedItems, unevaluatedProperties - 'result' attribute in JSON::Schema::Draft201909::Result has been renamed to 'valid', to better match what it represents (a boolean) 0.025 2021-03-30 05:36:14Z - minor changes to error strings to distinguish between issues that can be determined from static inspection of schema(s), and those that only arise during runtime evaluation (such as URIs that map to missing schema documents, or inconsistent configuration values). - more validity checks at traversal time of $ref, $schema, $vocabulary values - update ipv4 format validation to reject leading zeroes in octets, helping avoid a newly-discovered vulnerability in netmasks 0.024 2021-03-23 21:53:42Z - the default value for "validate_formats" is once again false (it became true in v0.020), to properly conform to the specification 0.023 2021-02-21 18:36:32Z - fix "try/catch is experimental" warnings in perl 5.33.7 0.022 2021-02-07 17:33:14Z - fix erroneous use of postfix dereference (closes #42). 0.021 2021-02-06 18:50:42Z - [Pp]roperties$ keywords now always produce annotations when evaluating successfully, even if there were no matching properties - added the "strict_basic" output format, for strict (but incorrect) adherence to the draft 2019-09 specification 0.020 2021-01-02 17:12:09Z - the default value for "validate_formats" is now true, to reflect the most typical usecase. - gracefully handle the erroneous schema { "type": null } - fixes to relative-json-pointer format validation - new "annotate_unknown_keywords" config option 0.019 2020-12-08 18:40:10Z - further improvements to the "terse" output format - add_schema will now die with a Result object rather than a listref of Error objects, when the document contains errors. 0.018 2020-12-07 18:22:07Z - now can correctly evaluate schemas containing unevaluatedItems, unevaluatedProperties keywords without the user having to explicitly set collect_annotations => 1 in the constructor - fix error in "terse" output formatting that mistakenly dropped some unevaluatedProperties errors 0.017 2020-11-24 19:15:18Z - refactor keyword implementations into separate vocabulary classes, to faciliate future support for custom vocabularies - traverse the schema before evaluation, for more correct and complete extraction of identifiers and invalid syntax - add callbacks to traverse(), to easily find keywords of interest 0.016 2020-11-18 18:18:40Z - further fixes to infinite loop detection - fix dereference error when evaluating "definitions", "dependencies" - when adding two schema documents with no canonical uri, preserve knowledge of other identifiers found in the first document - add_schema() no longer adds additional URIs to the document object, only the evaluator 0.015 2020-10-20 03:08:36Z - fixed infinity/overflow checks for older perls 0.014 2020-10-16 19:21:17Z - ensure "enum" value is an array - do not evaluate non-arrays against "unevaluatedItems" - fix detection of bad $recursiveAnchor - fix canonical uri calculation for $schema, $recursiveAnchor, and infinite loop detection - for output_format=terse, do not omit the important errors for unevaluated* when annotation collection is disabled 0.013 2020-09-15 19:14:53Z - detect more cases of resource identifier collisions - fix resolution of relative $ids - new "terse" output format 0.012 2020-08-13 20:23:21Z - now using unicode semantics for pattern matching 0.011 2020-08-04 22:16:46Z - better normalization of uris in errors - now detecting infinite loops separately from deep traversal - optionally collect annotations - support for the "unevaluatedItems" and "unevaluatedProperties" keywords 0.010 2020-07-23 16:50:18Z - fixed error generation for validator keywords with numeric arguments (e.g. minimum, multipleOf) - new "get" method for fetching the schema found at a URI - improved "ipv6" format validation 0.009 2020-07-07 19:54:44Z - no longer allowing adding another schema document with a duplicate uri but different schema content (some collision checks were too lax). - fix behaviour of $recursiveRef without an $recursiveAnchor in the initial target scope 0.008 2020-06-22 04:24:06Z - fix bad syntax used in a test 0.007 2020-06-21 21:20:33Z - raise some inadequate prereq declarations - fix incorrect canonical uri when evaluating a (sub)schema using a non-canonical uri 0.006 2020-06-19 20:54:40Z - add support for evaluation against a uri - add "add_schema" interface for using additional schema documents within the implementation - support using the "format" keyword as an assertion, with the "validate_formats" option 0.005 2020-06-09 01:54:05Z - fix some edge cases with usage of $recursiveAnchor, $recursiveRef - fixed several issues with resource identification within schema documents 0.004 2020-06-02 19:14:32Z - add support for $recursiveAnchor and $recursiveRef - support use of "$ref":"https://json-schema.org/draft/2019-09/schema" by loading common metaschemas from a local cache 0.003 2020-05-31 20:10:02Z - add infinite recursion detection - process properties in sorted order, for consistent ordering of results - mark a numeric comparison test as TODO on 32-bit machines (see GHI #10) 0.002 2020-05-27 22:28:15Z - fix incorrect prereq needed for tests - add support for $id and $anchor in single schema documents 0.001 2020-05-21 15:51:00Z - Initial release [as JSON-Schema-Draft201909]. JSON-Schema-Modern-0.609/MANIFEST000644 000766 000024 00000016210 15001302761 016320 0ustar00etherstaff000000 000000 # This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.032. Build.PL CONTRIBUTING Changes INSTALL LICENCE MANIFEST META.json META.yml README dist.ini inc/AppendSection.pm inc/CheckConflicts.pm lib/JSON/Schema/Modern.pm lib/JSON/Schema/Modern/Annotation.pm lib/JSON/Schema/Modern/Document.pm lib/JSON/Schema/Modern/Error.pm lib/JSON/Schema/Modern/Result.pm lib/JSON/Schema/Modern/ResultNode.pm lib/JSON/Schema/Modern/Utilities.pm lib/JSON/Schema/Modern/Vocabulary.pm lib/JSON/Schema/Modern/Vocabulary/Applicator.pm lib/JSON/Schema/Modern/Vocabulary/Content.pm lib/JSON/Schema/Modern/Vocabulary/Core.pm lib/JSON/Schema/Modern/Vocabulary/FormatAnnotation.pm lib/JSON/Schema/Modern/Vocabulary/FormatAssertion.pm lib/JSON/Schema/Modern/Vocabulary/MetaData.pm lib/JSON/Schema/Modern/Vocabulary/Unevaluated.pm lib/JSON/Schema/Modern/Vocabulary/Validation.pm script/json-schema-eval share/LICENSE share/draft2019-09/meta/applicator.json share/draft2019-09/meta/content.json share/draft2019-09/meta/core.json share/draft2019-09/meta/format.json share/draft2019-09/meta/meta-data.json share/draft2019-09/meta/validation.json share/draft2019-09/output/schema.json share/draft2019-09/schema.json share/draft2020-12/meta/applicator.json share/draft2020-12/meta/content.json share/draft2020-12/meta/core.json share/draft2020-12/meta/format-annotation.json share/draft2020-12/meta/format-assertion.json share/draft2020-12/meta/meta-data.json share/draft2020-12/meta/unevaluated.json share/draft2020-12/meta/validation.json share/draft2020-12/output/schema.json share/draft2020-12/schema.json share/draft4/schema.json share/draft6/schema.json share/draft7/schema.json t/00-report-prereqs.dd t/00-report-prereqs.t t/add-schema.t t/additional-tests-draft2019-09.t t/additional-tests-draft2019-09/README t/additional-tests-draft2019-09/anchor.json t/additional-tests-draft2019-09/annotation-collection.json t/additional-tests-draft2019-09/badRef.json t/additional-tests-draft2019-09/faux-buggy-schemas.json t/additional-tests-draft2019-09/format-duration.json t/additional-tests-draft2019-09/format-ipv4.json t/additional-tests-draft2019-09/format-ipv6.json t/additional-tests-draft2019-09/format-relative-json-pointer.json t/additional-tests-draft2019-09/formats.json t/additional-tests-draft2019-09/id.json t/additional-tests-draft2019-09/integers.json t/additional-tests-draft2019-09/keyword-independence.json t/additional-tests-draft2019-09/loose-types-const-enum.json t/additional-tests-draft2019-09/optional/format-date-time.json t/additional-tests-draft2019-09/optional/format-date.json t/additional-tests-draft2019-09/optional/format-time.json t/additional-tests-draft2019-09/recursive-dynamic.json t/additional-tests-draft2019-09/ref-and-id.json t/additional-tests-draft2019-09/ref.json t/additional-tests-draft2019-09/short-circuit.json t/additional-tests-draft2019-09/unknownKeyword.json t/additional-tests-draft2019-09/vocabulary.json t/additional-tests-draft2020-12.t t/additional-tests-draft2020-12/README t/additional-tests-draft2020-12/anchor.json t/additional-tests-draft2020-12/annotation-collection.json t/additional-tests-draft2020-12/badRef.json t/additional-tests-draft2020-12/dynamicRef.json t/additional-tests-draft2020-12/faux-buggy-schemas.json t/additional-tests-draft2020-12/format-duration.json t/additional-tests-draft2020-12/format-ipv4.json t/additional-tests-draft2020-12/format-ipv6.json t/additional-tests-draft2020-12/format-relative-json-pointer.json t/additional-tests-draft2020-12/formats.json t/additional-tests-draft2020-12/id.json t/additional-tests-draft2020-12/integers.json t/additional-tests-draft2020-12/keyword-independence.json t/additional-tests-draft2020-12/loose-types-const-enum.json t/additional-tests-draft2020-12/optional/format-date-time.json t/additional-tests-draft2020-12/optional/format-date.json t/additional-tests-draft2020-12/optional/format-time.json t/additional-tests-draft2020-12/recursive-dynamic.json t/additional-tests-draft2020-12/ref-and-id.json t/additional-tests-draft2020-12/ref.json t/additional-tests-draft2020-12/short-circuit.json t/additional-tests-draft2020-12/unknownKeyword.json t/additional-tests-draft2020-12/vocabulary.json t/additional-tests-draft7.t t/additional-tests-draft7/README t/additional-tests-draft7/badRef.json t/additional-tests-draft7/faux-buggy-schemas.json t/additional-tests-draft7/format-ipv4.json t/additional-tests-draft7/format-relative-json-pointer.json t/additional-tests-draft7/id.json t/additional-tests-draft7/integers.json t/additional-tests-draft7/keyword-independence.json t/additional-tests-draft7/loose-types-const-enum.json t/additional-tests-draft7/not-an-anchor.json t/additional-tests-draft7/not-an-id.json t/additional-tests-draft7/optional/format-date-time.json t/additional-tests-draft7/optional/format-date.json t/additional-tests-draft7/optional/format-time.json t/additional-tests-draft7/ref-and-id.json t/additional-tests-draft7/ref.json t/additional-tests-draft7/short-circuit.json t/additional-tests-draft7/unknownKeyword.json t/additional-tests-draft7/vocabulary.json t/annotations.t t/boolean-data.t t/boolean-schemas.t t/cached-metaschemas.t t/callbacks.t t/checksums.t t/content-encoding.t t/dialects.t t/document.t t/equality.t t/errors.t t/evaluate_json_string.t t/find-identifiers.t t/formats.t t/invalid-schemas.t t/invalid-schemas/invalid-input.json t/invalid-schemas/ref.json t/invalid-schemas/vocabulary.json t/lib/Acceptance.pm t/lib/Helper.pm t/lib/MyVocabulary/BadEvaluationOrder.pm t/lib/MyVocabulary/BadVocabularySub1.pm t/lib/MyVocabulary/BadVocabularySub2.pm t/lib/MyVocabulary/BadVocabularySub3.pm t/lib/MyVocabulary/MissingRole.pm t/lib/MyVocabulary/MissingSub.pm t/lib/MyVocabulary/StringComparison.pm t/max_traversal_depth.t t/multipleOf.t t/output_format.t t/pattern.t t/read_serialized_file t/ref.t t/results/draft2019-09-acceptance-format.txt t/results/draft2019-09-acceptance.txt t/results/draft2019-09-additional-tests.txt t/results/draft2019-09-invalid-schemas.txt t/results/draft2020-12-acceptance-format.txt t/results/draft2020-12-acceptance.txt t/results/draft2020-12-additional-tests.txt t/results/draft2020-12-invalid-schemas.txt t/results/draft4-acceptance-format.txt t/results/draft4-acceptance.txt t/results/draft6-acceptance-format.txt t/results/draft6-acceptance.txt t/results/draft7-acceptance-format.txt t/results/draft7-acceptance.txt t/results/draft7-additional-tests.txt t/serialization.t t/specification_version.t t/strict.t t/stringy-numbers.t t/traverse.t t/type.t t/unsupported-keywords.t t/validate-schema.t t/vocabularies.t t/zzz-acceptance-draft2019-09-format.t t/zzz-acceptance-draft2019-09.t t/zzz-acceptance-draft2020-12-format.t t/zzz-acceptance-draft2020-12.t t/zzz-acceptance-draft4-format.t t/zzz-acceptance-draft4.t t/zzz-acceptance-draft6-format.t t/zzz-acceptance-draft6.t t/zzz-acceptance-draft7-format.t t/zzz-acceptance-draft7.t t/zzz-check-breaks.t update-schemas weaver.ini xt/author/00-compile.t xt/author/clean-namespaces.t xt/author/distmeta.t xt/author/eol.t xt/author/kwalitee.t xt/author/minimum-version.t xt/author/mojibake.t xt/author/no-tabs.t xt/author/pod-coverage.t xt/author/pod-spell.t xt/author/pod-syntax.t xt/author/portability.t xt/release/changes_has_content.t xt/release/cpan-changes.t JSON-Schema-Modern-0.609/t/000770 000766 000024 00000000000 15001302761 015427 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/xt/000770 000766 000024 00000000000 15001302761 015617 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/README000644 000766 000024 00000000611 15001302761 016045 0ustar00etherstaff000000 000000 This archive contains the distribution JSON-Schema-Modern, version 0.609: Validate data against a schema using a JSON Schema This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. This README file was generated by Dist::Zilla::Plugin::Readme v6.032. JSON-Schema-Modern-0.609/script/000770 000766 000024 00000000000 15001302761 016470 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/META.yml000644 000766 000024 00000075632 15001302761 016455 0ustar00etherstaff000000 000000 --- abstract: 'Validate data against a schema using a JSON Schema' author: - 'Karen Etheridge ' build_requires: CPAN::Meta::Check: '0.011' CPAN::Meta::Requirements: '0' Data::Dumper: '0' File::Spec: '0' Math::BigInt: '1.999701' Module::Metadata: '0' Term::ANSIColor: '0' Test2::API: '0' Test::Deep: '0' Test::Deep::UnorderedPairs: '0' Test::Fatal: '0' Test::File::ShareDir: '0' Test::JSON::Schema::Acceptance: '1.021' Test::Memory::Cycle: '0' Test::More: '0.96' Test::Needs: '0' Test::Warnings: '0' Test::Without::Module: '0.19' lib: '0' perl: v5.20.0 utf8: '0' configure_requires: Module::Build::Tiny: '0.034' perl: '5.020' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010' keywords: - JSON - Schema - validator - data - validation - structure - specification license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: JSON-Schema-Modern no_index: directory: - inc - share - t - xt provides: JSON::Schema::Modern: file: lib/JSON/Schema/Modern.pm version: '0.609' JSON::Schema::Modern::Annotation: file: lib/JSON/Schema/Modern/Annotation.pm version: '0.609' JSON::Schema::Modern::Document: file: lib/JSON/Schema/Modern/Document.pm version: '0.609' JSON::Schema::Modern::Error: file: lib/JSON/Schema/Modern/Error.pm version: '0.609' JSON::Schema::Modern::Result: file: lib/JSON/Schema/Modern/Result.pm version: '0.609' JSON::Schema::Modern::ResultNode: file: lib/JSON/Schema/Modern/ResultNode.pm version: '0.609' JSON::Schema::Modern::Utilities: file: lib/JSON/Schema/Modern/Utilities.pm version: '0.609' JSON::Schema::Modern::Vocabulary: file: lib/JSON/Schema/Modern/Vocabulary.pm version: '0.609' JSON::Schema::Modern::Vocabulary::Applicator: file: lib/JSON/Schema/Modern/Vocabulary/Applicator.pm version: '0.609' JSON::Schema::Modern::Vocabulary::Content: file: lib/JSON/Schema/Modern/Vocabulary/Content.pm version: '0.609' JSON::Schema::Modern::Vocabulary::Core: file: lib/JSON/Schema/Modern/Vocabulary/Core.pm version: '0.609' JSON::Schema::Modern::Vocabulary::FormatAnnotation: file: lib/JSON/Schema/Modern/Vocabulary/FormatAnnotation.pm version: '0.609' JSON::Schema::Modern::Vocabulary::FormatAssertion: file: lib/JSON/Schema/Modern/Vocabulary/FormatAssertion.pm version: '0.609' JSON::Schema::Modern::Vocabulary::MetaData: file: lib/JSON/Schema/Modern/Vocabulary/MetaData.pm version: '0.609' JSON::Schema::Modern::Vocabulary::Unevaluated: file: lib/JSON/Schema/Modern/Vocabulary/Unevaluated.pm version: '0.609' JSON::Schema::Modern::Vocabulary::Validation: file: lib/JSON/Schema/Modern/Vocabulary/Validation.pm version: '0.609' requires: B: '0' Carp: '0' Cpanel::JSON::XS: '4.38' Digest::MD5: '0' Exporter: '0' Feature::Compat::Try: '0' File::ShareDir: '0' Getopt::Long::Descriptive: '0' JSON::PP: '4.11' List::Util: '1.55' MIME::Base64: '0' Math::BigFloat: '0' Math::BigInt: '1.999701' Mojo::JSON: '0' Mojo::JSON::Pointer: '0' Mojo::Message::Response: '0' Mojo::URL: '0' Mojolicious: '7.87' Moo: '0' Moo::Role: '0' MooX::TypeTiny: '0.002002' Path::Tiny: '0' Ref::Util: '0.100' Safe::Isa: '1.000008' Scalar::Util: '0' Storable: '0' Sub::Install: '0' Types::Common::Numeric: '0' Types::Standard: '1.016003' autovivification: '0' builtin::compat: '0.003003' constant: '0' experimental: '0.026' if: '0' namespace::clean: '0' open: '0' overload: '0' perl: v5.20.0 stable: '0.031' strict: '0' strictures: '2' warnings: '0' resources: bugtracker: https://github.com/karenetheridge/JSON-Schema-Modern/issues homepage: https://github.com/karenetheridge/JSON-Schema-Modern repository: https://github.com/karenetheridge/JSON-Schema-Modern.git version: '0.609' x_Dist_Zilla: perl: version: '5.041010' plugins: - class: Dist::Zilla::Plugin::Run::BeforeRelease config: Dist::Zilla::Plugin::Run::Role::Runner: eval: - "do './update-schemas'; die $@ || $! if $@ || $!" fatal_errors: 1 quiet: 0 version: '0.050' name: Run::BeforeRelease version: '0.050' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: develop type: recommends name: '@Author::ETHER/pluginbundle version' version: '6.032' - class: Dist::Zilla::Plugin::PromptIfStale config: Dist::Zilla::Plugin::PromptIfStale: check_all_plugins: 0 check_all_prereqs: 0 modules: - Dist::Zilla::PluginBundle::Author::ETHER phase: build run_under_travis: 0 skip: [] name: '@Author::ETHER/stale modules, build' version: '0.060' - class: Dist::Zilla::Plugin::ExecDir name: '@Author::ETHER/ExecDir' version: '6.032' - class: Dist::Zilla::Plugin::FileFinder::ByName name: '@Author::ETHER/Examples' version: '6.032' - class: Dist::Zilla::Plugin::Git::GatherDir config: Dist::Zilla::Plugin::GatherDir: exclude_filename: - CONTRIBUTING - INSTALL - LICENCE - README.pod - TODO - pull_request_template.md exclude_match: [] include_dotfiles: 0 prefix: '' prune_directory: [] root: . Dist::Zilla::Plugin::Git::GatherDir: include_untracked: 0 name: '@Author::ETHER/Git::GatherDir' version: '2.051' - class: Dist::Zilla::Plugin::MetaYAML name: '@Author::ETHER/MetaYAML' version: '6.032' - class: Dist::Zilla::Plugin::MetaJSON name: '@Author::ETHER/MetaJSON' version: '6.032' - class: Dist::Zilla::Plugin::Readme name: '@Author::ETHER/Readme' version: '6.032' - class: Dist::Zilla::Plugin::Manifest name: '@Author::ETHER/Manifest' version: '6.032' - class: Dist::Zilla::Plugin::License name: '@Author::ETHER/License' version: '6.032' - class: Dist::Zilla::Plugin::GenerateFile::FromShareDir config: Dist::Zilla::Plugin::GenerateFile::FromShareDir: destination_filename: CONTRIBUTING dist: Dist-Zilla-PluginBundle-Author-ETHER encoding: UTF-8 has_xs: 0 location: build source_filename: CONTRIBUTING Dist::Zilla::Role::RepoFileInjector: allow_overwrite: 1 repo_root: . version: '0.009' name: '@Author::ETHER/generate CONTRIBUTING' version: '0.015' - class: Dist::Zilla::Plugin::InstallGuide config: Dist::Zilla::Role::ModuleMetadata: Module::Metadata: '1.000038' version: '0.006' name: '@Author::ETHER/InstallGuide' version: '1.200014' - class: Dist::Zilla::Plugin::Test::Compile config: Dist::Zilla::Plugin::Test::Compile: bail_out_on_fail: 1 fail_on_warning: author fake_home: 0 filename: xt/author/00-compile.t module_finder: - ':InstallModules' needs_display: 0 phase: develop script_finder: - ':PerlExecFiles' - '@Author::ETHER/Examples' skips: [] switch: [] name: '@Author::ETHER/Test::Compile' version: '2.058' - class: Dist::Zilla::Plugin::Test::NoTabs config: Dist::Zilla::Plugin::Test::NoTabs: filename: xt/author/no-tabs.t finder: - ':InstallModules' - ':ExecFiles' - '@Author::ETHER/Examples' - ':TestFiles' - ':ExtraTestFiles' name: '@Author::ETHER/Test::NoTabs' version: '0.15' - class: Dist::Zilla::Plugin::Test::EOL config: Dist::Zilla::Plugin::Test::EOL: filename: xt/author/eol.t finder: - ':ExecFiles' - ':ExtraTestFiles' - ':InstallModules' - ':TestFiles' - '@Author::ETHER/Examples' trailing_whitespace: 1 name: '@Author::ETHER/Test::EOL' version: '0.19' - class: Dist::Zilla::Plugin::MetaTests name: '@Author::ETHER/MetaTests' version: '6.032' - class: Dist::Zilla::Plugin::Test::CPAN::Changes config: Dist::Zilla::Plugin::Test::CPAN::Changes: changelog: Changes filename: xt/release/cpan-changes.t name: '@Author::ETHER/Test::CPAN::Changes' version: '0.013' - class: Dist::Zilla::Plugin::Test::ChangesHasContent name: '@Author::ETHER/Test::ChangesHasContent' version: '0.011' - class: Dist::Zilla::Plugin::Test::MinimumVersion config: Dist::Zilla::Plugin::Test::MinimumVersion: max_target_perl: '5.020' name: '@Author::ETHER/Test::MinimumVersion' version: '2.000011' - class: Dist::Zilla::Plugin::PodSyntaxTests name: '@Author::ETHER/PodSyntaxTests' version: '6.032' - class: Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe config: Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe: finder: - ':InstallModules' name: '@Author::ETHER/Test::Pod::Coverage::TrustMe' version: v1.0.1 - class: Dist::Zilla::Plugin::Test::PodSpelling config: Dist::Zilla::Plugin::Test::PodSpelling: directories: - examples - lib - script - t - xt spell_cmd: '' stopwords: - irc wordlist: Pod::Wordlist name: '@Author::ETHER/Test::PodSpelling' version: '2.007006' - class: Dist::Zilla::Plugin::Test::Kwalitee config: Dist::Zilla::Plugin::Test::Kwalitee: filename: xt/author/kwalitee.t skiptest: [] name: '@Author::ETHER/Test::Kwalitee' version: '2.12' - class: Dist::Zilla::Plugin::MojibakeTests name: '@Author::ETHER/MojibakeTests' version: '0.8' - class: Dist::Zilla::Plugin::Test::ReportPrereqs name: '@Author::ETHER/Test::ReportPrereqs' version: '0.029' - class: Dist::Zilla::Plugin::Test::Portability config: Dist::Zilla::Plugin::Test::Portability: options: '' name: '@Author::ETHER/Test::Portability' version: '2.001003' - class: Dist::Zilla::Plugin::Test::CleanNamespaces config: Dist::Zilla::Plugin::Test::CleanNamespaces: filename: xt/author/clean-namespaces.t skips: [] name: '@Author::ETHER/Test::CleanNamespaces' version: '0.006' - class: Dist::Zilla::Plugin::Git::Describe name: '@Author::ETHER/Git::Describe' version: '0.008' - class: Dist::Zilla::Plugin::PodWeaver config: Dist::Zilla::Plugin::PodWeaver: finder: - ':InstallModules' - ':PerlExecFiles' plugins: - class: Pod::Weaver::Section::GenerateSection name: SUPPORT version: '4.020' - class: Pod::Weaver::Section::GenerateSection name: 'COPYRIGHT AND LICENCE' version: '4.020' - class: Pod::Weaver::Plugin::EnsurePod5 name: '@Author::ETHER/EnsurePod5' version: '4.020' - class: Pod::Weaver::Plugin::H1Nester name: '@Author::ETHER/H1Nester' version: '4.020' - class: Pod::Weaver::Plugin::SingleEncoding name: '@Author::ETHER/SingleEncoding' version: '4.020' - class: Pod::Weaver::Plugin::Transformer name: '@Author::ETHER/List' version: '4.020' - class: Pod::Weaver::Plugin::Transformer name: '@Author::ETHER/Verbatim' version: '4.020' - class: Pod::Weaver::Section::Region name: '@Author::ETHER/header' version: '4.020' - class: Pod::Weaver::Section::Name name: '@Author::ETHER/Name' version: '4.020' - class: Pod::Weaver::Section::Version name: '@Author::ETHER/Version' version: '4.020' - class: Pod::Weaver::Section::Region name: '@Author::ETHER/prelude' version: '4.020' - class: Pod::Weaver::Section::Generic name: SYNOPSIS version: '4.020' - class: Pod::Weaver::Section::Generic name: DESCRIPTION version: '4.020' - class: Pod::Weaver::Section::Generic name: OVERVIEW version: '4.020' - class: Pod::Weaver::Section::Collect name: ATTRIBUTES version: '4.020' - class: Pod::Weaver::Section::Collect name: METHODS version: '4.020' - class: Pod::Weaver::Section::Collect name: FUNCTIONS version: '4.020' - class: Pod::Weaver::Section::Collect name: TYPES version: '4.020' - class: Pod::Weaver::Section::Leftovers name: '@Author::ETHER/Leftovers' version: '4.020' - class: Pod::Weaver::Section::Region name: '@Author::ETHER/postlude' version: '4.020' - class: Pod::Weaver::Section::GenerateSection name: '@Author::ETHER/generate SUPPORT' version: '4.020' - class: Pod::Weaver::Section::Authors name: '@Author::ETHER/Authors' version: '4.020' - class: Pod::Weaver::Section::AllowOverride name: '@Author::ETHER/allow override AUTHOR' version: '0.05' - class: Pod::Weaver::Section::Contributors name: '@Author::ETHER/Contributors' version: '0.009' - class: Pod::Weaver::Section::Legal name: '@Author::ETHER/Legal' version: '4.020' - class: Pod::Weaver::Section::Region name: '@Author::ETHER/footer' version: '4.020' - class: inc::AppendSection name: AppendSupport version: ~ - class: inc::AppendSection name: AppendCopyright version: ~ name: '@Author::ETHER/PodWeaver' version: '4.010' - class: Dist::Zilla::Plugin::GithubMeta name: '@Author::ETHER/GithubMeta' version: '0.58' - class: Dist::Zilla::Plugin::AutoMetaResources name: '@Author::ETHER/AutoMetaResources' version: '1.21' - class: Dist::Zilla::Plugin::Authority name: '@Author::ETHER/Authority' version: '1.009' - class: Dist::Zilla::Plugin::MetaNoIndex name: '@Author::ETHER/MetaNoIndex' version: '6.032' - class: Dist::Zilla::Plugin::MetaProvides::Package config: Dist::Zilla::Plugin::MetaProvides::Package: finder: - ':InstallModules' finder_objects: - class: Dist::Zilla::Plugin::FinderCode name: ':InstallModules' version: '6.032' include_underscores: 0 Dist::Zilla::Role::MetaProvider::Provider: $Dist::Zilla::Role::MetaProvider::Provider::VERSION: '2.002004' inherit_missing: 0 inherit_version: 0 meta_noindex: 1 Dist::Zilla::Role::ModuleMetadata: Module::Metadata: '1.000038' version: '0.006' name: '@Author::ETHER/MetaProvides::Package' version: '2.004003' - class: Dist::Zilla::Plugin::MetaConfig name: '@Author::ETHER/MetaConfig' version: '6.032' - class: Dist::Zilla::Plugin::Keywords config: Dist::Zilla::Plugin::Keywords: keywords: - JSON - Schema - validator - data - validation - structure - specification name: '@Author::ETHER/Keywords' version: '0.007' - class: Dist::Zilla::Plugin::UseUnsafeInc config: Dist::Zilla::Plugin::UseUnsafeInc: dot_in_INC: 0 name: '@Author::ETHER/UseUnsafeInc' version: '0.002' - class: Dist::Zilla::Plugin::AutoPrereqs name: '@Author::ETHER/AutoPrereqs' version: '6.032' - class: Dist::Zilla::Plugin::Prereqs::AuthorDeps name: '@Author::ETHER/Prereqs::AuthorDeps' version: '0.007' - class: Dist::Zilla::Plugin::MinimumPerl name: '@Author::ETHER/MinimumPerl' version: '1.006' - class: Dist::Zilla::Plugin::ModuleBuildTiny config: Dist::Zilla::Role::TestRunner: default_jobs: 9 name: '@Author::ETHER/ModuleBuildTiny' version: '0.019' - class: Dist::Zilla::Plugin::Git::Contributors config: Dist::Zilla::Plugin::Git::Contributors: git_version: 2.47.1 include_authors: 0 include_releaser: 1 order_by: commits paths: [] name: '@Author::ETHER/Git::Contributors' version: '0.038' - class: Dist::Zilla::Plugin::StaticInstall config: Dist::Zilla::Plugin::StaticInstall: dry_run: 0 mode: off name: '@Author::ETHER/StaticInstall' version: '0.012' - class: Dist::Zilla::Plugin::RunExtraTests config: Dist::Zilla::Role::TestRunner: default_jobs: 9 name: '@Author::ETHER/RunExtraTests' version: '0.029' - class: Dist::Zilla::Plugin::CheckSelfDependency config: Dist::Zilla::Plugin::CheckSelfDependency: finder: - ':InstallModules' Dist::Zilla::Role::ModuleMetadata: Module::Metadata: '1.000038' version: '0.006' name: '@Author::ETHER/CheckSelfDependency' version: '0.011' - class: Dist::Zilla::Plugin::Run::AfterBuild config: Dist::Zilla::Plugin::Run::Role::Runner: fatal_errors: 1 quiet: 1 run: - "bash -c \"test -e .ackrc && grep -q -- '--ignore-dir=.latest' .ackrc || echo '--ignore-dir=.latest' >> .ackrc; if [[ `dirname '%d'` != .build ]]; then test -e .ackrc && grep -q -- '--ignore-dir=%d' .ackrc || echo '--ignore-dir=%d' >> .ackrc; fi\"" version: '0.050' name: '@Author::ETHER/.ackrc' version: '0.050' - class: Dist::Zilla::Plugin::Run::AfterBuild config: Dist::Zilla::Plugin::Run::Role::Runner: eval: - "if ('%d' =~ /^%n-[.[:xdigit:]]+$/) { unlink '.latest'; symlink '%d', '.latest'; }" fatal_errors: 0 quiet: 1 version: '0.050' name: '@Author::ETHER/.latest' version: '0.050' - class: Dist::Zilla::Plugin::CheckStrictVersion name: '@Author::ETHER/CheckStrictVersion' version: '0.001' - class: Dist::Zilla::Plugin::CheckMetaResources name: '@Author::ETHER/CheckMetaResources' version: '0.001' - class: Dist::Zilla::Plugin::EnsureLatestPerl config: Dist::Zilla::Plugin::EnsureLatestPerl: Module::CoreList: '5.20250414' name: '@Author::ETHER/EnsureLatestPerl' version: '0.010' - class: Dist::Zilla::Plugin::PromptIfStale config: Dist::Zilla::Plugin::PromptIfStale: check_all_plugins: 1 check_all_prereqs: 1 modules: [] phase: release run_under_travis: 0 skip: [] name: '@Author::ETHER/stale modules, release' version: '0.060' - class: Dist::Zilla::Plugin::Git::Check config: Dist::Zilla::Plugin::Git::Check: untracked_files: die Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: [] allow_dirty_match: [] changelog: Changes Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . name: '@Author::ETHER/initial check' version: '2.051' - class: Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts config: Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . name: '@Author::ETHER/Git::CheckFor::MergeConflicts' version: '0.014' - class: Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch config: Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . name: '@Author::ETHER/Git::CheckFor::CorrectBranch' version: '0.014' - class: Dist::Zilla::Plugin::Git::Remote::Check name: '@Author::ETHER/Git::Remote::Check' version: 0.1.2 - class: Dist::Zilla::Plugin::CheckPrereqsIndexed name: '@Author::ETHER/CheckPrereqsIndexed' version: '0.022' - class: Dist::Zilla::Plugin::TestRelease name: '@Author::ETHER/TestRelease' version: '6.032' - class: Dist::Zilla::Plugin::Git::Check config: Dist::Zilla::Plugin::Git::Check: untracked_files: die Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: [] allow_dirty_match: [] changelog: Changes Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . name: '@Author::ETHER/after tests' version: '2.051' - class: Dist::Zilla::Plugin::CheckIssues name: '@Author::ETHER/CheckIssues' version: '0.011' - class: Dist::Zilla::Plugin::UploadToCPAN name: '@Author::ETHER/UploadToCPAN' version: '6.032' - class: Dist::Zilla::Plugin::CopyFilesFromRelease config: Dist::Zilla::Plugin::CopyFilesFromRelease: filename: - CONTRIBUTING - INSTALL - LICENCE - LICENSE - ppport.h match: [] name: '@Author::ETHER/copy generated files' version: '0.007' - class: Dist::Zilla::Plugin::ReadmeAnyFromPod config: Dist::Zilla::Role::FileWatcher: version: '0.006' name: '@Author::ETHER/ReadmeAnyFromPod' version: '0.163250' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: develop type: recommends name: '@Author::ETHER/@Git::VersionManager/pluginbundle version' version: '6.032' - class: Dist::Zilla::Plugin::RewriteVersion::Transitional config: Dist::Zilla::Plugin::RewriteVersion: add_tarball_name: 0 finders: - ':ExecFiles' - ':InstallModules' global: 1 skip_version_provider: 0 Dist::Zilla::Plugin::RewriteVersion::Transitional: {} name: '@Author::ETHER/@Git::VersionManager/RewriteVersion::Transitional' version: '0.009' - class: Dist::Zilla::Plugin::MetaProvides::Update name: '@Author::ETHER/@Git::VersionManager/MetaProvides::Update' version: '0.007' - class: Dist::Zilla::Plugin::CopyFilesFromRelease config: Dist::Zilla::Plugin::CopyFilesFromRelease: filename: - Changes match: [] name: '@Author::ETHER/@Git::VersionManager/CopyFilesFromRelease' version: '0.007' - class: Dist::Zilla::Plugin::Git::Commit config: Dist::Zilla::Plugin::Git::Commit: add_files_in: - . commit_msg: '%N-%v%t%n%n%c' signoff: 0 Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: - CONTRIBUTING - Changes - INSTALL - LICENCE - README.pod allow_dirty_match: [] changelog: Changes Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . Dist::Zilla::Role::Git::StringFormatter: time_zone: local name: '@Author::ETHER/@Git::VersionManager/release snapshot' version: '2.051' - class: Dist::Zilla::Plugin::Git::Tag config: Dist::Zilla::Plugin::Git::Tag: branch: ~ changelog: Changes signed: 0 tag: v0.609 tag_format: v%V tag_message: v%v%t Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . Dist::Zilla::Role::Git::StringFormatter: time_zone: local name: '@Author::ETHER/@Git::VersionManager/Git::Tag' version: '2.051' - class: Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional config: Dist::Zilla::Plugin::BumpVersionAfterRelease: finders: - ':InstallModules' global: 1 munge_makefile_pl: 1 Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional: {} name: '@Author::ETHER/@Git::VersionManager/BumpVersionAfterRelease::Transitional' version: '0.009' - class: Dist::Zilla::Plugin::NextRelease name: '@Author::ETHER/@Git::VersionManager/NextRelease' version: '6.032' - class: Dist::Zilla::Plugin::Git::Commit config: Dist::Zilla::Plugin::Git::Commit: add_files_in: [] commit_msg: 'increment $VERSION after %v release' signoff: 0 Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: - Build.PL - Changes - Makefile.PL allow_dirty_match: - (?^:^lib/.*\.pm$) changelog: Changes Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . Dist::Zilla::Role::Git::StringFormatter: time_zone: local name: '@Author::ETHER/@Git::VersionManager/post-release commit' version: '2.051' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: x_Dist_Zilla type: requires name: '@Author::ETHER/@Git::VersionManager/prereqs for @Git::VersionManager' version: '6.032' - class: Dist::Zilla::Plugin::Git::Push config: Dist::Zilla::Plugin::Git::Push: push_to: - origin remotes_must_exist: 1 Dist::Zilla::Role::Git::Repo: git_version: 2.47.1 repo_root: . name: '@Author::ETHER/Git::Push' version: '2.051' - class: Dist::Zilla::Plugin::GitHub::Update config: Dist::Zilla::Plugin::GitHub::Update: metacpan: 1 name: '@Author::ETHER/GitHub::Update' version: '0.49' - class: Dist::Zilla::Plugin::Run::AfterRelease config: Dist::Zilla::Plugin::Run::Role::Runner: fatal_errors: 0 quiet: 0 run: - REDACTED version: '0.050' name: '@Author::ETHER/install release' version: '0.050' - class: Dist::Zilla::Plugin::Run::AfterRelease config: Dist::Zilla::Plugin::Run::Role::Runner: eval: - 'print "release complete!\xa"' fatal_errors: 1 quiet: 1 version: '0.050' name: '@Author::ETHER/release complete' version: '0.050' - class: Dist::Zilla::Plugin::ConfirmRelease name: '@Author::ETHER/ConfirmRelease' version: '6.032' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: x_Dist_Zilla type: requires name: '@Author::ETHER/prereqs for @Author::ETHER' version: '6.032' - class: inc::CheckConflicts name: =inc::CheckConflicts version: ~ - class: Dist::Zilla::Plugin::ShareDir name: ShareDir version: '6.032' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: runtime type: requires name: RuntimeRequires version: '6.032' - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: runtime type: suggests name: RuntimeSuggests version: '6.032' - class: Dist::Zilla::Plugin::Prereqs::Soften config: Dist::Zilla::Plugin::Prereqs::Soften: copy_to: - develop.requires modules: - Time::Moment - DateTime::Format::RFC3339 - Data::Validate::Domain - Email::Address::XS - Net::IDN::Encode - Sereal modules_from_features: ~ to_relationship: suggests name: Prereqs::Soften version: '0.006003' - class: Dist::Zilla::Plugin::Breaks name: Breaks version: '0.004' - class: Dist::Zilla::Plugin::Test::CheckBreaks config: Dist::Zilla::Plugin::Test::CheckBreaks: conflicts_module: [] no_forced_deps: 0 Dist::Zilla::Role::ModuleMetadata: Module::Metadata: '1.000038' version: '0.006' name: Test::CheckBreaks version: '0.020' - class: Dist::Zilla::Plugin::FinderCode name: ':InstallModules' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':IncModules' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':TestFiles' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':ExtraTestFiles' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':ExecFiles' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':PerlExecFiles' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':ShareFiles' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':MainModule' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':AllFiles' version: '6.032' - class: Dist::Zilla::Plugin::FinderCode name: ':NoFiles' version: '6.032' - class: Dist::Zilla::Plugin::VerifyPhases name: '@Author::ETHER/PHASE VERIFICATION' version: '0.016' zilla: class: Dist::Zilla::Dist::Builder config: is_trial: 0 version: '6.032' x_authority: cpan:ETHER x_breaks: JSON::Schema::Modern::Document::OpenAPI: '< 0.079' JSON::Schema::Modern::Vocabulary::OpenAPI: '< 0.080' Mojolicious::Plugin::OpenAPI::Modern: '< 0.014' OpenAPI::Modern: '< 0.077' Test::Mojo::Role::OpenAPI::Modern: '< 0.007' x_generated_by_perl: v5.41.10 x_serialization_backend: 'YAML::Tiny version 1.76' x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later' x_static_install: 0 x_use_unsafe_inc: 0 JSON-Schema-Modern-0.609/lib/000770 000766 000024 00000000000 15001302761 015732 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/update-schemas000750 000766 000024 00000007433 15001302761 020022 0ustar00etherstaff000000 000000 #!/usr/bin/env perl # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strict; use warnings; use 5.020; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use Path::Tiny; use HTTP::Tiny; use Digest::MD5 'md5_hex'; use Test::File::ShareDir -share => { -dist => { 'JSON-Schema-Modern' => 'share' } }; use lib 'lib'; use JSON::Schema::Modern 0.578; # ATTENTION DISTRO REPACKAGERS: do NOT use fresh copies of these files # from their source; it is important to include the original versions # of the files as they were packaged with this cpan distribution, or # surprising behaviour may occur. my %files = ( 'draft2020-12/meta/applicator.json' => 'https://json-schema.org/draft/2020-12/meta/applicator', 'draft2020-12/meta/content.json' => 'https://json-schema.org/draft/2020-12/meta/content', 'draft2020-12/meta/core.json' => 'https://json-schema.org/draft/2020-12/meta/core', 'draft2020-12/meta/format-annotation.json' => 'https://json-schema.org/draft/2020-12/meta/format-annotation', 'draft2020-12/meta/format-assertion.json' => 'https://json-schema.org/draft/2020-12/meta/format-assertion', 'draft2020-12/meta/meta-data.json' => 'https://json-schema.org/draft/2020-12/meta/meta-data', 'draft2020-12/meta/unevaluated.json' => 'https://json-schema.org/draft/2020-12/meta/unevaluated', 'draft2020-12/meta/validation.json' => 'https://json-schema.org/draft/2020-12/meta/validation', 'draft2020-12/output/schema.json' => 'https://json-schema.org/draft/2020-12/output/schema', 'draft2020-12/schema.json' => 'https://json-schema.org/draft/2020-12/schema', 'draft2019-09/meta/applicator.json' => 'https://json-schema.org/draft/2019-09/meta/applicator', 'draft2019-09/meta/content.json' => 'https://json-schema.org/draft/2019-09/meta/content', 'draft2019-09/meta/core.json' => 'https://json-schema.org/draft/2019-09/meta/core', 'draft2019-09/meta/format.json' => 'https://json-schema.org/draft/2019-09/meta/format', 'draft2019-09/meta/meta-data.json' => 'https://json-schema.org/draft/2019-09/meta/meta-data', 'draft2019-09/meta/validation.json' => 'https://json-schema.org/draft/2019-09/meta/validation', 'draft2019-09/output/schema.json' => 'https://json-schema.org/draft/2019-09/output/schema', 'draft2019-09/schema.json' => 'https://json-schema.org/draft/2019-09/schema', 'draft7/schema.json' => 'http://json-schema.org/draft-07/schema#', 'draft6/schema.json' => 'http://json-schema.org/draft-06/schema#', 'draft4/schema.json' => 'http://json-schema.org/draft-04/schema#', 'LICENSE' => 'https://raw.githubusercontent.com/json-schema-org/json-schema-spec/main/LICENSE', ); my $js = JSON::Schema::Modern->new(validate_formats => 1); my %checksums; foreach my $target (keys %files) { my $source_uri = $files{$target}; $target = path('share', $target); $target->parent->mkpath; my $response = HTTP::Tiny->new->get($source_uri); die "Failed to fetch $source_uri: $response->{status} $response->{reason}" if not $response->{success}; $target->spew_raw($response->{content}); $checksums{$target} = md5_hex($response->{content}); next if $target->basename eq 'LICENSE'; my $document = $js->get_document($source_uri); print '# validating ', $document->canonical_uri, "\n" if $ENV{DEBUG}; my $result = $document->validate; die $result->dump if not $result->valid; } # compute checksums and record them in the test path('t/checksums.t')->edit_raw(sub { m/^__DATA__$/mg; $_ = substr($_, 0, pos()+1).join("\n", map $_.' '.$checksums{$_}, sort keys %checksums)."\n"; }); JSON-Schema-Modern-0.609/weaver.ini000640 000766 000024 00000001547 15001302761 017164 0ustar00etherstaff000000 000000 ; this section is woven into the beginning of the document, but inc::AppendSection below later ; plucks it out of its position and appends it to the generic SUPPORT section that is subsequently ; generated by the plugin bundle. [GenerateSection / SUPPORT] main_module_only = 0 text = =for stopwords OpenAPI text = text = You can also find me on the L and L, which are also great resources for finding help. ; ditto [GenerateSection / COPYRIGHT AND LICENCE] main_module_only = 0 text = Some schema files have their own licence, in share/LICENSE. [@Author::ETHER] [=inc::AppendSection / AppendSupport] header_re = ^SUPPORT$ match_anywhere = 1 action = append [=inc::AppendSection / AppendCopyright] header_re = ^COPYRIGHT match_anywhere = 1 action = append JSON-Schema-Modern-0.609/LICENCE000644 000766 000024 00000046440 15001302761 016164 0ustar00etherstaff000000 000000 This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2020 by Karen Etheridge. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Perl Artistic License 1.0 --- This software is Copyright (c) 2020 by Karen Etheridge. This is free software, licensed under: The Perl Artistic License 1.0 The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End JSON-Schema-Modern-0.609/META.json000644 000766 000024 00000151661 15001302761 016622 0ustar00etherstaff000000 000000 { "abstract" : "Validate data against a schema using a JSON Schema", "author" : [ "Karen Etheridge " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010", "keywords" : [ "JSON", "Schema", "validator", "data", "validation", "structure", "specification" ], "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "JSON-Schema-Modern", "no_index" : { "directory" : [ "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "Module::Build::Tiny" : "0.034", "perl" : "5.020" } }, "develop" : { "recommends" : { "Dist::Zilla::PluginBundle::Author::ETHER" : "0.166", "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007" }, "requires" : { "Data::Validate::Domain" : "0.13", "DateTime::Format::RFC3339" : "0", "Email::Address::XS" : "1.04", "Encode" : "0", "File::Spec" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Net::IDN::Encode" : "0", "Pod::Wordlist" : "0", "Sereal" : "0", "Test::CPAN::Changes" : "0.19", "Test::CPAN::Meta" : "0", "Test::CleanNamespaces" : "0.15", "Test::EOL" : "0", "Test::Kwalitee" : "1.21", "Test::MinimumVersion" : "0", "Test::Mojibake" : "0", "Test::More" : "0.96", "Test::NoTabs" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage::TrustMe" : "0.002001", "Test::Portability::Files" : "0", "Test::Spelling" : "0.17", "Time::Moment" : "0" } }, "runtime" : { "requires" : { "B" : "0", "Carp" : "0", "Cpanel::JSON::XS" : "4.38", "Digest::MD5" : "0", "Exporter" : "0", "Feature::Compat::Try" : "0", "File::ShareDir" : "0", "Getopt::Long::Descriptive" : "0", "JSON::PP" : "4.11", "List::Util" : "1.55", "MIME::Base64" : "0", "Math::BigFloat" : "0", "Math::BigInt" : "1.999701", "Mojo::JSON" : "0", "Mojo::JSON::Pointer" : "0", "Mojo::Message::Response" : "0", "Mojo::URL" : "0", "Mojolicious" : "7.87", "Moo" : "0", "Moo::Role" : "0", "MooX::TypeTiny" : "0.002002", "Path::Tiny" : "0", "Ref::Util" : "0.100", "Safe::Isa" : "1.000008", "Scalar::Util" : "0", "Storable" : "0", "Sub::Install" : "0", "Types::Common::Numeric" : "0", "Types::Standard" : "1.016003", "autovivification" : "0", "builtin::compat" : "0.003003", "constant" : "0", "experimental" : "0.026", "if" : "0", "namespace::clean" : "0", "open" : "0", "overload" : "0", "perl" : "v5.20.0", "stable" : "0.031", "strict" : "0", "strictures" : "2", "warnings" : "0" }, "suggests" : { "Class::XSAccessor" : "0", "Data::Validate::Domain" : "0.13", "DateTime::Format::RFC3339" : "0", "Email::Address::XS" : "1.04", "Net::IDN::Encode" : "0", "Ref::Util::XS" : "0", "Sereal" : "0", "Time::Moment" : "0", "Type::Tiny::XS" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "CPAN::Meta::Check" : "0.011", "CPAN::Meta::Requirements" : "0", "Data::Dumper" : "0", "File::Spec" : "0", "Math::BigInt" : "1.999701", "Module::Metadata" : "0", "Term::ANSIColor" : "0", "Test2::API" : "0", "Test::Deep" : "0", "Test::Deep::UnorderedPairs" : "0", "Test::Fatal" : "0", "Test::File::ShareDir" : "0", "Test::JSON::Schema::Acceptance" : "1.021", "Test::Memory::Cycle" : "0", "Test::More" : "0.96", "Test::Needs" : "0", "Test::Warnings" : "0", "Test::Without::Module" : "0.19", "lib" : "0", "perl" : "v5.20.0", "utf8" : "0" } }, "x_Dist_Zilla" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::Authority" : "1.009", "Dist::Zilla::Plugin::AutoMetaResources" : "0", "Dist::Zilla::Plugin::AutoPrereqs" : "5.038", "Dist::Zilla::Plugin::Breaks" : "0", "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : "0.004", "Dist::Zilla::Plugin::CheckIssues" : "0", "Dist::Zilla::Plugin::CheckMetaResources" : "0", "Dist::Zilla::Plugin::CheckPrereqsIndexed" : "0.019", "Dist::Zilla::Plugin::CheckSelfDependency" : "0", "Dist::Zilla::Plugin::CheckStrictVersion" : "0", "Dist::Zilla::Plugin::ConfirmRelease" : "0", "Dist::Zilla::Plugin::CopyFilesFromRelease" : "0", "Dist::Zilla::Plugin::EnsureLatestPerl" : "0", "Dist::Zilla::Plugin::ExecDir" : "0", "Dist::Zilla::Plugin::FileFinder::ByName" : "0", "Dist::Zilla::Plugin::GenerateFile::FromShareDir" : "0", "Dist::Zilla::Plugin::Git::Check" : "0", "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch" : "0.004", "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts" : "0", "Dist::Zilla::Plugin::Git::Commit" : "2.020", "Dist::Zilla::Plugin::Git::Contributors" : "0.029", "Dist::Zilla::Plugin::Git::Describe" : "0.004", "Dist::Zilla::Plugin::Git::GatherDir" : "2.016", "Dist::Zilla::Plugin::Git::Push" : "0", "Dist::Zilla::Plugin::Git::Remote::Check" : "0", "Dist::Zilla::Plugin::Git::Tag" : "0", "Dist::Zilla::Plugin::GitHub::Update" : "0.40", "Dist::Zilla::Plugin::GithubMeta" : "0.54", "Dist::Zilla::Plugin::InstallGuide" : "1.200005", "Dist::Zilla::Plugin::Keywords" : "0.004", "Dist::Zilla::Plugin::License" : "5.038", "Dist::Zilla::Plugin::Manifest" : "0", "Dist::Zilla::Plugin::MetaConfig" : "0", "Dist::Zilla::Plugin::MetaJSON" : "0", "Dist::Zilla::Plugin::MetaNoIndex" : "0", "Dist::Zilla::Plugin::MetaProvides::Package" : "1.15000002", "Dist::Zilla::Plugin::MetaTests" : "0", "Dist::Zilla::Plugin::MetaYAML" : "0", "Dist::Zilla::Plugin::MinimumPerl" : "1.006", "Dist::Zilla::Plugin::ModuleBuildTiny" : "0.012", "Dist::Zilla::Plugin::MojibakeTests" : "0.8", "Dist::Zilla::Plugin::NextRelease" : "5.033", "Dist::Zilla::Plugin::PodSyntaxTests" : "5.040", "Dist::Zilla::Plugin::PodWeaver" : "4.008", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::Plugin::Prereqs::AuthorDeps" : "0.006", "Dist::Zilla::Plugin::Prereqs::Soften" : "0", "Dist::Zilla::Plugin::PromptIfStale" : "0", "Dist::Zilla::Plugin::Readme" : "0", "Dist::Zilla::Plugin::ReadmeAnyFromPod" : "0.142180", "Dist::Zilla::Plugin::RewriteVersion::Transitional" : "0.006", "Dist::Zilla::Plugin::Run::AfterBuild" : "0.041", "Dist::Zilla::Plugin::Run::AfterRelease" : "0.038", "Dist::Zilla::Plugin::Run::BeforeRelease" : "0", "Dist::Zilla::Plugin::RunExtraTests" : "0.024", "Dist::Zilla::Plugin::ShareDir" : "0", "Dist::Zilla::Plugin::StaticInstall" : "0.005", "Dist::Zilla::Plugin::Test::CPAN::Changes" : "0.012", "Dist::Zilla::Plugin::Test::ChangesHasContent" : "0", "Dist::Zilla::Plugin::Test::CheckBreaks" : "0", "Dist::Zilla::Plugin::Test::CleanNamespaces" : "0.006", "Dist::Zilla::Plugin::Test::Compile" : "2.039", "Dist::Zilla::Plugin::Test::EOL" : "0.17", "Dist::Zilla::Plugin::Test::Kwalitee" : "2.10", "Dist::Zilla::Plugin::Test::MinimumVersion" : "2.000010", "Dist::Zilla::Plugin::Test::NoTabs" : "0.08", "Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe" : "0", "Dist::Zilla::Plugin::Test::PodSpelling" : "2.006003", "Dist::Zilla::Plugin::Test::Portability" : "2.000007", "Dist::Zilla::Plugin::Test::ReportPrereqs" : "0.022", "Dist::Zilla::Plugin::TestRelease" : "0", "Dist::Zilla::Plugin::UploadToCPAN" : "0", "Dist::Zilla::Plugin::UseUnsafeInc" : "0", "Dist::Zilla::PluginBundle::Author::ETHER" : "0.154", "Dist::Zilla::PluginBundle::Git::VersionManager" : "0.007", "Software::License::Perl_5" : "0" } } }, "provides" : { "JSON::Schema::Modern" : { "file" : "lib/JSON/Schema/Modern.pm", "version" : "0.609" }, "JSON::Schema::Modern::Annotation" : { "file" : "lib/JSON/Schema/Modern/Annotation.pm", "version" : "0.609" }, "JSON::Schema::Modern::Document" : { "file" : "lib/JSON/Schema/Modern/Document.pm", "version" : "0.609" }, "JSON::Schema::Modern::Error" : { "file" : "lib/JSON/Schema/Modern/Error.pm", "version" : "0.609" }, "JSON::Schema::Modern::Result" : { "file" : "lib/JSON/Schema/Modern/Result.pm", "version" : "0.609" }, "JSON::Schema::Modern::ResultNode" : { "file" : "lib/JSON/Schema/Modern/ResultNode.pm", "version" : "0.609" }, "JSON::Schema::Modern::Utilities" : { "file" : "lib/JSON/Schema/Modern/Utilities.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary" : { "file" : "lib/JSON/Schema/Modern/Vocabulary.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::Applicator" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/Applicator.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::Content" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/Content.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::Core" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/Core.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::FormatAnnotation" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/FormatAnnotation.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::FormatAssertion" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/FormatAssertion.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::MetaData" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/MetaData.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::Unevaluated" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/Unevaluated.pm", "version" : "0.609" }, "JSON::Schema::Modern::Vocabulary::Validation" : { "file" : "lib/JSON/Schema/Modern/Vocabulary/Validation.pm", "version" : "0.609" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/karenetheridge/JSON-Schema-Modern/issues" }, "homepage" : "https://github.com/karenetheridge/JSON-Schema-Modern", "repository" : { "type" : "git", "url" : "https://github.com/karenetheridge/JSON-Schema-Modern.git", "web" : "https://github.com/karenetheridge/JSON-Schema-Modern" } }, "version" : "0.609", "x_Dist_Zilla" : { "perl" : { "version" : "5.041010" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::Run::BeforeRelease", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "eval" : [ "do './update-schemas'; die $@ || $! if $@ || $!" ], "fatal_errors" : 1, "quiet" : 0, "version" : "0.050" } }, "name" : "Run::BeforeRelease", "version" : "0.050" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "recommends" } }, "name" : "@Author::ETHER/pluginbundle version", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::PromptIfStale", "config" : { "Dist::Zilla::Plugin::PromptIfStale" : { "check_all_plugins" : 0, "check_all_prereqs" : 0, "modules" : [ "Dist::Zilla::PluginBundle::Author::ETHER" ], "phase" : "build", "run_under_travis" : 0, "skip" : [] } }, "name" : "@Author::ETHER/stale modules, build", "version" : "0.060" }, { "class" : "Dist::Zilla::Plugin::ExecDir", "name" : "@Author::ETHER/ExecDir", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FileFinder::ByName", "name" : "@Author::ETHER/Examples", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Git::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [ "CONTRIBUTING", "INSTALL", "LICENCE", "README.pod", "TODO", "pull_request_template.md" ], "exclude_match" : [], "include_dotfiles" : 0, "prefix" : "", "prune_directory" : [], "root" : "." }, "Dist::Zilla::Plugin::Git::GatherDir" : { "include_untracked" : 0 } }, "name" : "@Author::ETHER/Git::GatherDir", "version" : "2.051" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "@Author::ETHER/MetaYAML", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "@Author::ETHER/MetaJSON", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Readme", "name" : "@Author::ETHER/Readme", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "@Author::ETHER/Manifest", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "@Author::ETHER/License", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::GenerateFile::FromShareDir", "config" : { "Dist::Zilla::Plugin::GenerateFile::FromShareDir" : { "destination_filename" : "CONTRIBUTING", "dist" : "Dist-Zilla-PluginBundle-Author-ETHER", "encoding" : "UTF-8", "has_xs" : 0, "location" : "build", "source_filename" : "CONTRIBUTING" }, "Dist::Zilla::Role::RepoFileInjector" : { "allow_overwrite" : 1, "repo_root" : ".", "version" : "0.009" } }, "name" : "@Author::ETHER/generate CONTRIBUTING", "version" : "0.015" }, { "class" : "Dist::Zilla::Plugin::InstallGuide", "config" : { "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000038", "version" : "0.006" } }, "name" : "@Author::ETHER/InstallGuide", "version" : "1.200014" }, { "class" : "Dist::Zilla::Plugin::Test::Compile", "config" : { "Dist::Zilla::Plugin::Test::Compile" : { "bail_out_on_fail" : 1, "fail_on_warning" : "author", "fake_home" : 0, "filename" : "xt/author/00-compile.t", "module_finder" : [ ":InstallModules" ], "needs_display" : 0, "phase" : "develop", "script_finder" : [ ":PerlExecFiles", "@Author::ETHER/Examples" ], "skips" : [], "switch" : [] } }, "name" : "@Author::ETHER/Test::Compile", "version" : "2.058" }, { "class" : "Dist::Zilla::Plugin::Test::NoTabs", "config" : { "Dist::Zilla::Plugin::Test::NoTabs" : { "filename" : "xt/author/no-tabs.t", "finder" : [ ":InstallModules", ":ExecFiles", "@Author::ETHER/Examples", ":TestFiles", ":ExtraTestFiles" ] } }, "name" : "@Author::ETHER/Test::NoTabs", "version" : "0.15" }, { "class" : "Dist::Zilla::Plugin::Test::EOL", "config" : { "Dist::Zilla::Plugin::Test::EOL" : { "filename" : "xt/author/eol.t", "finder" : [ ":ExecFiles", ":ExtraTestFiles", ":InstallModules", ":TestFiles", "@Author::ETHER/Examples" ], "trailing_whitespace" : 1 } }, "name" : "@Author::ETHER/Test::EOL", "version" : "0.19" }, { "class" : "Dist::Zilla::Plugin::MetaTests", "name" : "@Author::ETHER/MetaTests", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Test::CPAN::Changes", "config" : { "Dist::Zilla::Plugin::Test::CPAN::Changes" : { "changelog" : "Changes", "filename" : "xt/release/cpan-changes.t" } }, "name" : "@Author::ETHER/Test::CPAN::Changes", "version" : "0.013" }, { "class" : "Dist::Zilla::Plugin::Test::ChangesHasContent", "name" : "@Author::ETHER/Test::ChangesHasContent", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::Test::MinimumVersion", "config" : { "Dist::Zilla::Plugin::Test::MinimumVersion" : { "max_target_perl" : "5.020" } }, "name" : "@Author::ETHER/Test::MinimumVersion", "version" : "2.000011" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "@Author::ETHER/PodSyntaxTests", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe", "config" : { "Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe" : { "finder" : [ ":InstallModules" ] } }, "name" : "@Author::ETHER/Test::Pod::Coverage::TrustMe", "version" : "v1.0.1" }, { "class" : "Dist::Zilla::Plugin::Test::PodSpelling", "config" : { "Dist::Zilla::Plugin::Test::PodSpelling" : { "directories" : [ "examples", "lib", "script", "t", "xt" ], "spell_cmd" : "", "stopwords" : [ "irc" ], "wordlist" : "Pod::Wordlist" } }, "name" : "@Author::ETHER/Test::PodSpelling", "version" : "2.007006" }, { "class" : "Dist::Zilla::Plugin::Test::Kwalitee", "config" : { "Dist::Zilla::Plugin::Test::Kwalitee" : { "filename" : "xt/author/kwalitee.t", "skiptest" : [] } }, "name" : "@Author::ETHER/Test::Kwalitee", "version" : "2.12" }, { "class" : "Dist::Zilla::Plugin::MojibakeTests", "name" : "@Author::ETHER/MojibakeTests", "version" : "0.8" }, { "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", "name" : "@Author::ETHER/Test::ReportPrereqs", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::Test::Portability", "config" : { "Dist::Zilla::Plugin::Test::Portability" : { "options" : "" } }, "name" : "@Author::ETHER/Test::Portability", "version" : "2.001003" }, { "class" : "Dist::Zilla::Plugin::Test::CleanNamespaces", "config" : { "Dist::Zilla::Plugin::Test::CleanNamespaces" : { "filename" : "xt/author/clean-namespaces.t", "skips" : [] } }, "name" : "@Author::ETHER/Test::CleanNamespaces", "version" : "0.006" }, { "class" : "Dist::Zilla::Plugin::Git::Describe", "name" : "@Author::ETHER/Git::Describe", "version" : "0.008" }, { "class" : "Dist::Zilla::Plugin::PodWeaver", "config" : { "Dist::Zilla::Plugin::PodWeaver" : { "finder" : [ ":InstallModules", ":PerlExecFiles" ], "plugins" : [ { "class" : "Pod::Weaver::Section::GenerateSection", "name" : "SUPPORT", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::GenerateSection", "name" : "COPYRIGHT AND LICENCE", "version" : "4.020" }, { "class" : "Pod::Weaver::Plugin::EnsurePod5", "name" : "@Author::ETHER/EnsurePod5", "version" : "4.020" }, { "class" : "Pod::Weaver::Plugin::H1Nester", "name" : "@Author::ETHER/H1Nester", "version" : "4.020" }, { "class" : "Pod::Weaver::Plugin::SingleEncoding", "name" : "@Author::ETHER/SingleEncoding", "version" : "4.020" }, { "class" : "Pod::Weaver::Plugin::Transformer", "name" : "@Author::ETHER/List", "version" : "4.020" }, { "class" : "Pod::Weaver::Plugin::Transformer", "name" : "@Author::ETHER/Verbatim", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/header", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Name", "name" : "@Author::ETHER/Name", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Version", "name" : "@Author::ETHER/Version", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/prelude", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "SYNOPSIS", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "DESCRIPTION", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Generic", "name" : "OVERVIEW", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "ATTRIBUTES", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "METHODS", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "FUNCTIONS", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Collect", "name" : "TYPES", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Leftovers", "name" : "@Author::ETHER/Leftovers", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/postlude", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::GenerateSection", "name" : "@Author::ETHER/generate SUPPORT", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Authors", "name" : "@Author::ETHER/Authors", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::AllowOverride", "name" : "@Author::ETHER/allow override AUTHOR", "version" : "0.05" }, { "class" : "Pod::Weaver::Section::Contributors", "name" : "@Author::ETHER/Contributors", "version" : "0.009" }, { "class" : "Pod::Weaver::Section::Legal", "name" : "@Author::ETHER/Legal", "version" : "4.020" }, { "class" : "Pod::Weaver::Section::Region", "name" : "@Author::ETHER/footer", "version" : "4.020" }, { "class" : "inc::AppendSection", "name" : "AppendSupport", "version" : null }, { "class" : "inc::AppendSection", "name" : "AppendCopyright", "version" : null } ] } }, "name" : "@Author::ETHER/PodWeaver", "version" : "4.010" }, { "class" : "Dist::Zilla::Plugin::GithubMeta", "name" : "@Author::ETHER/GithubMeta", "version" : "0.58" }, { "class" : "Dist::Zilla::Plugin::AutoMetaResources", "name" : "@Author::ETHER/AutoMetaResources", "version" : "1.21" }, { "class" : "Dist::Zilla::Plugin::Authority", "name" : "@Author::ETHER/Authority", "version" : "1.009" }, { "class" : "Dist::Zilla::Plugin::MetaNoIndex", "name" : "@Author::ETHER/MetaNoIndex", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Package", "config" : { "Dist::Zilla::Plugin::MetaProvides::Package" : { "finder" : [ ":InstallModules" ], "finder_objects" : [ { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.032" } ], "include_underscores" : 0 }, "Dist::Zilla::Role::MetaProvider::Provider" : { "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", "inherit_missing" : 0, "inherit_version" : 0, "meta_noindex" : 1 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000038", "version" : "0.006" } }, "name" : "@Author::ETHER/MetaProvides::Package", "version" : "2.004003" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "@Author::ETHER/MetaConfig", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Keywords", "config" : { "Dist::Zilla::Plugin::Keywords" : { "keywords" : [ "JSON", "Schema", "validator", "data", "validation", "structure", "specification" ] } }, "name" : "@Author::ETHER/Keywords", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::UseUnsafeInc", "config" : { "Dist::Zilla::Plugin::UseUnsafeInc" : { "dot_in_INC" : 0 } }, "name" : "@Author::ETHER/UseUnsafeInc", "version" : "0.002" }, { "class" : "Dist::Zilla::Plugin::AutoPrereqs", "name" : "@Author::ETHER/AutoPrereqs", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Prereqs::AuthorDeps", "name" : "@Author::ETHER/Prereqs::AuthorDeps", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::MinimumPerl", "name" : "@Author::ETHER/MinimumPerl", "version" : "1.006" }, { "class" : "Dist::Zilla::Plugin::ModuleBuildTiny", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 9 } }, "name" : "@Author::ETHER/ModuleBuildTiny", "version" : "0.019" }, { "class" : "Dist::Zilla::Plugin::Git::Contributors", "config" : { "Dist::Zilla::Plugin::Git::Contributors" : { "git_version" : "2.47.1", "include_authors" : 0, "include_releaser" : 1, "order_by" : "commits", "paths" : [] } }, "name" : "@Author::ETHER/Git::Contributors", "version" : "0.038" }, { "class" : "Dist::Zilla::Plugin::StaticInstall", "config" : { "Dist::Zilla::Plugin::StaticInstall" : { "dry_run" : 0, "mode" : "off" } }, "name" : "@Author::ETHER/StaticInstall", "version" : "0.012" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 9 } }, "name" : "@Author::ETHER/RunExtraTests", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::CheckSelfDependency", "config" : { "Dist::Zilla::Plugin::CheckSelfDependency" : { "finder" : [ ":InstallModules" ] }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000038", "version" : "0.006" } }, "name" : "@Author::ETHER/CheckSelfDependency", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::Run::AfterBuild", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "fatal_errors" : 1, "quiet" : 1, "run" : [ "bash -c \"test -e .ackrc && grep -q -- '--ignore-dir=.latest' .ackrc || echo '--ignore-dir=.latest' >> .ackrc; if [[ `dirname '%d'` != .build ]]; then test -e .ackrc && grep -q -- '--ignore-dir=%d' .ackrc || echo '--ignore-dir=%d' >> .ackrc; fi\"" ], "version" : "0.050" } }, "name" : "@Author::ETHER/.ackrc", "version" : "0.050" }, { "class" : "Dist::Zilla::Plugin::Run::AfterBuild", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "eval" : [ "if ('%d' =~ /^%n-[.[:xdigit:]]+$/) { unlink '.latest'; symlink '%d', '.latest'; }" ], "fatal_errors" : 0, "quiet" : 1, "version" : "0.050" } }, "name" : "@Author::ETHER/.latest", "version" : "0.050" }, { "class" : "Dist::Zilla::Plugin::CheckStrictVersion", "name" : "@Author::ETHER/CheckStrictVersion", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::CheckMetaResources", "name" : "@Author::ETHER/CheckMetaResources", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::EnsureLatestPerl", "config" : { "Dist::Zilla::Plugin::EnsureLatestPerl" : { "Module::CoreList" : "5.20250414" } }, "name" : "@Author::ETHER/EnsureLatestPerl", "version" : "0.010" }, { "class" : "Dist::Zilla::Plugin::PromptIfStale", "config" : { "Dist::Zilla::Plugin::PromptIfStale" : { "check_all_plugins" : 1, "check_all_prereqs" : 1, "modules" : [], "phase" : "release", "run_under_travis" : 0, "skip" : [] } }, "name" : "@Author::ETHER/stale modules, release", "version" : "0.060" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." } }, "name" : "@Author::ETHER/initial check", "version" : "2.051" }, { "class" : "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts", "config" : { "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." } }, "name" : "@Author::ETHER/Git::CheckFor::MergeConflicts", "version" : "0.014" }, { "class" : "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch", "config" : { "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." } }, "name" : "@Author::ETHER/Git::CheckFor::CorrectBranch", "version" : "0.014" }, { "class" : "Dist::Zilla::Plugin::Git::Remote::Check", "name" : "@Author::ETHER/Git::Remote::Check", "version" : "0.1.2" }, { "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed", "name" : "@Author::ETHER/CheckPrereqsIndexed", "version" : "0.022" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "@Author::ETHER/TestRelease", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." } }, "name" : "@Author::ETHER/after tests", "version" : "2.051" }, { "class" : "Dist::Zilla::Plugin::CheckIssues", "name" : "@Author::ETHER/CheckIssues", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "@Author::ETHER/UploadToCPAN", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "config" : { "Dist::Zilla::Plugin::CopyFilesFromRelease" : { "filename" : [ "CONTRIBUTING", "INSTALL", "LICENCE", "LICENSE", "ppport.h" ], "match" : [] } }, "name" : "@Author::ETHER/copy generated files", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "@Author::ETHER/ReadmeAnyFromPod", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "recommends" } }, "name" : "@Author::ETHER/@Git::VersionManager/pluginbundle version", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::RewriteVersion::Transitional", "config" : { "Dist::Zilla::Plugin::RewriteVersion" : { "add_tarball_name" : 0, "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 1, "skip_version_provider" : 0 }, "Dist::Zilla::Plugin::RewriteVersion::Transitional" : {} }, "name" : "@Author::ETHER/@Git::VersionManager/RewriteVersion::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Update", "name" : "@Author::ETHER/@Git::VersionManager/MetaProvides::Update", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "config" : { "Dist::Zilla::Plugin::CopyFilesFromRelease" : { "filename" : [ "Changes" ], "match" : [] } }, "name" : "@Author::ETHER/@Git::VersionManager/CopyFilesFromRelease", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [ "." ], "commit_msg" : "%N-%v%t%n%n%c", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "CONTRIBUTING", "Changes", "INSTALL", "LICENCE", "README.pod" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::ETHER/@Git::VersionManager/release snapshot", "version" : "2.051" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "config" : { "Dist::Zilla::Plugin::Git::Tag" : { "branch" : null, "changelog" : "Changes", "signed" : 0, "tag" : "v0.609", "tag_format" : "v%V", "tag_message" : "v%v%t" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::ETHER/@Git::VersionManager/Git::Tag", "version" : "2.051" }, { "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional", "config" : { "Dist::Zilla::Plugin::BumpVersionAfterRelease" : { "finders" : [ ":InstallModules" ], "global" : 1, "munge_makefile_pl" : 1 }, "Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional" : {} }, "name" : "@Author::ETHER/@Git::VersionManager/BumpVersionAfterRelease::Transitional", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::NextRelease", "name" : "@Author::ETHER/@Git::VersionManager/NextRelease", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "increment $VERSION after %v release", "signoff" : 0 }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Build.PL", "Changes", "Makefile.PL" ], "allow_dirty_match" : [ "(?^:^lib/.*\\.pm$)" ], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::ETHER/@Git::VersionManager/post-release commit", "version" : "2.051" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "x_Dist_Zilla", "type" : "requires" } }, "name" : "@Author::ETHER/@Git::VersionManager/prereqs for @Git::VersionManager", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "config" : { "Dist::Zilla::Plugin::Git::Push" : { "push_to" : [ "origin" ], "remotes_must_exist" : 1 }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.47.1", "repo_root" : "." } }, "name" : "@Author::ETHER/Git::Push", "version" : "2.051" }, { "class" : "Dist::Zilla::Plugin::GitHub::Update", "config" : { "Dist::Zilla::Plugin::GitHub::Update" : { "metacpan" : 1 } }, "name" : "@Author::ETHER/GitHub::Update", "version" : "0.49" }, { "class" : "Dist::Zilla::Plugin::Run::AfterRelease", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "fatal_errors" : 0, "quiet" : 0, "run" : [ "REDACTED" ], "version" : "0.050" } }, "name" : "@Author::ETHER/install release", "version" : "0.050" }, { "class" : "Dist::Zilla::Plugin::Run::AfterRelease", "config" : { "Dist::Zilla::Plugin::Run::Role::Runner" : { "eval" : [ "print \"release complete!\\xa\"" ], "fatal_errors" : 1, "quiet" : 1, "version" : "0.050" } }, "name" : "@Author::ETHER/release complete", "version" : "0.050" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "@Author::ETHER/ConfirmRelease", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "x_Dist_Zilla", "type" : "requires" } }, "name" : "@Author::ETHER/prereqs for @Author::ETHER", "version" : "6.032" }, { "class" : "inc::CheckConflicts", "name" : "=inc::CheckConflicts", "version" : null }, { "class" : "Dist::Zilla::Plugin::ShareDir", "name" : "ShareDir", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "runtime", "type" : "requires" } }, "name" : "RuntimeRequires", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "runtime", "type" : "suggests" } }, "name" : "RuntimeSuggests", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::Prereqs::Soften", "config" : { "Dist::Zilla::Plugin::Prereqs::Soften" : { "copy_to" : [ "develop.requires" ], "modules" : [ "Time::Moment", "DateTime::Format::RFC3339", "Data::Validate::Domain", "Email::Address::XS", "Net::IDN::Encode", "Sereal" ], "modules_from_features" : null, "to_relationship" : "suggests" } }, "name" : "Prereqs::Soften", "version" : "0.006003" }, { "class" : "Dist::Zilla::Plugin::Breaks", "name" : "Breaks", "version" : "0.004" }, { "class" : "Dist::Zilla::Plugin::Test::CheckBreaks", "config" : { "Dist::Zilla::Plugin::Test::CheckBreaks" : { "conflicts_module" : [], "no_forced_deps" : 0 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000038", "version" : "0.006" } }, "name" : "Test::CheckBreaks", "version" : "0.020" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExtraTestFiles", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":PerlExecFiles", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "6.032" }, { "class" : "Dist::Zilla::Plugin::VerifyPhases", "name" : "@Author::ETHER/PHASE VERIFICATION", "version" : "0.016" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : 0 }, "version" : "6.032" } }, "x_authority" : "cpan:ETHER", "x_breaks" : { "JSON::Schema::Modern::Document::OpenAPI" : "< 0.079", "JSON::Schema::Modern::Vocabulary::OpenAPI" : "< 0.080", "Mojolicious::Plugin::OpenAPI::Modern" : "< 0.014", "OpenAPI::Modern" : "< 0.077", "Test::Mojo::Role::OpenAPI::Modern" : "< 0.007" }, "x_generated_by_perl" : "v5.41.10", "x_serialization_backend" : "Cpanel::JSON::XS version 4.39", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later", "x_static_install" : 0, "x_use_unsafe_inc" : 0 } JSON-Schema-Modern-0.609/share/000770 000766 000024 00000000000 15001302761 016266 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/dist.ini000640 000766 000024 00000005616 15001302761 016637 0ustar00etherstaff000000 000000 name = JSON-Schema-Modern author = Karen Etheridge copyright_holder = Karen Etheridge copyright_year = 2020 license = Perl_5 ; ATTENTION DISTRO REPACKAGERS: do NOT use fresh copies of these files ; from their source; it is important to include the original versions ; of the files as they were packaged with this cpan distribution, or ; surprising behaviour may occur. [Run::BeforeRelease] eval = do './update-schemas'; die $@ || $! if $@ || $! [@Author::ETHER] :version = 0.154 bugtracker = github installer = ModuleBuildTiny Test::MinimumVersion.max_target_perl = 5.020 ; may go higher later on Git::GatherDir.exclude_filename = pull_request_template.md Test::ReportPrereqs.include[0] = JSON::PP Test::ReportPrereqs.include[1] = Cpanel::JSON::XS Test::ReportPrereqs.include[2] = JSON::XS Test::ReportPrereqs.include[3] = Mojolicious Test::ReportPrereqs.include[4] = Sereal::Encoder Test::ReportPrereqs.include[5] = Sereal::Decoder Test::ReportPrereqs.include[6] = Math::BigInt Test::ReportPrereqs.include[7] = Math::BigFloat Test::ReportPrereqs.include[8] = builtin Test::ReportPrereqs.include[9] = builtin::Backport -remove = Test::Pod::No404s ; some vocabulary class URIs now return 403 Forbidden StaticInstall.mode = off [=inc::CheckConflicts] [ShareDir] dir = share [Prereqs / RuntimeRequires] Mojolicious = 7.87 ; Mojo::JSON::JSON_XS Math::BigInt = 1.999701 ; bdiv and bmod fixes Email::Address::XS = 1.04 ; softened later Sereal = 0 ; softened later JSON::PP = 4.11 ; softened later Cpanel::JSON::XS = 4.38 ; softened later builtin::compat = 0.003003 [Prereqs / RuntimeSuggests] Class::XSAccessor = 0 Type::Tiny::XS = 0 Ref::Util::XS = 0 [Prereqs::Soften] to_relationship = suggests copy_to = develop.requires module = Time::Moment ; required for format 'date-time', 'date', 'time' module = DateTime::Format::RFC3339 ; required for edge cases for format 'date-time' module = Data::Validate::Domain ; required for format 'hostname', 'idn-hostname' module = Email::Address::XS ; required for format 'email', 'idn-email' module = Net::IDN::Encode ; required for format 'idn-hostname' module = Sereal ; required for serialization support JSON::PP = 4.11 ; support for core bools Cpanel::JSON::XS = 4.38 ; support for core bools [Breaks] JSON::Schema::Modern::Vocabulary::OpenAPI = < 0.080 ; discriminator traversal error status JSON::Schema::Modern::Document::OpenAPI = < 0.079 ; error mode; traverse now accepts a third argument; identifiers is a hashref; anchors are no longer stored as separate resources OpenAPI::Modern = < 0.077 ; ::Result boolean overload Mojolicious::Plugin::OpenAPI::Modern = < 0.014 ; ::Result boolean overload Test::Mojo::Role::OpenAPI::Modern = < 0.007 ; ::Result boolean overload [Test::CheckBreaks] JSON-Schema-Modern-0.609/share/LICENSE000640 000766 000024 00000026733 15001302761 017305 0ustar00etherstaff000000 000000 Copyright (c) 2022 JSON Schema Specification Authors Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: Licensed under the Academic Free License version 3.0 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: a) to reproduce the Original Work in copies, either alone or as part of a collective work; b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; c) to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; d) to perform the Original Work publicly; and e) to display the Original Work publicly. 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. 3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. 4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. 5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). 6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. 8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. 9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). 10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. 12) Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. 13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. 14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. 16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. JSON-Schema-Modern-0.609/share/draft6/000770 000766 000024 00000000000 15001302761 017454 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft2020-12/000770 000766 000024 00000000000 15001302761 020112 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft7/000770 000766 000024 00000000000 15001302761 017455 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft2019-09/000770 000766 000024 00000000000 15001302761 020130 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft4/000770 000766 000024 00000000000 15001302761 017452 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft4/schema.json000640 000766 000024 00000010405 15001302761 021604 0ustar00etherstaff000000 000000 { "id": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#", "description": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "positiveInteger": { "type": "integer", "minimum": 0 }, "positiveIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "type": "object", "properties": { "id": { "type": "string" }, "$schema": { "type": "string" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": {}, "multipleOf": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "maxLength": { "$ref": "#/definitions/positiveInteger" }, "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": {} }, "maxItems": { "$ref": "#/definitions/positiveInteger" }, "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "maxProperties": { "$ref": "#/definitions/positiveInteger" }, "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "format": { "type": "string" }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "dependencies": { "exclusiveMaximum": [ "maximum" ], "exclusiveMinimum": [ "minimum" ] }, "default": {} } JSON-Schema-Modern-0.609/share/draft2019-09/output/000770 000766 000024 00000000000 15001302761 021470 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft2019-09/meta/000770 000766 000024 00000000000 15001302761 021056 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft2019-09/schema.json000640 000766 000024 00000003371 15001302761 022266 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/schema", "$vocabulary": { "https://json-schema.org/draft/2019-09/vocab/core": true, "https://json-schema.org/draft/2019-09/vocab/applicator": true, "https://json-schema.org/draft/2019-09/vocab/validation": true, "https://json-schema.org/draft/2019-09/vocab/meta-data": true, "https://json-schema.org/draft/2019-09/vocab/format": false, "https://json-schema.org/draft/2019-09/vocab/content": true }, "$recursiveAnchor": true, "title": "Core and Validation specifications meta-schema", "allOf": [ {"$ref": "meta/core"}, {"$ref": "meta/applicator"}, {"$ref": "meta/validation"}, {"$ref": "meta/meta-data"}, {"$ref": "meta/format"}, {"$ref": "meta/content"} ], "type": ["object", "boolean"], "properties": { "definitions": { "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.", "type": "object", "additionalProperties": { "$recursiveRef": "#" }, "default": {} }, "dependencies": { "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"", "type": "object", "additionalProperties": { "anyOf": [ { "$recursiveRef": "#" }, { "$ref": "meta/validation#/$defs/stringArray" } ] } } } } JSON-Schema-Modern-0.609/share/draft2019-09/meta/content.json000640 000766 000024 00000000645 15001302761 023427 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/meta/content", "$recursiveAnchor": true, "title": "Content vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "contentMediaType": { "type": "string" }, "contentEncoding": { "type": "string" }, "contentSchema": { "$recursiveRef": "#" } } } JSON-Schema-Modern-0.609/share/draft2019-09/meta/applicator.json000640 000766 000024 00000003341 15001302761 024107 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/meta/applicator", "$recursiveAnchor": true, "title": "Applicator vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "additionalItems": { "$recursiveRef": "#" }, "unevaluatedItems": { "$recursiveRef": "#" }, "items": { "anyOf": [ { "$recursiveRef": "#" }, { "$ref": "#/$defs/schemaArray" } ] }, "contains": { "$recursiveRef": "#" }, "additionalProperties": { "$recursiveRef": "#" }, "unevaluatedProperties": { "$recursiveRef": "#" }, "properties": { "type": "object", "additionalProperties": { "$recursiveRef": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$recursiveRef": "#" }, "propertyNames": { "format": "regex" }, "default": {} }, "dependentSchemas": { "type": "object", "additionalProperties": { "$recursiveRef": "#" } }, "propertyNames": { "$recursiveRef": "#" }, "if": { "$recursiveRef": "#" }, "then": { "$recursiveRef": "#" }, "else": { "$recursiveRef": "#" }, "allOf": { "$ref": "#/$defs/schemaArray" }, "anyOf": { "$ref": "#/$defs/schemaArray" }, "oneOf": { "$ref": "#/$defs/schemaArray" }, "not": { "$recursiveRef": "#" } }, "$defs": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$recursiveRef": "#" } } } } JSON-Schema-Modern-0.609/share/draft2019-09/meta/meta-data.json000640 000766 000024 00000001432 15001302761 023605 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/meta/meta-data", "$recursiveAnchor": true, "title": "Meta-data vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "default": true, "deprecated": { "type": "boolean", "default": false }, "readOnly": { "type": "boolean", "default": false }, "writeOnly": { "type": "boolean", "default": false }, "examples": { "type": "array", "items": true } } } JSON-Schema-Modern-0.609/share/draft2019-09/meta/core.json000640 000766 000024 00000002636 15001302761 022707 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/meta/core", "$recursiveAnchor": true, "title": "Core vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "$id": { "type": "string", "format": "uri-reference", "$comment": "Non-empty fragments not allowed.", "pattern": "^[^#]*#?$" }, "$schema": { "type": "string", "format": "uri" }, "$anchor": { "type": "string", "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" }, "$ref": { "type": "string", "format": "uri-reference" }, "$recursiveRef": { "type": "string", "format": "uri-reference" }, "$recursiveAnchor": { "type": "boolean", "default": false }, "$vocabulary": { "type": "object", "propertyNames": { "type": "string", "format": "uri" }, "additionalProperties": { "type": "boolean" } }, "$comment": { "type": "string" }, "$defs": { "type": "object", "additionalProperties": { "$recursiveRef": "#" }, "default": {} } } } JSON-Schema-Modern-0.609/share/draft2019-09/meta/validation.json000640 000766 000024 00000005257 15001302761 024113 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/meta/validation", "$recursiveAnchor": true, "title": "Validation vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "multipleOf": { "type": "number", "exclusiveMinimum": 0 }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "number" }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "number" }, "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, "minContains": { "$ref": "#/$defs/nonNegativeInteger", "default": 1 }, "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, "required": { "$ref": "#/$defs/stringArray" }, "dependentRequired": { "type": "object", "additionalProperties": { "$ref": "#/$defs/stringArray" } }, "const": true, "enum": { "type": "array", "items": true }, "type": { "anyOf": [ { "$ref": "#/$defs/simpleTypes" }, { "type": "array", "items": { "$ref": "#/$defs/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] } }, "$defs": { "nonNegativeInteger": { "type": "integer", "minimum": 0 }, "nonNegativeIntegerDefault0": { "$ref": "#/$defs/nonNegativeInteger", "default": 0 }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "default": [] } } } JSON-Schema-Modern-0.609/share/draft2019-09/meta/format.json000640 000766 000024 00000000464 15001302761 023244 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/meta/format", "$recursiveAnchor": true, "title": "Format vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "format": { "type": "string" } } } JSON-Schema-Modern-0.609/share/draft2019-09/output/schema.json000640 000766 000024 00000004174 15001302761 023630 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2019-09/schema", "$id": "https://json-schema.org/draft/2019-09/output/schema", "description": "A schema that validates the minimum requirements for validation output", "oneOf": [ { "$ref": "#/$defs/flag" }, { "$ref": "#/$defs/basic" }, { "$ref": "#/$defs/detailed" }, { "$ref": "#/$defs/verbose" } ], "$defs": { "outputUnit":{ "properties": { "valid": { "type": "boolean" }, "keywordLocation": { "type": "string", "format": "uri-reference" }, "absoluteKeywordLocation": { "type": "string", "format": "uri" }, "instanceLocation": { "type": "string", "format": "uri-reference" }, "errors": { "$ref": "#/$defs/outputUnitArray" }, "annotations": { "$ref": "#/$defs/outputUnitArray" } }, "required": [ "valid", "keywordLocation", "instanceLocation" ], "allOf": [ { "if": { "properties": { "valid": { "const": false } } }, "then": { "required": [ "errors" ] } }, { "if": { "anyOf": [ { "properties": { "keywordLocation": { "pattern": "/\\$ref/" } } }, { "properties": { "keywordLocation": { "pattern": "/\\$recursiveRef/" } } } ] }, "then": { "required": [ "absoluteKeywordLocation" ] } } ] }, "outputUnitArray": { "type": "array", "items": { "$ref": "#/$defs/outputUnit" } }, "flag": { "properties": { "valid": { "type": "boolean" } }, "required": [ "valid" ] }, "basic": { "$ref": "#/$defs/outputUnit" }, "detailed": { "$ref": "#/$defs/outputUnit" }, "verbose": { "$ref": "#/$defs/outputUnit" } } } JSON-Schema-Modern-0.609/share/draft7/schema.json000640 000766 000024 00000011563 15001302761 021615 0ustar00etherstaff000000 000000 { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://json-schema.org/draft-07/schema#", "title": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "nonNegativeInteger": { "type": "integer", "minimum": 0 }, "nonNegativeIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/nonNegativeInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "default": [] } }, "type": ["object", "boolean"], "properties": { "$id": { "type": "string", "format": "uri-reference" }, "$schema": { "type": "string", "format": "uri" }, "$ref": { "type": "string", "format": "uri-reference" }, "$comment": { "type": "string" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": true, "readOnly": { "type": "boolean", "default": false }, "writeOnly": { "type": "boolean", "default": false }, "examples": { "type": "array", "items": true }, "multipleOf": { "type": "number", "exclusiveMinimum": 0 }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "number" }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "number" }, "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "$ref": "#" }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": true }, "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "contains": { "$ref": "#" }, "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "$ref": "#" }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "propertyNames": { "format": "regex" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "propertyNames": { "$ref": "#" }, "const": true, "enum": { "type": "array", "items": true, "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "format": { "type": "string" }, "contentMediaType": { "type": "string" }, "contentEncoding": { "type": "string" }, "if": { "$ref": "#" }, "then": { "$ref": "#" }, "else": { "$ref": "#" }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "default": true } JSON-Schema-Modern-0.609/share/draft2020-12/output/000770 000766 000024 00000000000 15001302761 021452 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft2020-12/meta/000770 000766 000024 00000000000 15001302761 021040 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/share/draft2020-12/schema.json000640 000766 000024 00000004624 15001302761 022252 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/schema", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://json-schema.org/draft/2020-12/vocab/applicator": true, "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, "https://json-schema.org/draft/2020-12/vocab/validation": true, "https://json-schema.org/draft/2020-12/vocab/meta-data": true, "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, "https://json-schema.org/draft/2020-12/vocab/content": true }, "$dynamicAnchor": "meta", "title": "Core and Validation specifications meta-schema", "allOf": [ {"$ref": "meta/core"}, {"$ref": "meta/applicator"}, {"$ref": "meta/unevaluated"}, {"$ref": "meta/validation"}, {"$ref": "meta/meta-data"}, {"$ref": "meta/format-annotation"}, {"$ref": "meta/content"} ], "type": ["object", "boolean"], "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.", "properties": { "definitions": { "$comment": "\"definitions\" has been replaced by \"$defs\".", "type": "object", "additionalProperties": { "$dynamicRef": "#meta" }, "deprecated": true, "default": {} }, "dependencies": { "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.", "type": "object", "additionalProperties": { "anyOf": [ { "$dynamicRef": "#meta" }, { "$ref": "meta/validation#/$defs/stringArray" } ] }, "deprecated": true, "default": {} }, "$recursiveAnchor": { "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".", "$ref": "meta/core#/$defs/anchorString", "deprecated": true }, "$recursiveRef": { "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".", "$ref": "meta/core#/$defs/uriReferenceString", "deprecated": true } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/content.json000640 000766 000024 00000000647 15001302761 023413 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/content", "$dynamicAnchor": "meta", "title": "Content vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "contentEncoding": { "type": "string" }, "contentMediaType": { "type": "string" }, "contentSchema": { "$dynamicRef": "#meta" } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/unevaluated.json000640 000766 000024 00000000626 15001302761 024253 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated", "$dynamicAnchor": "meta", "title": "Unevaluated applicator vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "unevaluatedItems": { "$dynamicRef": "#meta" }, "unevaluatedProperties": { "$dynamicRef": "#meta" } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/format-annotation.json000640 000766 000024 00000000526 15001302761 025375 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation", "$dynamicAnchor": "meta", "title": "Format vocabulary meta-schema for annotation results", "type": ["object", "boolean"], "properties": { "format": { "type": "string" } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/format-assertion.json000640 000766 000024 00000000524 15001302761 025230 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/format-assertion", "$dynamicAnchor": "meta", "title": "Format vocabulary meta-schema for assertion results", "type": ["object", "boolean"], "properties": { "format": { "type": "string" } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/applicator.json000640 000766 000024 00000003030 15001302761 024064 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/applicator", "$dynamicAnchor": "meta", "title": "Applicator vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "prefixItems": { "$ref": "#/$defs/schemaArray" }, "items": { "$dynamicRef": "#meta" }, "contains": { "$dynamicRef": "#meta" }, "additionalProperties": { "$dynamicRef": "#meta" }, "properties": { "type": "object", "additionalProperties": { "$dynamicRef": "#meta" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$dynamicRef": "#meta" }, "propertyNames": { "format": "regex" }, "default": {} }, "dependentSchemas": { "type": "object", "additionalProperties": { "$dynamicRef": "#meta" }, "default": {} }, "propertyNames": { "$dynamicRef": "#meta" }, "if": { "$dynamicRef": "#meta" }, "then": { "$dynamicRef": "#meta" }, "else": { "$dynamicRef": "#meta" }, "allOf": { "$ref": "#/$defs/schemaArray" }, "anyOf": { "$ref": "#/$defs/schemaArray" }, "oneOf": { "$ref": "#/$defs/schemaArray" }, "not": { "$dynamicRef": "#meta" } }, "$defs": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$dynamicRef": "#meta" } } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/meta-data.json000640 000766 000024 00000001432 15001302761 023567 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/meta-data", "$dynamicAnchor": "meta", "title": "Meta-data vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "default": true, "deprecated": { "type": "boolean", "default": false }, "readOnly": { "type": "boolean", "default": false }, "writeOnly": { "type": "boolean", "default": false }, "examples": { "type": "array", "items": true } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/core.json000640 000766 000024 00000002677 15001302761 022676 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/core", "$dynamicAnchor": "meta", "title": "Core vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "$id": { "$ref": "#/$defs/uriReferenceString", "$comment": "Non-empty fragments not allowed.", "pattern": "^[^#]*#?$" }, "$schema": { "$ref": "#/$defs/uriString" }, "$ref": { "$ref": "#/$defs/uriReferenceString" }, "$anchor": { "$ref": "#/$defs/anchorString" }, "$dynamicRef": { "$ref": "#/$defs/uriReferenceString" }, "$dynamicAnchor": { "$ref": "#/$defs/anchorString" }, "$vocabulary": { "type": "object", "propertyNames": { "$ref": "#/$defs/uriString" }, "additionalProperties": { "type": "boolean" } }, "$comment": { "type": "string" }, "$defs": { "type": "object", "additionalProperties": { "$dynamicRef": "#meta" } } }, "$defs": { "anchorString": { "type": "string", "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" }, "uriString": { "type": "string", "format": "uri" }, "uriReferenceString": { "type": "string", "format": "uri-reference" } } } JSON-Schema-Modern-0.609/share/draft2020-12/meta/validation.json000640 000766 000024 00000005257 15001302761 024075 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/meta/validation", "$dynamicAnchor": "meta", "title": "Validation vocabulary meta-schema", "type": ["object", "boolean"], "properties": { "type": { "anyOf": [ { "$ref": "#/$defs/simpleTypes" }, { "type": "array", "items": { "$ref": "#/$defs/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "const": true, "enum": { "type": "array", "items": true }, "multipleOf": { "type": "number", "exclusiveMinimum": 0 }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "number" }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "number" }, "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, "minContains": { "$ref": "#/$defs/nonNegativeInteger", "default": 1 }, "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, "required": { "$ref": "#/$defs/stringArray" }, "dependentRequired": { "type": "object", "additionalProperties": { "$ref": "#/$defs/stringArray" } } }, "$defs": { "nonNegativeInteger": { "type": "integer", "minimum": 0 }, "nonNegativeIntegerDefault0": { "$ref": "#/$defs/nonNegativeInteger", "default": 0 }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "default": [] } } } JSON-Schema-Modern-0.609/share/draft2020-12/output/schema.json000640 000766 000024 00000004503 15001302761 023606 0ustar00etherstaff000000 000000 { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://json-schema.org/draft/2020-12/output/schema", "description": "A schema that validates the minimum requirements for validation output", "anyOf": [ { "$ref": "#/$defs/flag" }, { "$ref": "#/$defs/basic" }, { "$ref": "#/$defs/detailed" }, { "$ref": "#/$defs/verbose" } ], "$defs": { "outputUnit":{ "properties": { "valid": { "type": "boolean" }, "keywordLocation": { "type": "string", "format": "json-pointer" }, "absoluteKeywordLocation": { "type": "string", "format": "uri" }, "instanceLocation": { "type": "string", "format": "json-pointer" }, "error": { "type": "string" }, "errors": { "$ref": "#/$defs/outputUnitArray" }, "annotations": { "$ref": "#/$defs/outputUnitArray" } }, "required": [ "valid", "keywordLocation", "instanceLocation" ], "allOf": [ { "if": { "properties": { "valid": { "const": false } } }, "then": { "anyOf": [ { "required": [ "error" ] }, { "required": [ "errors" ] } ] } }, { "if": { "anyOf": [ { "properties": { "keywordLocation": { "pattern": "/\\$ref/" } } }, { "properties": { "keywordLocation": { "pattern": "/\\$dynamicRef/" } } } ] }, "then": { "required": [ "absoluteKeywordLocation" ] } } ] }, "outputUnitArray": { "type": "array", "items": { "$ref": "#/$defs/outputUnit" } }, "flag": { "properties": { "valid": { "type": "boolean" } }, "required": [ "valid" ] }, "basic": { "$ref": "#/$defs/outputUnit" }, "detailed": { "$ref": "#/$defs/outputUnit" }, "verbose": { "$ref": "#/$defs/outputUnit" } } } JSON-Schema-Modern-0.609/share/draft6/schema.json000640 000766 000024 00000010621 15001302761 021606 0ustar00etherstaff000000 000000 { "$schema": "http://json-schema.org/draft-06/schema#", "$id": "http://json-schema.org/draft-06/schema#", "title": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "nonNegativeInteger": { "type": "integer", "minimum": 0 }, "nonNegativeIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/nonNegativeInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "uniqueItems": true, "default": [] } }, "type": ["object", "boolean"], "properties": { "$id": { "type": "string", "format": "uri-reference" }, "$schema": { "type": "string", "format": "uri" }, "$ref": { "type": "string", "format": "uri-reference" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": {}, "examples": { "type": "array", "items": {} }, "multipleOf": { "type": "number", "exclusiveMinimum": 0 }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "number" }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "number" }, "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "$ref": "#" }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": {} }, "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "contains": { "$ref": "#" }, "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "$ref": "#" }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "propertyNames": { "format": "regex" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "propertyNames": { "$ref": "#" }, "const": {}, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "format": { "type": "string" }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "default": {} } JSON-Schema-Modern-0.609/lib/JSON/000770 000766 000024 00000000000 15001302761 016503 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/lib/JSON/Schema/000770 000766 000024 00000000000 15001302761 017703 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern.pm000640 000766 000024 00000235563 15001302761 021502 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern; # git description: v0.608-6-g0b0c26e9 # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Validate data against a schema using a JSON Schema # KEYWORDS: JSON Schema validator data validation structure specification our $VERSION = '0.609'; use 5.020; # for fc, unicode_strings features use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use Mojo::JSON (); # for JSON_XS, MOJO_NO_JSON_XS environment variables use Carp qw(croak carp); use List::Util 1.55 qw(pairs first uniqint pairmap uniq any); use Ref::Util 0.100 qw(is_ref is_plain_hashref); use builtin::compat qw(refaddr load_module); use Mojo::URL; use Safe::Isa; use Path::Tiny; use Storable 'dclone'; use File::ShareDir 'dist_dir'; use MooX::TypeTiny 0.002002; use Types::Standard 1.016003 qw(Bool Int Str HasMethods Enum InstanceOf HashRef Dict CodeRef Optional Slurpy ArrayRef Undef ClassName Tuple Map); use Digest::MD5 'md5'; use Feature::Compat::Try; use JSON::Schema::Modern::Error; use JSON::Schema::Modern::Result; use JSON::Schema::Modern::Document; use JSON::Schema::Modern::Utilities qw(get_type canonical_uri E abort annotate_self jsonp is_type assert_uri local_annotations); use namespace::clean; our @CARP_NOT = qw( JSON::Schema::Modern::Document JSON::Schema::Modern::Vocabulary JSON::Schema::Modern::Vocabulary::Applicator JSON::Schema::Modern::Document::OpenAPI OpenAPI::Modern ); use constant SPECIFICATION_VERSION_DEFAULT => 'draft2020-12'; use constant SPECIFICATION_VERSIONS_SUPPORTED => [qw(draft4 draft6 draft7 draft2019-09 draft2020-12)]; has specification_version => ( is => 'ro', isa => Enum(SPECIFICATION_VERSIONS_SUPPORTED), coerce => sub { return $_[0] if any { $_[0] eq $_ } SPECIFICATION_VERSIONS_SUPPORTED->@*; my $real = 'draft'.($_[0]//''); (any { $real eq $_ } SPECIFICATION_VERSIONS_SUPPORTED->@*) ? $real : $_[0]; }, ); has output_format => ( is => 'ro', isa => Enum(JSON::Schema::Modern::Result->OUTPUT_FORMATS), default => 'basic', ); has short_circuit => ( is => 'ro', isa => Bool, lazy => 1, default => sub { $_[0]->output_format eq 'flag' && !$_[0]->collect_annotations }, ); has max_traversal_depth => ( is => 'ro', isa => Int, default => 50, ); has validate_formats => ( is => 'ro', isa => Bool, lazy => 1, # as specified by https://json-schema.org/draft//schema#/$vocabulary default => sub { ($_[0]->specification_version//SPECIFICATION_VERSION_DEFAULT) =~ /^draft[467]$/ ? 1 : 0 }, ); has validate_content_schemas => ( is => 'ro', isa => Bool, lazy => 1, # defaults to false in latest versions, as specified by # https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.8.2 default => sub { ($_[0]->specification_version//'') eq 'draft7' }, ); has [qw(collect_annotations scalarref_booleans stringy_numbers strict)] => ( is => 'ro', isa => Bool, ); my $core_types = Enum[qw(null object array boolean string number)]; my @core_formats = qw(date-time date time duration email idn-email hostname idn-hostname ipv4 ipv6 uri uri-reference iri iri-reference uuid uri-template json-pointer relative-json-pointer regex); # { $format_name => { type => ..., sub => ... }, ... } has _format_validations => ( is => 'bare', isa => my $format_type = HashRef[Dict[ type => $core_types|ArrayRef[$core_types], sub => CodeRef, ]], init_arg => 'format_validations', ); sub _get_format_validation ($self, $format) { ($self->{_format_validations}//{})->{$format} } sub add_format_validation ($self, $format, $definition) { $definition = { type => 'string', sub => $definition } if not is_plain_hashref($definition); $format_type->({ $format => $definition }); # all core formats are of type string (so far); changing type of custom format is permitted croak "Type for override of format $format does not match original type" if any { $format eq $_ } @core_formats and $definition->{type} ne 'string'; use autovivification 'store'; $self->{_format_validations}{$format} = $definition; } around BUILDARGS => sub ($orig, $class, @args) { my $args = $class->$orig(@args); croak 'output_format: strict_basic can only be used with specification_version: draft2019-09' if ($args->{output_format}//'') eq 'strict_basic' and ($args->{specification_version}//'') ne 'draft2019-09'; croak 'collect_annotations cannot be used with specification_version '.$args->{specification_version} if $args->{collect_annotations} and ($args->{specification_version}//'') =~ /^draft[467]$/; $args->{format_validations} = +{ map +($_->[0] => is_plain_hashref($_->[1]) ? $_->[1] : +{ type => 'string', sub => $_->[1] }), pairs $args->{format_validations}->%* } if $args->{format_validations}; return $args; }; sub add_schema { croak 'insufficient arguments' if @_ < 2; my $self = shift; # TODO: resolve $uri against $self->base_uri my $uri = !is_ref($_[0]) ? Mojo::URL->new(shift) : $_[0]->$_isa('Mojo::URL') ? shift : Mojo::URL->new; croak 'cannot add a schema with a uri with a fragment' if defined $uri->fragment; croak 'insufficient arguments' if not @_; if ($_[0]->$_isa('JSON::Schema::Modern::Document')) { Carp::carp('use of deprecated form of add_schema with document'); return $self->add_document($uri, $_[0]); } # document BUILD will trigger $self->traverse($schema) # Note we do not pass the uri to the document constructor, so resources in that document may still # be relative my $document = JSON::Schema::Modern::Document->new( schema => $_[0], evaluator => $self, # used mainly for traversal during document construction ); # try to reuse the same document, if the same schema is being added twice: # this results in _add_resource silently ignoring the duplicate add, rather than erroring. my $schema_checksum = $document->_checksum(md5($self->_json_decoder->encode($document->schema))); if (my $existing_doc = first { my $existing_checksum = $_->_checksum // $_->_checksum(md5($self->_json_decoder->encode($_->schema))); $existing_checksum eq $schema_checksum and $_->canonical_uri eq $document->canonical_uri # FIXME: must also check spec version/metaschema_uri/vocabularies } uniqint map $_->{document}, $self->_canonical_resources) { $document = $existing_doc; } $self->add_document($uri, $document); } sub add_document { croak 'insufficient arguments' if @_ < 2; my $self = shift; # TODO: resolve $uri against $self->base_uri my $base_uri = !is_ref($_[0]) ? Mojo::URL->new(shift) : $_[0]->$_isa('Mojo::URL') ? shift : Mojo::URL->new; croak 'cannot add a schema with a uri with a fragment' if defined $base_uri->fragment; my $document = shift or croak 'insufficient arguments'; croak 'wrong document type' if not $document->$_isa('JSON::Schema::Modern::Document'); die JSON::Schema::Modern::Result->new( output_format => $self->output_format, valid => 0, errors => [ $document->errors ], exception => 1, ) if $document->has_errors; my @root; # uri_string => resource hash of the resource at path '' # document resources are added after resolving each resource against our provided base uri foreach my $res_pair ($document->resource_pairs) { my ($uri_string, $doc_resource) = @$res_pair; $uri_string = Mojo::URL->new($uri_string)->to_abs($base_uri)->to_string if length $base_uri; my $new_resource = { $doc_resource->%{qw(path specification_version vocabularies configs)}, document => $document, }; $new_resource->{canonical_uri} = length $base_uri ? Mojo::URL->new($doc_resource->{canonical_uri})->to_abs($base_uri) : $doc_resource->{canonical_uri}; foreach my $anchor (keys (($doc_resource->{anchors}//{})->%*)) { use autovivification 'store'; $new_resource->{anchors}{$anchor} = { path => $doc_resource->{anchors}{$anchor}{path}, canonical_uri => length $base_uri ? Mojo::URL->new($doc_resource->{anchors}{$anchor}{canonical_uri})->to_abs($base_uri) : $doc_resource->{anchors}{$anchor}{canonical_uri}, }; } # this might croak if there are duplicates or malformed entries. $self->_add_resource($uri_string => $new_resource); @root = ( $uri_string => $new_resource ) if $new_resource->{path} eq '' and $uri_string !~ /#./; } # associate the root resource with the base uri we were provided, if it does not already exist $self->_add_resource($base_uri.'' => $root[1]) if length $base_uri and $root[0] ne $base_uri; return $document; } sub evaluate_json_string ($self, $json_data, $schema, $config_override = {}) { croak 'evaluate_json_string called in void context' if not defined wantarray; my $data; try { $data = $self->_json_decoder->decode($json_data) } catch ($e) { return JSON::Schema::Modern::Result->new( output_format => $self->output_format, valid => 0, exception => 1, errors => [ JSON::Schema::Modern::Error->new( depth => 0, mode => 'evaluate', keyword => undef, instance_location => '', keyword_location => '', error => $e, ) ], ); } return $self->evaluate($data, $schema, $config_override); } # this is called whenever we need to walk a document for something. # for now it is just called when a ::Document object is created, to verify the integrity of the # schema structure, to identify the metaschema (via the $schema keyword), and to extract all # embedded resources via $id and $anchor keywords within. # Returns the internal $state object accumulated during the traversal. sub traverse ($self, $schema_reference, $config_override = {}) { my %overrides = %$config_override; delete @overrides{qw(callbacks initial_schema_uri metaschema_uri traversed_schema_path specification_version)}; croak join(', ', sort keys %overrides), ' not supported as a config override in traverse' if keys %overrides; # Note: the starting position is not guaranteed to be at the root of the $document, # nor is the fragment portion of this uri necessarily empty my $initial_uri = Mojo::URL->new($config_override->{initial_schema_uri} // ''); my $initial_path = $config_override->{traversed_schema_path} // ''; my $spec_version = $config_override->{specification_version} // $self->specification_version // SPECIFICATION_VERSION_DEFAULT; croak 'traversed_schema_path must be a json pointer' if $initial_path !~ m{^(?:/|$)}; if (length(my $uri_path = $initial_uri->fragment)) { croak 'initial_schema_uri fragment must be a json pointer' if $uri_path !~ m{^/}; croak 'traversed_schema_path does not match initial_schema_uri path fragment' if substr($initial_path, -length($uri_path)) ne $uri_path; } my $state = { depth => 0, data_path => '', # this never changes since we don't have an instance yet initial_schema_uri => $initial_uri, # the canonical URI as of the start of this method or last $id traversed_schema_path => $initial_path, # the accumulated traversal path as of the start or last $id schema_path => '', # the rest of the path, since the start of this method or last $id errors => [], identifiers => {}, subschemas => [], configs => {}, callbacks => $config_override->{callbacks} // {}, evaluator => $self, traverse => 1, }; try { my $for_canonical_uri = Mojo::URL->new( (is_plain_hashref($schema_reference) && exists $schema_reference->{'$id'} ? Mojo::URL->new($schema_reference->{'$id'}) : undef) // $state->{initial_schema_uri}); $for_canonical_uri->fragment(undef) if not length $for_canonical_uri->fragment; # a subsequent "$schema" keyword can still change these values $state->@{qw(spec_version vocabularies)} = $self->_get_metaschema_info( $config_override->{metaschema_uri} // $self->METASCHEMA_URIS->{$spec_version}, $for_canonical_uri, ); my $valid = $self->_traverse_subschema($schema_reference, $state); die 'result is false but there are no errors' if not $valid and not $state->{errors}->@*; die 'result is true but there are errors' if $valid and $state->{errors}->@*; } catch ($e) { if ($e->$_isa('JSON::Schema::Modern::Result')) { push $state->{errors}->@*, $e->errors; } elsif ($e->$_isa('JSON::Schema::Modern::Error')) { # note: we should never be here, since traversal subs are no longer fatal push $state->{errors}->@*, $e; } else { E({ %$state, exception => 1 }, 'EXCEPTION: '.$e); } } delete $state->{traverse}; return $state; } # the actual runtime evaluation of the schema against input data. sub evaluate ($self, $data, $schema_reference, $config_override = {}) { croak 'evaluate called in void context' if not defined wantarray; my %overrides = %$config_override; delete @overrides{qw(validate_formats validate_content_schemas short_circuit collect_annotations scalarref_booleans stringy_numbers strict callbacks effective_base_uri data_path traversed_schema_path _strict_schema_data)}; croak join(', ', sort keys %overrides), ' not supported as a config override in evaluate' if keys %overrides; my $state = { data_path => $config_override->{data_path} // '', traversed_schema_path => $config_override->{traversed_schema_path} // '', # the accumulated path as of the start of evaluation or last $id or $ref initial_schema_uri => Mojo::URL->new, # the canonical URI as of the start of evaluation or last $id or $ref schema_path => '', # the rest of the path, since the start of evaluation or last $id or $ref errors => [], depth => 0, configs => {}, }; # resolve locations against this for errors and annotations, if locations are not already absolute if (length $config_override->{effective_base_uri}) { $state->{effective_base_uri} = Mojo::URL->new($config_override->{effective_base_uri}); croak 'it is meaningless for effective_base_uri to have a fragment' if defined $state->{effective_base_uri}->fragment; } my $valid; try { my $schema_info; if (not is_ref($schema_reference) or $schema_reference->$_isa('Mojo::URL')) { $schema_info = $self->_fetch_from_uri($schema_reference); } else { # traverse is called via add_schema -> ::Document->new -> ::Document->BUILD my $document = $schema_reference->$_isa('JSON::Schema::Modern::Document') ? $self->add_document($schema_reference) : $self->add_schema($schema_reference); my $base_resource = $document->_get_resource($document->canonical_uri) || croak "couldn't get resource: document parse error"; $schema_info = { schema => $document->schema, document => $document, document_path => '', $base_resource->%{qw(canonical_uri specification_version vocabularies configs)}, }; } abort($state, 'EXCEPTION: collect_annotations cannot be used with specification_version '.$schema_info->{specification_version}) if $config_override->{collect_annotations} and $schema_info->{specification_version} =~ /^draft[467]$/; abort($state, 'EXCEPTION: unable to find resource "%s"', $schema_reference) if not $schema_info; abort($state, 'EXCEPTION: "%s" is not a schema', $schema_reference) if not $schema_info->{document}->get_entity_at_location($schema_info->{document_path}); $state = +{ %$state, initial_schema_uri => $schema_info->{canonical_uri}, # the canonical URI as of the start of evaluation, or last $id or $ref document => $schema_info->{document}, # the ::Document object containing this schema document_path => $schema_info->{document_path}, # the path within the document of this schema, as of the start of evaluation, or last $id or $ref dynamic_scope => [ $schema_info->{canonical_uri} ], annotations => [], seen => {}, spec_version => $schema_info->{specification_version}, vocabularies => $schema_info->{vocabularies}, callbacks => $config_override->{callbacks} // {}, evaluator => $self, $schema_info->{configs}->%*, (map { my $val = $config_override->{$_} // $self->$_; defined $val ? ( $_ => $val ) : () # note: this is a subset of the allowed overrides defined above } qw(validate_formats validate_content_schemas short_circuit collect_annotations scalarref_booleans stringy_numbers strict)), }; # this hash will be added to at each level of schema evaluation $state->{seen_data_properties} = {} if $config_override->{_strict_schema_data}; # we're going to set collect_annotations during evaluation when we see an unevaluated* keyword # (or for object data when the _strict_schema_data configuration is set), # but after we pass to a new data scope we'll clear it again.. unless we've got the config set # globally for the entire evaluation, so we store that value in a high bit. $state->{collect_annotations} = ($state->{collect_annotations}//0) << 8; $valid = $self->_eval_subschema($data, $schema_info->{schema}, $state); warn 'result is false but there are no errors' if not $valid and not $state->{errors}->@*; warn 'result is true but there are errors' if $valid and $state->{errors}->@*; } catch ($e) { if ($e->$_isa('JSON::Schema::Modern::Result')) { return $e; } elsif ($e->$_isa('JSON::Schema::Modern::Error')) { push $state->{errors}->@*, $e; } else { $valid = E({ %$state, exception => 1 }, 'EXCEPTION: '.$e); } } if ($state->{seen_data_properties}) { my @unevaluated_properties = grep !$state->{seen_data_properties}{$_}, keys $state->{seen_data_properties}->%*; foreach my $property (sort @unevaluated_properties) { $valid = E({ %$state, data_path => $property }, 'unknown keyword found in schema: %s', $property =~ m{/([^/]+)$}); } } die 'evaluate validity inconsistent with error count' if $valid xor !$state->{errors}->@*; return JSON::Schema::Modern::Result->new( output_format => $self->output_format, valid => $valid, $valid # strip annotations from result if user didn't explicitly ask for them ? ($config_override->{collect_annotations} // $self->collect_annotations ? (annotations => $state->{annotations}) : ()) : (errors => $state->{errors}), ); } sub validate_schema ($self, $schema, $config_override = {}) { croak 'validate_schema called in void context' if not defined wantarray; my $metaschema_uri = is_plain_hashref($schema) && $schema->{'$schema'} ? $schema->{'$schema'} : $self->METASCHEMA_URIS->{$self->specification_version // $self->SPECIFICATION_VERSION_DEFAULT}; my $result = $self->evaluate($schema, $metaschema_uri, { %$config_override, $self->strict || $config_override->{strict} ? (_strict_schema_data => 1) : () }); return $result if not $result->valid; my $state = $self->traverse($schema, $config_override); return JSON::Schema::Modern::Result->new( output_format => $self->output_format, valid => 0, errors => $state->{errors}, ) if $state->{errors}->@*; return $result; # valid: true } sub get ($self, $uri_reference) { if (wantarray) { my $schema_info = $self->_fetch_from_uri($uri_reference); return if not $schema_info; my $subschema = is_ref($schema_info->{schema}) ? dclone($schema_info->{schema}) : $schema_info->{schema}; return ($subschema, $schema_info->{canonical_uri}); } else { # abridged version of _fetch_from_uri $uri_reference = Mojo::URL->new($uri_reference) if not is_ref($uri_reference); my $fragment = $uri_reference->fragment; my $resource = $self->_get_or_load_resource($uri_reference->clone->fragment(undef)); return if not $resource; my $schema; if (not length($fragment) or $fragment =~ m{^/}) { $schema = $resource->{document}->get($resource->{path}.($fragment//'')); } else { # we are following a URI with a plain-name fragment return if not my $subresource = ($resource->{anchors}//{})->{$fragment}; $schema = $resource->{document}->get($subresource->{path}); } return is_ref($schema) ? dclone($schema) : $schema; } } sub get_document ($self, $uri_reference) { my $schema_info = $self->_fetch_from_uri($uri_reference); return if not $schema_info; return $schema_info->{document}; } # defined lower down: # sub add_media_type ($self, $media_type, $sub) { ... } # sub get_media_type ($self, $media_type) { ... } # sub add_encoding ($self, $encoding, $sub) { ... } # sub get_encoding ($self, $encoding) { ... } # sub add_vocabulary ($self, $classname) { ... } ######## NO PUBLIC INTERFACES FOLLOW THIS POINT ######## # current spec version => { keyword => undef, or arrayref of alternatives } my %removed_keywords = ( 'draft4' => { }, 'draft6' => { id => [ '$id' ], }, 'draft7' => { id => [ '$id' ], }, 'draft2019-09' => { id => [ '$id' ], definitions => [ '$defs' ], dependencies => [ qw(dependentSchemas dependentRequired) ], }, 'draft2020-12' => { id => [ '$id' ], definitions => [ '$defs' ], dependencies => [ qw(dependentSchemas dependentRequired) ], '$recursiveAnchor' => [ '$dynamicAnchor' ], '$recursiveRef' => [ '$dynamicRef' ], additionalItems => [ 'items' ], }, ); # { # $spec_version => { # $vocabulary_class => { # traverse => [ [ $keyword => $subref ], [ ... ] ], # evaluate => [ [ $keyword => $subref ], [ ... ] ], # } # } # } # If we could serialize coderefs, this could be an object attribute; # otherwise, we might as well persist this for the lifetime of the process. our $vocabulary_cache = {}; sub _traverse_subschema ($self, $schema, $state) { delete $state->@{'keyword', grep /^_/, keys %$state}; return E($state, 'EXCEPTION: maximum traversal depth (%d) exceeded', $self->max_traversal_depth) if $state->{depth}++ > $self->max_traversal_depth; push $state->{subschemas}->@*, $state->{traversed_schema_path}.$state->{schema_path}; my $schema_type = get_type($schema); return 1 if $schema_type eq 'boolean'; return E($state, 'invalid schema type: %s', $schema_type) if $schema_type ne 'object'; return 1 if not keys %$schema; my $valid = 1; my %unknown_keywords = map +($_ => undef), keys %$schema; # we use an index rather than iterating through the lists directly because the lists of # vocabularies and keywords can change after we have started. However, only the Core vocabulary # and $schema keyword can make this change, and they both come first, therefore a simple index # into the list is sufficient. ALL_KEYWORDS: for (my $vocab_index = 0; $vocab_index < $state->{vocabularies}->@*; $vocab_index++) { my $vocabulary = $state->{vocabularies}[$vocab_index]; my $keyword_list; for (my $keyword_index = 0; $keyword_index < ($keyword_list //= do { use autovivification qw(fetch store); $vocabulary_cache->{$state->{spec_version}}{$vocabulary}{traverse} //= [ map [ $_ => $vocabulary->can('_traverse_keyword_'.($_ =~ s/^\$//r)) ], $vocabulary->keywords($state->{spec_version}) ]; })->@*; $keyword_index++) { my ($keyword, $sub) = $keyword_list->[$keyword_index]->@*; next if not exists $schema->{$keyword}; # keywords adjacent to $ref are not evaluated before draft2019-09 next if $keyword ne '$ref' and exists $schema->{'$ref'} and $state->{spec_version} =~ /^draft[467]$/; delete $unknown_keywords{$keyword}; $state->{keyword} = $keyword; my $old_spec_version = $state->{spec_version}; my $error_count = $state->{errors}->@*; if (not $sub->($vocabulary, $schema, $state)) { die 'traverse result is false but there are no errors (keyword: '.$keyword.')' if $error_count == $state->{errors}->@*; $valid = 0; next; } warn 'traverse result is true but there are errors (keyword: '.$keyword.')' if $error_count != $state->{errors}->@*; # a keyword changed the keyword list for this vocabulary; re-fetch the list before continuing undef $keyword_list if $state->{spec_version} ne $old_spec_version; if (my $callback = $state->{callbacks}{$keyword}) { $error_count = $state->{errors}->@*; if (not $callback->($schema, $state)) { die 'callback result is false but there are no errors (keyword: '.$keyword.')' if $error_count == $state->{errors}->@*; $valid = 0; next; } die 'callback result is true but there are errors (keyword: '.$keyword.')' if $error_count != $state->{errors}->@*; } } } delete $state->{keyword}; if ($self->strict and keys %unknown_keywords) { $valid = E($state, 'unknown keyword%s found: %s', keys %unknown_keywords > 1 ? 's' : '', join(', ', sort keys %unknown_keywords)); } # check for previously-supported but now removed keywords foreach my $keyword (sort keys $removed_keywords{$state->{spec_version}}->%*) { next if not exists $schema->{$keyword}; my $message ='no-longer-supported "'.$keyword.'" keyword present (at location "' .canonical_uri($state).'")'; if (my $alternates = $removed_keywords{$state->{spec_version}}->{$keyword}) { my @list = map '"'.$_.'"', @$alternates; @list = ((map $_.',', @list[0..$#list-1]), $list[-1]) if @list > 2; splice(@list, -1, 0, 'or') if @list > 1; $message .= ': this should be rewritten as '.join(' ', @list); } carp $message; } return $valid; } sub _eval_subschema ($self, $data, $schema, $state) { croak '_eval_subschema called in void context' if not defined wantarray; # callers created a new $state for us, so we do not propagate upwards changes to depth, traversed # paths; but annotations, errors are arrayrefs so their contents will be shared $state->{dynamic_scope} = [ ($state->{dynamic_scope}//[])->@* ]; delete $state->@{'keyword', grep /^_/, keys %$state}; abort($state, 'EXCEPTION: maximum evaluation depth (%d) exceeded', $self->max_traversal_depth) if $state->{depth}++ > $self->max_traversal_depth; my $schema_type = get_type($schema); return $schema || E($state, 'subschema is false') if $schema_type eq 'boolean'; # this should never happen, due to checks in traverse abort($state, 'invalid schema type: %s', $schema_type) if $schema_type ne 'object'; return 1 if not keys %$schema; # find all schema locations in effect at this data path + uri combination # if any of them are absolute prefix of this schema location, we are in a loop. my $canonical_uri = canonical_uri($state); my $schema_location = $state->{traversed_schema_path}.$state->{schema_path}; { use autovivification qw(fetch store); abort($state, 'EXCEPTION: infinite loop detected (same location evaluated twice)') if grep substr($schema_location, 0, length) eq $_, keys $state->{seen}{$state->{data_path}}{$canonical_uri}->%*; $state->{seen}{$state->{data_path}}{$canonical_uri}{$schema_location}++; } my $valid = 1; my %unknown_keywords = map +($_ => undef), keys %$schema; # set aside annotations collected so far; they are not used in the current scope's evaluation my $parent_annotations = $state->{annotations}; $state->{annotations} = []; # in order to collect annotations from applicator keywords only when needed, we twiddle the low # bit if we see a local unevaluated* keyword, and clear it again as we move on to a new data path. # We also set it when _strict_schema_data is set, but only for object data instances. $state->{collect_annotations} |= 0+(exists $schema->{unevaluatedItems} || exists $schema->{unevaluatedProperties} || !!$state->{seen_data_properties} && (my $is_object_data = is_plain_hashref($data))); # in order to collect annotations for unevaluated* keywords, we sometimes need to ignore the # suggestion to short_circuit evaluation at this scope (but lower scopes are still fine) $state->{short_circuit} = ($state->{short_circuit} || delete($state->{short_circuit_suggested})) && !exists($schema->{unevaluatedItems}) && !exists($schema->{unevaluatedProperties}); # we use an index rather than iterating through the lists directly because the lists of # vocabularies and keywords can change after we have started. However, only the Core vocabulary # and $schema keyword can make this change, and they both come first, therefore a simple index # into the list is sufficient. ALL_KEYWORDS: for (my $vocab_index = 0; $vocab_index < $state->{vocabularies}->@*; $vocab_index++) { my $vocabulary = $state->{vocabularies}[$vocab_index]; my $keyword_list; for (my $keyword_index = 0; $keyword_index < ($keyword_list //= do { use autovivification qw(fetch store); $vocabulary_cache->{$state->{spec_version}}{$vocabulary}{evaluate} //= [ map [ $_ => $vocabulary->can('_eval_keyword_'.($_ =~ s/^\$//r)) ], $vocabulary->keywords($state->{spec_version}) ]; })->@*; $keyword_index++) { my ($keyword, $sub) = $keyword_list->[$keyword_index]->@*; next if not exists $schema->{$keyword}; # keywords adjacent to $ref are not evaluated before draft2019-09 next if $keyword ne '$ref' and exists $schema->{'$ref'} and $state->{spec_version} =~ /^draft[467]$/; delete $unknown_keywords{$keyword}; $state->{keyword} = $keyword; if ($sub) { my $old_spec_version = $state->{spec_version}; my $error_count = $state->{errors}->@*; try { if (not $sub->($vocabulary, $data, $schema, $state)) { warn 'evaluation result is false but there are no errors (keyword: '.$keyword.')' if $error_count == $state->{errors}->@*; $valid = 0; last ALL_KEYWORDS if $state->{short_circuit}; next; } warn 'evaluation result is true but there are errors (keyword: '.$keyword.')' if $error_count != $state->{errors}->@*; } catch ($e) { die $e if $e->$_isa('JSON::Schema::Modern::Error'); abort($state, 'EXCEPTION: '.$e); } # a keyword changed the keyword list for this vocabulary; re-fetch the list before continuing undef $keyword_list if $state->{spec_version} ne $old_spec_version; } if (my $callback = ($state->{callbacks}//{})->{$keyword}) { my $error_count = $state->{errors}->@*; if (not $callback->($data, $schema, $state)) { warn 'callback result is false but there are no errors (keyword: '.$keyword.')' if $error_count == $state->{errors}->@*; $valid = 0; last ALL_KEYWORDS if $state->{short_circuit}; next; } warn 'callback result is true but there are errors (keyword: '.$keyword.')' if $error_count != $state->{errors}->@*; } } } delete $state->{keyword}; if ($state->{strict} and keys %unknown_keywords) { abort($state, 'unknown keyword%s found: %s', keys %unknown_keywords > 1 ? 's' : '', join(', ', sort keys %unknown_keywords)); } # Note: we can remove all of this entirely and just rely on strict mode when we (eventually!) remove # the traverse phase and replace with evaluate-against-metaschema. if ($state->{seen_data_properties} and $is_object_data) { # record the locations of all local properties $state->{seen_data_properties}{jsonp($state->{data_path}, $_)} |= 0 foreach keys %$data; my @evaluated_properties = map { my $keyword = $_->{keyword}; (grep $keyword eq $_, qw(properties additionalProperties patternProperties unevaluatedProperties)) ? $_->{annotation}->@* : (); } local_annotations($state); # tick off properties that were recognized by this subschema $state->{seen_data_properties}{jsonp($state->{data_path}, $_)} |= 1 foreach @evaluated_properties; } if ($valid and $state->{collect_annotations} and $state->{spec_version} !~ /^draft(?:7|2019-09)$/) { annotate_self(+{ %$state, keyword => $_, _unknown => 1 }, $schema) foreach sort keys %unknown_keywords; } # only keep new annotations if schema is valid push $parent_annotations->@*, $state->{annotations}->@* if $valid; return $valid; } my $path_type = Str->where('m{^(?:/|$)}'); # JSON pointer relative to the document root has _resource_index => ( is => 'bare', isa => Map[my $resource_key_type = Str->where('!/#/'), my $resource_type = Dict[ # may not be stringwise-equal to the top level key canonical_uri => (InstanceOf['Mojo::URL'])->where(q{not defined $_->fragment}), path => $path_type, specification_version => my $spec_version_type = Enum(SPECIFICATION_VERSIONS_SUPPORTED), document => InstanceOf['JSON::Schema::Modern::Document'], # the vocabularies used when evaluating instance data against schema vocabularies => ArrayRef[my $vocabulary_class_type = ClassName->where(q{$_->DOES('JSON::Schema::Modern::Vocabulary')})], anchors => Optional[HashRef[Dict[ canonical_uri => (InstanceOf['Mojo::URL'])->where(q{not defined $_->fragment or substr($_->fragment, 0, 1) eq '/'}), path => $path_type, ]]], configs => HashRef, Slurpy[HashRef[Undef]], # no other fields allowed ]], ); sub _get_resource { ($_[0]->{_resource_index}//{})->{$_[1]} } # does not check for duplicate entries, or for malformed uris sub _add_resources_unsafe { use autovivification 'store'; $_[0]->{_resource_index}{$resource_key_type->($_->[0])} = $resource_type->($_->[1]) foreach pairs @_[1..$#_]; } sub _resource_index { ($_[0]->{_resource_index}//{})->%* } sub _canonical_resources { values(($_[0]->{_resource_index}//{})->%*) } sub _resource_pairs { pairs(($_[0]->{_resource_index}//{})->%*) } sub _add_resource ($self, @kvs) { foreach my $pair (sort { $a->[0] cmp $b->[0] } pairs @kvs) { my ($key, $value) = @$pair; if (my $existing = $self->_get_resource($key)) { # we allow overwriting canonical_uri = '' to allow for ad hoc evaluation of schemas that # lack all identifiers altogether, but preserve other resources from the original document if ($key ne '') { next if $existing->{path} eq $value->{path} and $existing->{canonical_uri} eq $value->{canonical_uri} and $existing->{specification_version} eq $value->{specification_version} and refaddr($existing->{document}) == refaddr($value->{document}); croak 'uri "'.$key.'" conflicts with an existing schema resource'; } } elsif ($self->CACHED_METASCHEMAS->{$key}) { croak 'uri "'.$key.'" conflicts with an existing meta-schema resource'; } use autovivification 'store'; $self->{_resource_index}{$resource_key_type->($key)} = $resource_type->($value); } } # $vocabulary uri (not its $id!) => [ spec_version, class ] has _vocabulary_classes => ( is => 'bare', isa => HashRef[ my $vocabulary_type = Tuple[ $spec_version_type, $vocabulary_class_type, ] ], reader => '__vocabulary_classes', lazy => 1, default => sub { +{ map { my $class = $_; pairmap { $a => [ $b, $class ] } $class->vocabulary } map load_module('JSON::Schema::Modern::Vocabulary::'.$_), qw(Core Applicator Validation FormatAssertion FormatAnnotation Content MetaData Unevaluated) } }, ); sub _get_vocabulary_class { $_[0]->__vocabulary_classes->{$_[1]} } sub add_vocabulary ($self, $classname) { return if grep $_->[1] eq $classname, values $self->__vocabulary_classes->%*; $vocabulary_class_type->(load_module($classname)); # uri => version, uri => version foreach my $pair (pairs $classname->vocabulary) { my ($uri_string, $spec_version) = @$pair; Str->where(q{my $uri = Mojo::URL->new($_); $uri->is_abs && !defined $uri->fragment})->($uri_string); $spec_version_type->($spec_version); $self->{_vocabulary_classes}{$uri_string} = $vocabulary_type->([ $spec_version, $classname ]); } } # $schema uri => [ spec_version, [ vocab classes, in evaluation order ] ]. has _metaschema_vocabulary_classes => ( is => 'bare', isa => HashRef[ my $mvc_type = Tuple[ $spec_version_type, ArrayRef[$vocabulary_class_type], ] ], reader => '__metaschema_vocabulary_classes', lazy => 1, default => sub { my @modules = map load_module('JSON::Schema::Modern::Vocabulary::'.$_), qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated); +{ 'https://json-schema.org/draft/2020-12/schema' => [ 'draft2020-12', [ @modules ] ], do { pop @modules; () }, # remove Unevaluated 'https://json-schema.org/draft/2019-09/schema' => [ 'draft2019-09', [ @modules ] ], 'http://json-schema.org/draft-07/schema' => [ 'draft7', [ @modules ] ], do { splice @modules, 4, 1; () }, # remove Content 'http://json-schema.org/draft-06/schema' => [ 'draft6', \@modules ], 'http://json-schema.org/draft-04/schema' => [ 'draft4', \@modules ], }, }, ); sub _get_metaschema_vocabulary_classes { $_[0]->__metaschema_vocabulary_classes->{$_[1] =~ s/#$//r} } sub _set_metaschema_vocabulary_classes { $_[0]->__metaschema_vocabulary_classes->{$_[1] =~ s/#$//r} = $mvc_type->($_[2]) } sub __all_metaschema_vocabulary_classes { values $_[0]->__metaschema_vocabulary_classes->%* } # retrieves metaschema info either from cache or by parsing the schema for vocabularies # throws a JSON::Schema::Modern::Result on error sub _get_metaschema_info ($self, $metaschema_uri, $for_canonical_uri) { # check the cache. specification metaschemas are already populated. my $metaschema_info = $self->_get_metaschema_vocabulary_classes($metaschema_uri); return @$metaschema_info if $metaschema_info; # otherwise, fetch the metaschema and parse its $vocabulary keyword. # we do this by traversing a baby schema with just the $schema keyword. my $state = $self->traverse({ '$schema' => $metaschema_uri.'' }); die JSON::Schema::Modern::Result->new( output_format => $self->output_format, valid => JSON::PP::false, errors => [ map { my $e = $_; # absolute location is undef iff the location = '/$schema' my $absolute_location = $e->absolute_keyword_location // $for_canonical_uri; JSON::Schema::Modern::Error->new( depth => $e->depth, mode => 'traverse', keyword => $e->keyword eq '$schema' ? '' : $e->keyword, instance_location => $e->instance_location, keyword_location => ($for_canonical_uri->fragment//'').($e->keyword_location =~ s{^/\$schema\b}{}r), length $absolute_location ? ( absolute_keyword_location => $absolute_location ) : (), error => $e->error, ) } $state->{errors}->@* ], exception => 1, ) if $state->{errors}->@*; return ($state->{spec_version}, $state->{vocabularies}); } # translate vocabulary URIs into classes, caching the results (if any) sub _fetch_vocabulary_data ($self, $state, $schema_info) { if (not exists $schema_info->{schema}{'$vocabulary'}) { # "If "$vocabulary" is absent, an implementation MAY determine behavior based on the meta-schema # if it is recognized from the URI value of the referring schema's "$schema" keyword." my $metaschema_uri = $self->METASCHEMA_URIS->{$schema_info->{specification_version}}; return $self->_get_metaschema_vocabulary_classes($metaschema_uri)->@*; } my $valid = 1; $valid = E($state, '$vocabulary can only appear at the document root') if length $schema_info->{document_path}; $valid = E($state, 'metaschemas must have an $id') if not exists $schema_info->{schema}{'$id'}; return (undef, []) if not $valid; my @vocabulary_classes; foreach my $uri (sort keys $schema_info->{schema}{'$vocabulary'}->%*) { my $class_info = $self->_get_vocabulary_class($uri); $valid = E({ %$state, _schema_path_suffix => $uri }, '"%s" is not a known vocabulary', $uri), next if $schema_info->{schema}{'$vocabulary'}{$uri} and not $class_info; next if not $class_info; # vocabulary is not known, but marked as false in the metaschema my ($spec_version, $class) = @$class_info; $valid = E({ %$state, _schema_path_suffix => $uri }, '"%s" uses %s, but the metaschema itself uses %s', $uri, $spec_version, $schema_info->{specification_version}), next if $spec_version ne $schema_info->{specification_version}; push @vocabulary_classes, $class; } @vocabulary_classes = sort { $a->evaluation_order <=> $b->evaluation_order || ($a->evaluation_order == 999 ? 0 : ($valid = E($state, '%s and %s have a conflicting evaluation_order', sort $a, $b))) } @vocabulary_classes; $valid = E($state, 'the first vocabulary (by evaluation_order) must be Core') if ($vocabulary_classes[0]//'') ne 'JSON::Schema::Modern::Vocabulary::Core'; return ($schema_info->{specification_version}, $valid ? \@vocabulary_classes : []); } # used for determining a default '$schema' keyword where there is none use constant METASCHEMA_URIS => { 'draft2020-12' => 'https://json-schema.org/draft/2020-12/schema', 'draft2019-09' => 'https://json-schema.org/draft/2019-09/schema', 'draft7' => 'http://json-schema.org/draft-07/schema#', 'draft6' => 'http://json-schema.org/draft-06/schema#', 'draft4' => 'http://json-schema.org/draft-04/schema#', }; use constant CACHED_METASCHEMAS => { 'https://json-schema.org/draft/2020-12/meta/applicator' => 'draft2020-12/meta/applicator.json', 'https://json-schema.org/draft/2020-12/meta/content' => 'draft2020-12/meta/content.json', 'https://json-schema.org/draft/2020-12/meta/core' => 'draft2020-12/meta/core.json', 'https://json-schema.org/draft/2020-12/meta/format-annotation' => 'draft2020-12/meta/format-annotation.json', 'https://json-schema.org/draft/2020-12/meta/format-assertion' => 'draft2020-12/meta/format-assertion.json', 'https://json-schema.org/draft/2020-12/meta/meta-data' => 'draft2020-12/meta/meta-data.json', 'https://json-schema.org/draft/2020-12/meta/unevaluated' => 'draft2020-12/meta/unevaluated.json', 'https://json-schema.org/draft/2020-12/meta/validation' => 'draft2020-12/meta/validation.json', 'https://json-schema.org/draft/2020-12/output/schema' => 'draft2020-12/output/schema.json', 'https://json-schema.org/draft/2020-12/schema' => 'draft2020-12/schema.json', 'https://json-schema.org/draft/2019-09/meta/applicator' => 'draft2019-09/meta/applicator.json', 'https://json-schema.org/draft/2019-09/meta/content' => 'draft2019-09/meta/content.json', 'https://json-schema.org/draft/2019-09/meta/core' => 'draft2019-09/meta/core.json', 'https://json-schema.org/draft/2019-09/meta/format' => 'draft2019-09/meta/format.json', 'https://json-schema.org/draft/2019-09/meta/meta-data' => 'draft2019-09/meta/meta-data.json', 'https://json-schema.org/draft/2019-09/meta/validation' => 'draft2019-09/meta/validation.json', 'https://json-schema.org/draft/2019-09/output/schema' => 'draft2019-09/output/schema.json', 'https://json-schema.org/draft/2019-09/schema' => 'draft2019-09/schema.json', # trailing # is omitted because we always cache documents by its canonical (fragmentless) URI 'http://json-schema.org/draft-07/schema' => 'draft7/schema.json', 'http://json-schema.org/draft-06/schema' => 'draft6/schema.json', 'http://json-schema.org/draft-04/schema' => 'draft4/schema.json', }; # returns the same as _get_resource sub _get_or_load_resource ($self, $uri) { my $resource = $self->_get_resource($uri); return $resource if $resource; if (my $local_filename = $self->CACHED_METASCHEMAS->{$uri}) { my $file = path(dist_dir('JSON-Schema-Modern'), $local_filename); my $schema = $self->_json_decoder->decode($file->slurp_raw); my $document = JSON::Schema::Modern::Document->new(schema => $schema, evaluator => $self); # this should be caught by the try/catch in evaluate() die JSON::Schema::Modern::Result->new( output_format => $self->output_format, valid => 0, errors => [ $document->errors ], exception => 1, ) if $document->has_errors; # we have already performed the appropriate collision checks, so we bypass them here $self->_add_resources_unsafe( map +($_->[0] => +{ $_->[1]->%*, document => $document }), $document->resource_pairs ); return $self->_get_resource($uri); } # TODO: # - load from network or disk return; }; # returns information necessary to use a schema found at a particular URI or uri-reference: # - schema: a schema (which may not be at a document root) # - canonical_uri: the canonical uri for that schema, # - document: the JSON::Schema::Modern::Document object that holds that schema # - document_path: the path relative to the document root for this schema # - specification_version: the specification version that applies to this schema # - vocabularies: the vocabularies to use when considering schema keywords # - configs: the config overrides to set when considering schema keywords # creates a Document and adds it to the resource index, if not already present. sub _fetch_from_uri ($self, $uri_reference) { $uri_reference = Mojo::URL->new($uri_reference) if not is_ref($uri_reference); my $fragment = $uri_reference->fragment; my $resource = $self->_get_or_load_resource($uri_reference->clone->fragment(undef)); return if not $resource; if (not length($fragment) or $fragment =~ m{^/}) { my $subschema = $resource->{document}->get(my $document_path = $resource->{path}.($fragment//'')); return if not defined $subschema; my $doc_addr = refaddr($resource->{document}); my $closest_resource = first { !length($_->[1]{path}) # document root || length($document_path) && $document_path =~ m{^\Q$_->[1]{path}\E(?:/|\z)} } # path is above present location sort { length($b->[1]{path}) <=> length($a->[1]{path}) } # sort by length, descending grep { refaddr($_->[1]{document}) == $doc_addr } # in same document $self->_resource_pairs; my $canonical_uri = $closest_resource->[1]{canonical_uri}->clone ->fragment(substr($document_path, length($closest_resource->[1]{path}))); $canonical_uri->fragment(undef) if not length($canonical_uri->fragment); return { schema => $subschema, canonical_uri => $canonical_uri, document_path => $document_path, $resource->%{qw(document specification_version vocabularies configs)}, # reference, not copy }; } else { # we are following a URI with a plain-name fragment return if not my $subresource = ($resource->{anchors}//{})->{$fragment}; return { schema => $resource->{document}->get($subresource->{path}), canonical_uri => $subresource->{canonical_uri}->clone, # this is *not* the anchor-containing URI document_path => $subresource->{path}, $resource->%{qw(document specification_version vocabularies configs)}, # reference, not copy }; } } # Mojo::JSON::JSON_XS is false when the environment variable $MOJO_NO_JSON_XS is set # and also checks if Cpanel::JSON::XS is installed. # Mojo::JSON falls back to its own pure-perl encoder/decoder but does not support all the options # that we require here. use constant _JSON_BACKEND => Mojo::JSON::JSON_XS ? 'Cpanel::JSON::XS' : 'JSON::PP'; # used for internal encoding as well (when caching serialized schemas) has _json_decoder => ( is => 'ro', isa => HasMethods[qw(encode decode)], lazy => 1, default => sub { _JSON_BACKEND->new->allow_nonref(1)->canonical(1)->utf8(1)->allow_bignum(1)->convert_blessed(1) }, ); # since media types are case-insensitive, all type names must be casefolded on insertion. has _media_type => ( is => 'bare', isa => my $media_type_type = Map[Str->where(q{$_ eq CORE::fc($_)}), CodeRef], reader => '__media_type', lazy => 1, default => sub ($self) { my $_json_media_type = sub ($content_ref) { # utf-8 decoding is always done, as per the JSON spec. # other charsets are not supported: see RFC8259 §11 \ _JSON_BACKEND->new->allow_nonref(1)->utf8(1)->decode($content_ref->$*); }; +{ (map +($_ => $_json_media_type), qw(application/json application/schema+json application/schema-instance+json)), (map +($_ => sub ($content_ref) { $content_ref }), qw(text/* application/octet-stream)), 'application/x-www-form-urlencoded' => sub ($content_ref) { \ Mojo::Parameters->new->charset('UTF-8')->parse($content_ref->$*)->to_hash; }, 'application/x-ndjson' => sub ($content_ref) { my $decoder = _JSON_BACKEND->new->allow_nonref(1)->utf8(1); my $line = 0; # line numbers start at 1 \[ map { do { try { ++$line; $decoder->decode($_) } catch ($e) { die 'parse error at line '.$line.': '.$e } } } split(/\r?\n/, $content_ref->$*) ]; }, }; }, ); sub add_media_type { $media_type_type->({ @_[1..2] }); $_[0]->__media_type->{$_[1]} = $_[2]; } # get_media_type('TExT/bloop') will fall through to matching an entry for 'text/*' or '*/*' sub get_media_type ($self, $type) { my $types = $self->__media_type; my $mt = $types->{fc $type}; return $mt if $mt; return $types->{(first { m{([^/]+)/\*$} && fc($type) =~ m{^\Q$1\E/[^/]+$} } keys %$types) // '*/*'}; }; has _encoding => ( is => 'bare', isa => HashRef[CodeRef], reader => '__encoding', lazy => 1, default => sub ($self) { +{ identity => sub ($content_ref) { $content_ref }, base64 => sub ($content_ref) { die "invalid characters\n" if $content_ref->$* =~ m{[^A-Za-z0-9+/=]} or $content_ref->$* =~ m{=(?=[^=])}; require MIME::Base64; \ MIME::Base64::decode_base64($content_ref->$*); }, base64url => sub ($content_ref) { die "invalid characters\n" if $content_ref->$* =~ m{[^A-Za-z0-9=_-]} or $content_ref->$* =~ m{=(?=[^=])}; require MIME::Base64; \ MIME::Base64::decode_base64url($content_ref->$*); }, }; }, ); sub get_encoding { $_[0]->__encoding->{$_[1]} } sub add_encoding { $_[0]->__encoding->{$_[1]} = CodeRef->($_[2]) } # callback hook for Sereal::Encoder sub FREEZE ($self, $serializer) { my $data = +{ %$self }; # Cpanel::JSON::XS doesn't serialize: https://github.com/Sereal/Sereal/issues/266 # coderefs can't serialize cleanly and must be re-added by the user. delete $data->@{qw(_json_decoder _format_validations _media_type _encoding)}; return $data; } # callback hook for Sereal::Decoder sub THAW ($class, $serializer, $data) { my $self = bless($data, $class); # load all vocabulary classes, both those used by loaded schemas, as well as all the core modules load_module($_) foreach uniq( (map $_->{vocabularies}->@*, $self->_canonical_resources), (map $_->[1], values $self->__vocabulary_classes->%*)); return $self; } 1; __END__ =pod =encoding UTF-8 =for stopwords schema subschema metaschema validator evaluator listref =head1 NAME JSON::Schema::Modern - Validate data against a schema using a JSON Schema =head1 VERSION version 0.609 =head1 SYNOPSIS use JSON::Schema::Modern; $js = JSON::Schema::Modern->new( specification_version => 'draft2020-12', output_format => 'flag', ... # other options ); $result = $js->evaluate($instance_data, $schema_data); =head1 DESCRIPTION This module aims to be a fully-compliant L evaluator and validator, targeting the currently-latest L version of the specification. =head1 CONFIGURATION OPTIONS These values are all passed as arguments to the constructor. =head2 specification_version Indicates which version of the JSON Schema specification is used during evaluation. This value is overridden by the value determined from the C<$schema> keyword in the schema used in evaluation (when present), or defaults to the latest version (currently C). The use of the C<$schema> keyword in your schema is I encouraged to ensure continued correct operation of your schema. The current default value will not stay the same over time. May be one of: =over 4 =item * L or C<2020-12>|https://json-schema.org/specification-links.html#2020-12>, corresponding to metaschema C =item * L or C<2019-09>|https://json-schema.org/specification-links.html#2019-09-formerly-known-as-draft-8>, corresponding to metaschema C =item * L or C<7>|https://json-schema.org/specification-links.html#draft-7>, corresponding to metaschema C =item * L or C<6>|https://json-schema.org/specification-links.html#draft-6>, corresponding to metaschema C =item * L or C<4>|https://json-schema.org/specification-links.html#draft-4>, corresponding to metaschema C =back =head2 output_format One of: C, C, C, C. Defaults to C. C can only be used with C. Passed to L. =head2 short_circuit When true, evaluation will return early in any execution path as soon as the outcome can be determined, rather than continuing to find all errors or annotations. This option is safe to use in all circumstances, even in the presence of C and C keywords: the validation result will not change; only some errors will be omitted from the result. Defaults to true when C is C, and false otherwise. =head2 max_traversal_depth The maximum number of levels deep a schema traversal may go, before evaluation is halted. This is to protect against accidental infinite recursion, such as from two subschemas that each reference each other, or badly-written schemas that could be optimized. Defaults to 50. =head2 validate_formats When true, the C keyword will be treated as an assertion, not merely an annotation. Defaults to true when specification_version is draft4, draft6 or draft7, and false for all other versions, but this may change in the future. Note that the use of a format that does not have a defined handler will B be interpreted as an error in this mode; instead, the undefined format will simply be ignored. If you instead want this to be treated as an evaluation error, you must define a custom schema dialect that uses the format-assertion vocabulary (available in specification version C) and reference it in your schema with the C<$schema> keyword. =head2 format_validations =for stopwords subref An optional hashref that allows overriding the validation method for formats, or adding new ones. Overrides to existing formats (see L) must be specified in the form of C<< { $format_name => $format_sub } >>, where the format sub is a subref that takes one argument and returns a boolean result. New formats must be specified in the form of C<< { $format_name => { type => $type, sub => $format_sub } } >>, where the type indicates which of the core JSON Schema types (null, object, array, boolean, string, number, or integer) the instance value must be for the format validation to be considered. =head2 validate_content_schemas When true, the C and C keywords are not treated as pure annotations: C (when present) is used to decode the applied data payload and then C will be used as the media-type for decoding to produce the data payload which is then applied to the schema in C for validation. (Note that treating these keywords as anything beyond simple annotations is contrary to the specification, therefore this option defaults to false.) See L and L for adding additional type support. =for stopwords shhh Technically only draft4, draft6 and draft7 allow this and drafts 2019-09 and 2020-12 prohibit ever returning the subschema evaluation results together with their parent schema's results, so shhh. I'm trying to get this fixed for the next draft. =head2 collect_annotations When true, annotations are collected from keywords that produce them, when validation succeeds. These annotations are available in the returned result (see L). Not operational when L is C, C or C. Defaults to false. =head2 scalarref_booleans When true, any value that is expected to be a boolean B may also be expressed as the scalar references C<\0> or C<\1> (which are serialized as booleans by JSON backends). Defaults to false. =head2 stringy_numbers When true, any value that is expected to be a number or integer B may also be expressed as a string. This applies only to the following keywords: =over 4 =item * C (where both C and C (and possibly C) are considered valid) =item * C and C (where the string C<"1"> will match with C<"const": 1>) =item * C (where strings and numbers are compared numerically to each other, if either or both are numeric) =item * C =item * C =item * C =item * C =item * C =item * C (for formats defined to validate numbers) =back This allows you to write a schema like this (which validates a string representing an integer): type: string pattern: ^[0-9]$ multipleOf: 4 minimum: 16 maximum: 256 Such keywords are only applied if the value looks like a number, and do not generate a failure otherwise. Values are determined to be numbers via L. This option is only intended to be used for evaluating data from sources that can only be strings, such as the extracted value of an HTTP header or query parameter. Defaults to false. =head2 strict When true, unrecognized keywords are disallowed in schemas (they will cause an immediate abort in L or L). Defaults to false. =head1 METHODS =for Pod::Coverage BUILDARGS FREEZE THAW CACHED_METASCHEMAS METASCHEMA_URIS SPECIFICATION_VERSIONS_SUPPORTED SPECIFICATION_VERSION_DEFAULT =head2 evaluate_json_string $result = $js->evaluate_json_string($data_as_json_string, $schema); $result = $js->evaluate_json_string($data_as_json_string, $schema, { collect_annotations => 1 }); Evaluates the provided instance data against the known schema document. The data is in the form of a JSON-encoded string (in accordance with L). B The schema must be in one of these forms: =over 4 =item * a Perl data structure, such as what is returned from a JSON decode operation, =item * a L object, =item * or a URI string indicating the location where such a schema is located. =back Optionally, a hashref can be passed as a third parameter which allows changing the values of the L, L, L, L, L, L, and/or L settings for just this evaluation call. You can also pass use these keys to alter behaviour (these are generally only used by custom validation applications that contain embedded JSON Schemas): =over 4 =item * C: adjusts the effective path of the data instance as of the start of evaluation =item * C: adjusts the accumulated path as of the start of evaluation (or last C<$id> or C<$ref>) =item * C: adjusts the recorded absolute keyword location as of the start of evaluation =item * C: locations in errors and annotations are resolved against this URI (only useful when providing an inline schema that does not declare an absolute base URI for itself) =back The return value is a L object, which can also be used as a boolean. =head2 evaluate $result = $js->evaluate($instance_data, $schema); $result = $js->evaluate($instance_data, $schema, { short_circuit => 0 }); Evaluates the provided instance data against the known schema document. The data is in the form of an unblessed nested Perl data structure representing any type that JSON allows: null, boolean, string, number, object, array. (See L below.) The schema must be in one of these forms: =over 4 =item * a Perl data structure, such as what is returned from a JSON decode operation, =item * a L object, =item * or a URI string indicating the location where such a schema is located. =back Optionally, a hashref can be passed as a third parameter which allows changing the values of the L, L, L, L, L, L, and/or L settings for just this evaluation call. You can also pass use these keys to alter behaviour (these are generally only used by custom validation applications that contain embedded JSON Schemas): =over 4 =item * C: adjusts the effective path of the data instance as of the start of evaluation =item * C: adjusts the accumulated path as of the start of evaluation (or last C<$id> or C<$ref>) =item * C: locations in errors and annotations are resolved against this URI (only useful when providing an inline schema that does not declare an absolute base URI for itself) =back You can pass a series of callback subs to this method corresponding to keywords, which is useful for identifying various data that are not exposed by annotations. This feature is highly experimental and may change in the future. For example, to find the locations where all C<$ref> keywords are applied B: my @used_ref_at; $js->evaluate($data, $schema_or_uri, { callbacks => { '$ref' => sub ($data, $schema, $state) { push @used_ref_at, $state->{data_path}; } }, }); The return value is a L object, which can also be used as a boolean. Callbacks are not compatible with L mode. =head2 validate_schema $result = $js->validate_schema($schema); $result = $js->validate_schema($schema, $config_override); Evaluates the provided schema as instance data against its metaschema. Accepts C<$schema> and C<$config_override> parameters in the same form as L. =head2 traverse $result = $js->traverse($schema); $result = $js->traverse($schema, { initial_schema_uri => 'http://example.com' }); Traverses the provided schema without evaluating it against any instance data. Returns the internal state object accumulated during the traversal, including any identifiers found therein, and any errors found during parsing. For internal purposes only. Optionally, a hashref can be passed as a second parameter which alters some behaviour (these are generally only used by custom validation applications that contain embedded JSON Schemas): =over 4 =item * C: adjusts the accumulated path as of the start of evaluation (or last C<$id> or C<$ref>) =item * C: adjusts the absolute keyword location as of the start of evaluation =item * C: use the indicated URI as the metaschema =back You can pass a series of callback subs to this method corresponding to keywords, which is useful for extracting data from within schemas and skipping properties that may look like keywords but actually are not (for example C<{"const": {"$ref": "this is not actually a $ref"}}>). This feature is highly experimental and is highly likely to change in the future. For example, to find the resolved targets of all C<$ref> keywords in a schema document: my @refs; JSON::Schema::Modern->new->traverse($schema, { callbacks => { '$ref' => sub ($schema, $state) { push @refs, Mojo::URL->new($schema->{'$ref'}) ->to_abs(JSON::Schema::Modern::Utilities::canonical_uri($state)); } }, }); =head2 add_schema $js->add_schema($uri => $schema); $js->add_schema($schema); Introduces the (unblessed, nested) Perl data structure representing a JSON Schema to the implementation, registering it under the indicated URI if provided, and all identifiers found within the document will be resolved against this URI (if provided) and added as well. C<''> will be used if no other identifier can be found within. You B call C or L (below) for any external resources that a schema may reference via C<$ref> before calling L, other than the standard metaschemas which are loaded from a local cache as needed. If you add multiple schemas (either with this method, or implicitly via L) with no root identifier (either provided explicitly in the method call, or via an C<$id> keyword at the schema root), all such previous schemas are removed from memory and can no longer be referenced. If there were errors in the document, will die with these errors; otherwise returns the L that contains the added schema. =head2 add_document $js->add_document($uri => $document); $js->add_document($document); Makes the L (or subclass) object, representing a JSON Schema, available to the evaluator. All identifiers known to the document are added to the evaluator's resource index; if the C<$uri> argument is provided, those identifiers are resolved against C<$uri> as they are added. C<$uri> itself is also added to the resource index, referencing the root of the document itself. If you add multiple documents (either with this method, or implicitly via C or L) with no root identifier (either provided explicitly in the method call, or via an C<$id> keyword at the schema root), all such previous schemas are removed from memory and can no longer be referenced. If there were errors in the document, this method will die with these errors; otherwise it returns the L object. =head2 add_format_validation $js->add_format_validation(all_lc => sub ($value) { lc($value) eq $value }); =for comment we are the nine Eleven Deniers or $js->add_format_validation(no_nines => { type => 'number', sub => sub ($value) { $value =~ m/^[0-8]+$/ }); $js->add_format_validation(8bits => { type => 'string', sub => sub ($value) { $value =~ m/^[\x00-\xFF]+$/ }); Adds support for a custom format. If not supplied, the data type(s) that this format applies to defaults to string; all values of any other type will automatically be deemed to be valid, and will not be passed to the subref. Additionally, you can redefine the definition for any core format (see L), but the data type(s) supported by that format may not be changed. Be careful to not mutate the type of the value while checking it -- for example, if it is a string, do not apply arithmetic operators to it -- or subsequent type checks on this value may fail. See L for a registry of known and useful formats; for compatibility reasons, avoid defining a format listed here with different semantics. =head2 add_vocabulary $js->add_vocabulary('My::Custom::Vocabulary::Class'); Makes a custom vocabulary class available to metaschemas that make use of this vocabulary. as described in the specification at L<"Meta-Schemas and Vocabularies"|https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.8.1>. The class must compose the L role and implement the L and L methods, as well as C<< _traverse_keyword_ >> methods for each keyword. C<< _eval_keyword_ >> methods are optional; when not provided, evaluation will always return a true result. =head2 add_media_type $js->add_media_type('application/furble' => sub ($content_ref) { return ...; # data representing the deserialized text for Content-Type: application/furble }); Takes a media-type name and a subref which takes a single scalar reference, which is expected to be a reference to a string, which might contain wide characters (i.e. not octets), especially when used in conjunction with L below. Must return B (which is then dereferenced for the C keyword). These media types are already known: =over 4 =item * C - see L =item * C - see L =item * C - see L =item * C - passes strings through unchanged =item * C =item * C - see L =item * C - passes strings through unchanged =back =head2 get_media_type Fetches a decoder sub for the indicated media type. Lookups are performed B. =for stopwords thusly You can use it thusly: $js->add_media_type('application/furble' => sub { ... }); # as above my $decoder = $self->get_media_type('application/furble') or die 'cannot find media type decoder'; my $content_ref = $decoder->(\$content_string); =head2 add_encoding $js->add_encoding('bloop' => sub ($content_ref) { return \ ...; # data representing the deserialized content for Content-Transfer-Encoding: bloop }); Takes an encoding name and a subref which takes a single scalar reference, which is expected to be a reference to a string, which SHOULD be a 7-bit or 8-bit string. Result values MUST be a scalar-reference to a string (which is then dereferenced for the C keyword). =for stopwords natively Encodings handled natively are: =over 4 =item * C - passes strings through unchanged =item * C - see L =item * C - see L =back See also L. =head2 get_encoding Fetches a decoder sub for the indicated encoding. Incoming values MUST be a reference to an octet string. Result values will be a scalar-reference to a string, which might be passed to a media_type decoder (see above). You can use it thusly: my $decoder = $self->get_encoding('base64') or die 'cannot find encoding decoder'; my $content_ref = $decoder->(\$content_string); =head2 get my $schema = $js->get($uri); my ($schema, $canonical_uri) = $js->get($uri); Fetches the Perl data structure represented by the indicated identifier (uri or uri-reference). When called in list context, the canonical URI of that location is also returned, as a L. Returns C if the schema with that URI has not been loaded (or cached). Note that the data so returned may not be a JSON Schema, if the document encapsulating this location is a subclass of L (for example L, which contains addressable locations of various semantic types). =head2 get_document my $document = $js->get_document($uri_reference); Fetches the L object (or subclass) that contains the provided identifier (uri or uri-reference). C if the schema with that URI has not been loaded (or cached). =head1 CACHING =for stopwords preforking Very large documents, particularly those used by L, may take a noticeable time to be loaded and parsed. You can reduce the impact to your preforking application by loading all necessary documents at startup, and impact can be further reduced by saving objects to cache and then reloading them (perhaps by using a timestamp or checksum to determine if a fresh reload is needed). Custom L, L or L are not serialized, as they are represented by subroutine references, and will need to be manually added after thawing. sub get_evaluator (...) { my $serialized_file = Path::Tiny::path($filename); my $schema_file = Path::Tiny::path($schema_filename); my $js; if ($serialized_file->stat->mtime < $schema_file->stat->mtime)) { $js = JSON::Schema::Modern->new; $js->add_schema(decode_json($schema_file->slurp_raw)); # your application schema my $frozen = Sereal::Encoder->new({ freeze_callbacks => 1 })->encode($js); $serialized_file->spew_raw($frozen); } else { my $frozen = $serialized_file->slurp_raw; $js = Sereal::Decoder->new->decode($frozen); } # add custom format validations, media types and encodings here $js->add_media_type(...); return $js; } See also L. =head1 LIMITATIONS =head2 Types Perl is a more loosely-typed language than JSON. This module delves into a value's internal representation in an attempt to derive the true "intended" type of the value. This should not be an issue if data validation is occurring immediately after decoding a JSON payload, or if the JSON string itself is passed to this module. If you are having difficulties, make sure you are using Perl's fastest and most trusted and reliable JSON decoder, L. Other JSON decoders are known to produce data with incorrect data types, and data from other sources may also be problematic. For more information, see L. =head2 Format Validation By default (and unless you specify a custom metaschema with the C<$schema> keyword or L), formats are treated only as annotations, not assertions. When L is true, strings are also checked against the format as specified in the schema. At present the following formats are supported (use of any other formats than these will always evaluate as true, but remember you can always supply custom format handlers; see L above): =over 4 =item * C =item * C =item * C should include annotations in the output. Defaults to true. =head2 exception Indicates that evaluation stopped due to a severe error. =head2 recommended_response =for stopwords OpenAPI A tuple, consisting of C<[ integer, string ]>, indicating the recommended HTTP response code and string to use for this result (if validating an HTTP request). This could exist for things like a failed authentication check in OpenAPI validation, in which case it would contain C<[ 401, 'Unauthorized' ]>. Only populated when there are errors; when not explicitly set by an evaluator, defaults to C<< [ 500, 'Internal Server Error' ] >> if any errors indicate an exception, and C<< [ 400, ] >> otherwise. The exact error string is hidden in the case of 500 errors because you should not leak internal issues with your application, but you may also wish to obfuscate normal validation errors, in which case you should check for C<400> and change the string to C<'Bad Request'>. =head1 METHODS =for Pod::Coverage BUILD BUILDARGS OUTPUT_FORMATS result stringify annotation_count error_count true false =head2 format Returns a data structure suitable for serialization; requires one argument specifying the output format to use, which corresponds to the formats documented in L. The only supported formats at this time are C, C, C, and C. =head2 TO_JSON Calls L with the style configured in L. =head2 count Returns the number of annotations when the result is true, or the number of errors when the result is false. =head2 combine When provided with another result object, returns a new object with the combination of all results. See C<&> at L. =head2 dump Returns a JSON string representing the result object, using the requested L, according to the L and the L. =head1 SERIALIZATION Results (and their contained errors and annotations) can be serialized in a number of ways. Results have defined Ls, which can be generated as nested unblessed hashes/arrays and are suitable for serializing using a JSON encoder for use in another application. A JSON string of the result can be obtained directly using L. If it is preferable to omit direct references to the schema (for example in an application where the schema is not published), but still convey some semantic information about the nature of the errors, stringify the object directly. This also means that result objects can be thrown as exceptions, or embedded in error messages. If you are embedding the full result inside another data structure, perhaps to be serialized to JSON (or another format) later on, use L or L. =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Document.pm000640 000766 000024 00000037376 15001302761 023262 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Document; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: One JSON Schema document our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use Mojo::URL; use Carp 'croak'; use List::Util 1.29 'pairs'; use Ref::Util 0.100 'is_plain_hashref'; use builtin::compat 'refaddr'; use Safe::Isa 1.000008; use MooX::TypeTiny; use Types::Standard 1.016003 qw(InstanceOf HashRef Str Map Dict ArrayRef Enum ClassName Undef Slurpy Optional); use Types::Common::Numeric 'PositiveOrZeroInt'; use namespace::clean; extends 'Mojo::JSON::Pointer'; has schema => ( is => 'ro', required => 1, ); has canonical_uri => ( is => 'rwp', isa => (InstanceOf['Mojo::URL'])->where(q{not defined $_->fragment}), lazy => 1, default => sub { Mojo::URL->new }, coerce => sub { $_[0]->$_isa('Mojo::URL') ? $_[0] : Mojo::URL->new($_[0]) }, ); # this is also known as the retrieval uri in the OpenAPI specification has original_uri => ( is => 'rwp', isa => (InstanceOf['Mojo::URL'])->where(q{not defined $_->fragment}), init_arg => undef, ); *retrieval_uri = \&original_uri; has metaschema_uri => ( is => 'rwp', isa => (InstanceOf['Mojo::URL'])->where(q{not length $_->fragment}), # allow for .../draft-07/schema# coerce => sub { $_[0]->$_isa('Mojo::URL') ? $_[0] : Mojo::URL->new($_[0]) }, predicate => '_has_metaschema_uri', # default not defined here, but might be defined in a subclass ); has evaluator => ( is => 'rwp', isa => InstanceOf['JSON::Schema::Modern'], weak_ref => 1, ); # "A JSON Schema resource is a schema which is canonically identified by an absolute URI." # https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.4.3.5 my $path_type = Str->where('m{^(?:/|$)}'); # JSON pointer relative to the document root has resource_index => ( is => 'bare', isa => Map[my $resource_key_type = Str->where('!/#/'), my $resource_type = Dict[ # always stringwise-equal to the top level key canonical_uri => (InstanceOf['Mojo::URL'])->where(q{not defined $_->fragment}), path => $path_type, specification_version => Enum[qw(draft4 draft6 draft7 draft2019-09 draft2020-12)], # the vocabularies used when evaluating instance data against schema vocabularies => ArrayRef[ClassName->where(q{$_->DOES('JSON::Schema::Modern::Vocabulary')})], configs => HashRef, anchors => Optional[HashRef[Dict[ canonical_uri => (InstanceOf['Mojo::URL'])->where(q{not defined $_->fragment or substr($_->fragment, 0, 1) eq '/'}), path => $path_type, ]]], Slurpy[HashRef[Undef]], # no other fields allowed ]], init_arg => undef, default => sub { {} }, ); sub resource_index { $_[0]->{resource_index}->%* } sub resource_pairs { pairs $_[0]->{resource_index}->%* } sub _get_resource { $_[0]->{resource_index}{$_[1]} } sub _canonical_resources { values $_[0]->{resource_index}->%* } sub _add_resource { croak 'uri "'.$_[1].'" conflicts with an existing schema resource' if $_[0]->{resource_index}{$_[1]}; $_[0]->{resource_index}{$resource_key_type->($_[1])} = $resource_type->($_[2]); } # this is not "the path to the resource", but rather "have path, want resource" has _path_to_resource => ( is => 'ro', isa => HashRef[$resource_type], init_arg => undef, lazy => 1, default => sub { +{ map +($_->{path} => $_), shift->_canonical_resources } }, ); sub path_to_resource { $_[0]->_path_to_resource; $_[0]->{_path_to_resource}{$_[1]} } # for internal use only has _checksum => ( is => 'rw', isa => Str, init_arg => undef, ); has errors => ( is => 'bare', writer => '_set_errors', isa => ArrayRef[InstanceOf['JSON::Schema::Modern::Error']], lazy => 1, default => sub { [] }, ); sub errors { ($_[0]->{errors}//[])->@* } sub has_errors { scalar(($_[0]->{errors}//[])->@*) } # json pointer => entity name (indexed by integer) has _entities => ( is => 'ro', isa => HashRef[PositiveOrZeroInt], lazy => 1, default => sub { {} }, ); # in this class, the only entity type is 'schema', but subclasses add more sub __entities ($) { qw(schema) } sub __entity_type { Enum[$_[0]->__entities] } sub __entity_index ($self, $entity) { my @e = $self->__entities; foreach my $i (0..$#e) { return $i if $e[$i] eq $entity; } return undef; } sub _add_entity_location ($self, $location, $entity) { $self->__entity_type->($entity); # verify string $self->_entities->{$location} = $self->__entity_index($entity); # store integer-mapped value } sub get_entity_at_location ($self, $location) { return '' if not exists $self->_entities->{$location}; ($self->__entities)[ $self->_entities->{$location} ] // die "missing mapping for ", $self->_entities->{$location}; } # note: not sorted sub get_entity_locations ($self, $entity) { $self->__entity_type->($entity); # verify string my $index = $self->__entity_index($entity); grep $self->{_entities}{$_} == $index, keys $self->{_entities}->%*; } # shims for Mojo::JSON::Pointer sub data { shift->schema(@_) } sub FOREIGNBUILDARGS { () } # for JSON serializers sub TO_JSON { shift->schema } # note that this is always called, even in subclasses sub BUILD ($self, $args) { # note! not a clone! Please don't change canonical_uri in-place. $self->_set_original_uri($self->canonical_uri); my $state = $self->traverse( $self->evaluator // $self->_set_evaluator(JSON::Schema::Modern->new), $args->{specification_version} ? +{ $args->%{specification_version} } : (), ); if ($state->{errors}->@*) { $self->_set_errors($state->{errors}); return; } my $seen_root; foreach my $key (keys $state->{identifiers}->%*) { my $value = $state->{identifiers}{$key}; $self->_add_resource($key => $value); # we're adding a non-anchor entry for the document root ++$seen_root if $value->{path} eq ''; } # we only index the original uri if nothing in the schema itself identified a root resource: # otherwise the top of the document would be unreferenceable. $self->_add_resource($self->original_uri.'' => { path => '', canonical_uri => $self->canonical_uri, specification_version => $state->{spec_version}, vocabularies => $state->{vocabularies}, configs => $state->{configs}, }) if not $seen_root; $self->_add_entity_location($_, 'schema') foreach $state->{subschemas}->@*; } # a subclass's method will override this one sub traverse ($self, $evaluator, $config_override = {}) { die 'wrong class - use JSON::Schema::Modern::Document::OpenAPI instead' if is_plain_hashref($self->schema) and exists $self->schema->{openapi}; my $original_uri = $self->original_uri; my $state = $evaluator->traverse($self->schema, { initial_schema_uri => $original_uri, $self->_has_metaschema_uri ? ( metaschema_uri => $self->metaschema_uri ) : (), %$config_override, } ); die 'original_uri has changed' if $self->original_uri ne $original_uri or refaddr($self->original_uri) != refaddr($original_uri); # if the schema identified a canonical uri for itself via '$id', it overrides the initial value # Note that subclasses of this class may choose to identify the canonical uri in a different way $self->_set_canonical_uri($state->{initial_schema_uri}) if $state->{initial_schema_uri} ne $original_uri; return $state if $state->{errors}->@*; # we don't store the metaschema_uri in $state nor in resource_index, but we can figure it out # easily enough. my $metaschema_uri = (is_plain_hashref($self->schema) ? $self->schema->{'$schema'} : undef) // $self->metaschema_uri // $evaluator->METASCHEMA_URIS->{$state->{spec_version}}; $self->_set_metaschema_uri($metaschema_uri) if $metaschema_uri ne ($self->metaschema_uri//''); return $state; } sub validate ($self) { my $js = $self->$_call_if_can('evaluator') // JSON::Schema::Modern->new; return $js->evaluate($self->schema, $self->metaschema_uri); } # callback hook for Sereal::Encoder sub FREEZE ($self, $serializer) { +{ %$self } } # callback hook for Sereal::Decoder sub THAW ($class, $serializer, $data) { my $self = bless($data, $class); foreach my $attr (qw(schema _entities _checksum)) { die "serialization missing attribute '$attr': perhaps your serialized data was produced for an older version of $class?" if not exists $self->{$attr}; } return $self; } 1; __END__ =pod =encoding UTF-8 =for stopwords subschema =head1 NAME JSON::Schema::Modern::Document - One JSON Schema document =head1 VERSION version 0.609 =head1 SYNOPSIS use JSON::Schema::Modern::Document; my $document = JSON::Schema::Modern::Document->new( canonical_uri => 'https://example.com/v1/schema', metaschema_uri => 'https://example.com/my/custom/metaschema', schema => $schema, ); my $foo_definition = $document->get('/$defs/foo'); my %resource_index = $document->resource_index; my sanity_check = $document->validate; =head1 DESCRIPTION This class represents one JSON Schema document, to be used by L. =head1 CONSTRUCTOR ARGUMENTS Unless otherwise noted, these are also available as read-only accessors. =head2 schema The actual raw data representing the schema. Required. =head2 canonical_uri As a constructor value, represents the initial URI by which the document should be known, or a base URI to use to determine that value. The URI found in the root schema's C<$id> keyword is resolved against this URI to determine the final value, which is then stored in this accessor. As such, it can be considered the canonical URI for the document as a whole, from which subsequent C<$ref> keywords are resolved. The original passed-in value is saved in L. =head2 metaschema_uri =for stopwords metaschema schemas Sets the metaschema that is used to describe the document (or more specifically, any JSON Schemas contained within the document), which determines the specification version and vocabularies used during evaluation. Does not override any C<$schema> keyword actually present in the schema document. =head2 specification_version Only a constructor argument, not an accessor method. Indicates which version of the JSON Schema specification is used during evaluation. This value is overridden by the value determined from the C<$schema> keyword in the schema used in evaluation (when present), or defaults to the latest version (currently C). The use of the C<$schema> keyword in your schema is I encouraged to ensure continued correct operation of your schema. The current default value will not stay the same over time. May be one of: =over 4 =item * L or C<2020-12>|https://json-schema.org/specification-links.html#2020-12>, corresponding to metaschema C =item * L or C<2019-09>|https://json-schema.org/specification-links.html#2019-09-formerly-known-as-draft-8>, corresponding to metaschema C =item * L or C<7>|https://json-schema.org/specification-links.html#draft-7>, corresponding to metaschema C =item * L or C<6>|https://json-schema.org/specification-links.html#draft-6>, corresponding to metaschema C =item * L or C<4>|https://json-schema.org/specification-links.html#draft-4>, corresponding to metaschema C =back =head2 evaluator A L object. Optional, unless custom metaschemas are used. =head1 METHODS =for Pod::Coverage FOREIGNBUILDARGS BUILDARGS BUILD FREEZE THAW traverse has_errors path_to_resource resource_pairs get_entity_at_location get_entity_locations retrieval_uri =head2 errors Returns a list of L objects that resulted when the schema document was originally parsed. (If a syntax error occurred, usually there will be just one error, as parse errors halt the parsing process.) Documents with errors cannot be used for evaluation. =head2 original_uri Returns the original value of L that was passed to the document constructor (which C<$id> keywords within the document would have been resolved against, if they were not already absolute). Some subclasses may make use of this value for resolving URIs when matching HTTP requests at runtime. This URI is B added to the document's resource index, so if you want the document to be addressable at this location you must add it to the evaluator yourself with the two-argument form of L. Read-only. =head2 resource_index An index of URIs to subschemas (JSON pointer to reach the location, and the canonical URI of that location) for all identifiable subschemas found in the document. An entry for URI C<''> is added only when no other suitable identifier can be found for the root schema. This attribute should only be used by L and not intended for use externally (you should use the public accessors in L instead). When called as a method, returns the flattened list of tuples (path, uri). You can also use C which returns a list of tuples as arrayrefs. =head2 contains Check if L contains a value that can be identified with the given JSON Pointer. See L. =head2 get Extract value from L identified by the given JSON Pointer. See L. =head2 validate Evaluates the document against its metaschema. See L. For regular JSON Schemas this is redundant with creating the document in the first place (which also includes a validation check), but for some subclasses of this class, additional things might be checked that are not caught by document creation. =head2 TO_JSON Returns a data structure suitable for serialization. See L. =head1 SUBCLASSING =for stopwords OpenAPI referenceable This class can be subclassed to describe documents of other types, which follow the same basic model (has a document-level identifier and may contain internal referenceable identifiers). The overall document itself may not be a JSON Schema, but it may contain JSON Schemas internally. Referenceable entities may or may not be JSON Schemas. As long as the C method is implemented and the C<$state> object is respected, any other functionality may be contained by this subclass. To date, there is one subclass of JSON::Schema::Modern::Document: L, which contains entities of type C as well as others (e.g. C, C, C, etc). An object of this class represents one OpenAPI document, used by L to specify application APIs. =head1 SEE ALSO =over 4 =item * L =item * L =back =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Utilities.pm000640 000766 000024 00000045255 15001302761 023452 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Utilities; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Internal utilities for JSON::Schema::Modern our $VERSION = '0.609'; use 5.020; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use B; use Carp 'croak'; use Ref::Util 0.100 qw(is_ref is_plain_arrayref is_plain_hashref); use builtin::compat qw(blessed created_as_number); use Scalar::Util 'looks_like_number'; use Storable 'dclone'; use Feature::Compat::Try; use namespace::clean; use Exporter 'import'; our @EXPORT_OK = qw( is_type get_type is_bignum is_equal is_elements_unique jsonp unjsonp local_annotations canonical_uri E A abort assert_keyword_exists assert_keyword_type assert_pattern assert_uri_reference assert_uri annotate_self sprintf_num true false ); use JSON::PP (); use constant { true => JSON::PP::true, false => JSON::PP::false }; # supports the six core types, plus integer (which is also a number) # we do NOT check stringy_numbers here -- you must do that in the caller # note that sometimes a value may return true for more than one type, e.g. integer+number, # or number+string, depending on its internal flags. # pass { legacy_ints => 1 } in $config to use draft4 integer behaviour sub is_type ($type, $value, $config = {}) { if ($type eq 'null') { return !(defined $value); } if ($type eq 'boolean') { return is_bool($value); } if ($type eq 'object') { return is_plain_hashref($value); } if ($type eq 'array') { return is_plain_arrayref($value); } if ($type eq 'string' or $type eq 'number' or $type eq 'integer') { return 0 if not defined $value; my $flags = B::svref_2object(\$value)->FLAGS; # dualvars with the same string and (stringified) numeric value could be either a string or a # number, and before 5.36 we can't tell the difference, so we will answer yes for both. # in 5.36+, stringified numbers still get a PV but don't have POK set, whereas # numified strings do have POK set, so we can tell which one came first. if ($type eq 'string') { # like created_as_string, but rejects dualvars with stringwise-unequal string and numeric parts return !is_ref($value) && $flags & B::SVf_POK && (!($flags & (B::SVf_IOK | B::SVf_NOK)) || do { no warnings 'numeric'; 0+$value eq $value }); } if ($type eq 'number') { # floats in json will always be parsed into Math::BigFloat, when allow_bignum is enabled return is_bignum($value) || created_as_number($value); } if ($type eq 'integer') { if ($config->{legacy_ints}) { # in draft4, an integer is "A JSON number without a fraction or exponent part.", # therefore 2.0 is NOT an integer return ref($value) eq 'Math::BigInt' || ($flags & B::SVf_IOK) && !($flags & B::SVf_NOK) && created_as_number($value); } else { # note: values that are larger than $Config{ivsize} will be represented as an NV, not IV, # therefore they will fail this check return is_bignum($value) && $value->is_int # if dualvar, PV and stringified NV/IV must be identical || created_as_number($value) && int($value) == $value; } } } if ($type =~ /^reference to (.+)$/) { return !blessed($value) && ref($value) eq $1; } return ref($value) eq $type; } # returns one of the six core types, plus integer # we do NOT check stringy_numbers here -- you must do that in the caller # pass { legacy_ints => 1 } in $config to use draft4 integer behaviour sub get_type ($value, $config = {}) { return 'object' if is_plain_hashref($value); return 'boolean' if is_bool($value); return 'null' if not defined $value; return 'array' if is_plain_arrayref($value); # floats in json will always be parsed into Math::BigFloat, when allow_bignum is enabled if (is_ref($value)) { my $ref = ref($value); return $ref eq 'Math::BigInt' ? 'integer' # note: this will be wrong for Math::BigFloat->new('1.0') in draft4 : $ref eq 'Math::BigFloat' ? ($value->is_int ? 'integer' : 'number') : (defined blessed($value) ? '' : 'reference to ').$ref; } my $flags = B::svref_2object(\$value)->FLAGS; # dualvars with the same string and (stringified) numeric value could be either a string or a # number, and before 5.36 we can't tell the difference, so we choose number because it has been # evaluated as a number already. # in 5.36+, stringified numbers still get a PV but don't have POK set, whereas # numified strings do have POK set, so we can tell which one came first. # like created_as_string, but rejects dualvars with stringwise-unequal string and numeric parts return 'string' if $flags & B::SVf_POK && (!($flags & (B::SVf_IOK | B::SVf_NOK)) || do { no warnings 'numeric'; 0+$value eq $value }); if ($config->{legacy_ints}) { # in draft4, an integer is "A JSON number without a fraction or exponent part.", # therefore 2.0 is NOT an integer return ($flags & B::SVf_IOK) && !($flags & B::SVf_NOK) ? 'integer' : 'number' if created_as_number($value); } else { # note: values that are larger than $Config{ivsize} will be represented as an NV, not IV, # therefore they will fail this check return int($value) == $value ? 'integer' : 'number' if created_as_number($value); } # this might be a scalar with POK|IOK or POK|NOK set return 'ambiguous type'; } # lifted from JSON::MaybeXS # note: unlike builtin::compat::is_bool on older perls, we do not accept # dualvar(0,"") or dualvar(1,"1") because JSON::PP and Cpanel::JSON::XS # do not encode these as booleans. use constant HAVE_BUILTIN => "$]" >= 5.035010; use if HAVE_BUILTIN, experimental => 'builtin'; sub is_bool ($value) { HAVE_BUILTIN and builtin::is_bool($value) or !!blessed($value) and ($value->isa('JSON::PP::Boolean') or $value->isa('Cpanel::JSON::XS::Boolean') or $value->isa('JSON::XS::Boolean')); } sub is_bignum ($value) { ref($value) =~ /^Math::Big(?:Int|Float)$/; } # compares two arbitrary data payloads for equality, as per # https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.4.2.2 # $state hashref supports the following fields: # - stringy_numbers (input): strings will also be compared numerically # - path (output): location of the first difference # - error (output): description of the difference sub is_equal ($x, $y, $state = {}) { $state->{path} //= ''; my @types = map get_type($_), $x, $y; $state->{error} = 'ambiguous type encountered', return 0 if grep $types[$_] eq 'ambiguous type', 0..1; if ($state->{scalarref_booleans}) { ($x, $types[0]) = (0+!!$$x, 'boolean') if $types[0] eq 'reference to SCALAR'; ($y, $types[1]) = (0+!!$$y, 'boolean') if $types[1] eq 'reference to SCALAR'; } if ($state->{stringy_numbers}) { ($x, $types[0]) = (0+$x, int(0+$x) == $x ? 'integer' : 'number') if $types[0] eq 'string' and looks_like_number($x); ($y, $types[1]) = (0+$y, int(0+$y) == $y ? 'integer' : 'number') if $types[1] eq 'string' and looks_like_number($y); } $state->{error} = "wrong type: $types[0] vs $types[1]", return 0 if $types[0] ne $types[1]; return 1 if $types[0] eq 'null'; ($x eq $y and return 1), $state->{error} = 'strings not equal', return 0 if $types[0] eq 'string'; ($x == $y and return 1), $state->{error} = "$types[0]s not equal", return 0 if grep $types[0] eq $_, qw(boolean number integer); my $path = $state->{path}; if ($types[0] eq 'object') { $state->{error} = 'property count differs: '.keys(%$x).' vs '.keys(%$y), return 0 if keys %$x != keys %$y; if (not is_equal(my $arr_x = [ sort keys %$x ], my $arr_y = [ sort keys %$y ], my $s={})) { my $pos = substr($s->{path}, 1); $state->{error} = 'property names differ starting at position '.$pos.' ("'.$arr_x->[$pos].'" vs "'.$arr_y->[$pos].'")'; return 0; } foreach my $property (sort keys %$x) { $state->{path} = jsonp($path, $property); return 0 if not is_equal($x->{$property}, $y->{$property}, $state); } return 1; } if ($types[0] eq 'array') { $state->{error} = 'element count differs: '.@$x.' vs '.@$y, return 0 if @$x != @$y; foreach my $idx (0 .. $x->$#*) { $state->{path} = $path.'/'.$idx; return 0 if not is_equal($x->[$idx], $y->[$idx], $state); } return 1; } $state->{error} = 'uh oh', return 0; # should never get here } # checks array elements for uniqueness. short-circuits on first pair of matching elements # if second arrayref is provided, it is populated with the indices of identical items # $state hashref supports the following fields: # - scalarref_booleans (input): treats \0 and \1 as boolean values # - stringy_numbers (input): strings will also be compared numerically sub is_elements_unique ($array, $equal_indices = undef, $state = {}) { my %s = $state->%{qw(scalarref_booleans stringy_numbers)}; foreach my $idx0 (0 .. $array->$#*-1) { foreach my $idx1 ($idx0+1 .. $array->$#*) { if (is_equal($array->[$idx0], $array->[$idx1], \%s)) { push @$equal_indices, $idx0, $idx1 if defined $equal_indices; return 0; } } } return 1; } # shorthand for creating and appending json pointers # the first argument is an already-encoded json pointer; remaining arguments are path segments to be # encoded and appended sub jsonp { warn q{first argument to jsonp should be '' or start with '/'} if length($_[0]) and substr($_[0],0,1) ne '/'; return join('/', shift, map s/~/~0/gr =~ s!/!~1!gr, grep defined, @_); } # splits a json pointer apart into its path segments sub unjsonp ($path) { return map s!~0!~!gr =~ s!~1!/!gr, split m!/!, $path; } # get all annotations produced for the current instance data location (that are visible to this # schema location) - remember these are hashrefs, not Annotation objects sub local_annotations ($state) { grep $_->{instance_location} eq $state->{data_path}, $state->{annotations}->@*; } # shorthand for finding the current uri of the present schema location # ensure that this code is kept consistent with the absolute_keyword_location builder in ResultNode # Note that this may not be canonical if schema_path has not yet been reset via the processing of a # local identifier keyword (e.g. '$id'). sub canonical_uri ($state, @extra_path) { return $state->{initial_schema_uri} if not @extra_path and not length($state->{schema_path}); my $uri = $state->{initial_schema_uri}->clone; my $fragment = ($uri->fragment//'').(@extra_path ? jsonp($state->{schema_path}, @extra_path) : $state->{schema_path}); undef $fragment if not length($fragment); $uri->fragment($fragment); $uri; } # shorthand for creating error objects # uses these keys from $state: # - initial_schema_uri # - effective_base_uri (optional) # - keyword (optional) # - data_path # - traversed_schema_path # - schema_path # - _schema_path_suffix (optional) # - errors # - exception (optional; set by abort()) # - recommended_response (optional) # - depth # - traverse (boolean, used for mode) # returns defined-false, so callers can use 'return;' to differentiate between # failed-with-no-error from failed-with-error. sub E ($state, $error_string, @args) { croak 'E called in void context' if not defined wantarray; # sometimes the keyword shouldn't be at the very end of the schema path my $sps = delete $state->{_schema_path_suffix}; my @schema_path_suffix = defined $sps && is_plain_arrayref($sps) ? $sps->@* : $sps//(); # we store the absolute uri in unresolved form until needed, # and perform the rest of the calculations later. my $uri = [ $state->{initial_schema_uri}, $state->{schema_path}, ($state->{keyword}//()), @schema_path_suffix, $state->{effective_base_uri} ]; my $keyword_location = $state->{traversed_schema_path} .jsonp($state->{schema_path}, $state->{keyword}, @schema_path_suffix); require JSON::Schema::Modern::Error; push $state->{errors}->@*, JSON::Schema::Modern::Error->new( depth => $state->{depth} // 0, keyword => $state->{keyword}, instance_location => $state->{data_path}, keyword_location => $keyword_location, # we calculate absolute_keyword_location when instantiating the Error object for Result _uri => $uri, error => @args ? sprintf($error_string, @args) : $error_string, $state->{exception} ? ( exception => $state->{exception} ) : (), $state->{recommended_response} ? ( recommended_response => $state->{recommended_response} ) : (), mode => $state->{traverse} ? 'traverse' : 'evaluate', ); return 0; } # shorthand for creating annotations # uses these keys from $state: # - initial_schema_uri # - keyword (mandatory) # - data_path # - traversed_schema_path # - schema_path # - _schema_path_suffix (optional) # - annotations # - collect_annotations # - spec_version # - _unknown (boolean) # - depth sub A ($state, $annotation) { return 1 if not $state->{collect_annotations}; # we store the absolute uri in unresolved form until needed, # and perform the rest of the calculations later. my $uri = [ $state->{initial_schema_uri}, $state->{schema_path}, $state->{keyword}, $state->{effective_base_uri} ]; my $keyword_location = $state->{traversed_schema_path} .jsonp($state->{schema_path}, $state->{keyword}); push $state->{annotations}->@*, { depth => $state->{depth} // 0, keyword => $state->{keyword}, instance_location => $state->{data_path}, keyword_location => $keyword_location, # we calculate absolute_keyword_location when instantiating the Annotation object for Result _uri => $uri, annotation => $annotation, $state->{_unknown} ? ( unknown => 1 ) : (), }; return 1; } # creates an error object, but also aborts evaluation immediately # only this error is returned, because other errors on the stack might not actually be "real" # errors (consider if we were in the middle of evaluating a "not" or "if"). # Therefore this is only appropriate during the evaluation phase, not the traverse phase. sub abort ($state, $error_string, @args) { ()= E({ %$state, exception => 1 }, $error_string, @args); croak 'abort() called during traverse' if $state->{traverse}; die pop $state->{errors}->@*; } sub assert_keyword_exists ($state, $schema) { croak 'assert_keyword_exists called in void context' if not defined wantarray; return E($state, '%s keyword is required', $state->{keyword}) if not exists $schema->{$state->{keyword}}; return 1; } sub assert_keyword_type ($state, $schema, $type) { croak 'assert_keyword_type called in void context' if not defined wantarray; return 1 if is_type($type, $schema->{$state->{keyword}}); E($state, '%s value is not a%s %s', $state->{keyword}, ($type =~ /^[aeiou]/ ? 'n' : ''), $type); } sub assert_pattern ($state, $pattern) { croak 'assert_pattern called in void context' if not defined wantarray; try { local $SIG{__WARN__} = sub { die @_ }; qr/$pattern/; } catch ($e) { return E($state, $e); }; return 1; } # this is only suitable for checking URIs within schemas themselves sub assert_uri_reference ($state, $schema) { croak 'assert_uri_reference called in void context' if not defined wantarray; my $string = $schema->{$state->{keyword}}; return E($state, '%s value is not a valid URI reference', $state->{keyword}) # see also uri-reference format sub if fc(Mojo::URL->new($string)->to_unsafe_string) ne fc($string) or $string =~ /[^[:ascii:]]/ # ascii characters only or $string =~ /#/ # no fragment, except... and $string !~ m{#$} # allow empty fragment and $string !~ m{#[A-Za-z][A-Za-z0-9_:.-]*$} # allow plain-name fragment and $string !~ m{#/(?:[^~]|~[01])*$}; # allow json pointer fragment return 1; } # this is only suitable for checking URIs within schemas themselves, # which have fragments consisting of plain names (anchors) or json pointers sub assert_uri ($state, $schema, $override = undef) { croak 'assert_uri called in void context' if not defined wantarray; my $string = $override // $schema->{$state->{keyword}}; my $uri = Mojo::URL->new($string); return E($state, '"%s" is not a valid URI', $string) # see also uri format sub if fc($uri->to_unsafe_string) ne fc($string) or $string =~ /[^[:ascii:]]/ # ascii characters only or not $uri->is_abs # must have a scheme or $string =~ /#/ # no fragment, except... and $string !~ m{#$} # empty fragment and $string !~ m{#[A-Za-z][A-Za-z0-9_:.-]*$} # plain-name fragment and $string !~ m{#/(?:[^~]|~[01])*$}; # json pointer fragment return 1; } # produces an annotation whose value is the same as that of the current schema keyword # makes a copy as this is passed back to the user, who cannot be trusted to not mutate it sub annotate_self ($state, $schema) { A($state, is_ref($schema->{$state->{keyword}}) ? dclone($schema->{$state->{keyword}}) : $schema->{$state->{keyword}}); } # use original value as stored in the NV, without losing precision sub sprintf_num ($value) { is_bignum($value) ? $value->bstr : sprintf('%s', $value); } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Utilities - Internal utilities for JSON::Schema::Modern =head1 VERSION version 0.609 =head1 SYNOPSIS use JSON::Schema::Modern::Utilities qw(func1 func2..); =head1 DESCRIPTION This class contains internal utilities to be used by L. =for Pod::Coverage is_type get_type is_bignum is_bool is_equal is_elements_unique jsonp unjsonp local_annotations canonical_uri E A abort assert_keyword_exists assert_keyword_type assert_pattern assert_uri_reference assert_uri annotate_self sprintf_num HAVE_BUILTIN true false =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/000770 000766 000024 00000000000 15001302761 023236 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary.pm000640 000766 000024 00000014532 15001302761 023600 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Base role for JSON Schema vocabulary classes our $VERSION = '0.609'; use 5.020; use Moo::Role; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use Ref::Util 0.100 'is_plain_arrayref'; use JSON::Schema::Modern::Utilities qw(jsonp assert_keyword_type abort); use Carp (); use namespace::clean; our @CARP_NOT = qw(JSON::Schema::Modern); requires qw(vocabulary keywords); sub evaluation_order { 999 } # override, if needed sub BUILD { die 'these classes are never instantiated' } sub traverse ($class, $schema, $state) { $state->{evaluator}->_traverse_subschema($schema, $state); } sub traverse_subschema ($class, $schema, $state) { $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}, +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword} }); } sub traverse_array_schemas ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'array'); return E($state, '%s array is empty', $state->{keyword}) if not $schema->{$state->{keyword}}->@*; my $valid = 1; foreach my $idx (0 .. $schema->{$state->{keyword}}->$#*) { $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}[$idx], +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword}.'/'.$idx }); } return $valid; } sub traverse_object_schemas ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'object'); my $valid = 1; foreach my $property (sort keys $schema->{$state->{keyword}}->%*) { $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property}, +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) }); } return $valid; } sub traverse_property_schema ($class, $schema, $state, $property) { return if not assert_keyword_type($state, $schema, 'object'); $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property}, +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) }); } sub eval ($class, $data, $schema, $state) { $state->{evaluator}->_eval_subschema($data, $schema, $state); } sub eval_subschema_at_uri ($class, $data, $schema, $state, $uri) { my $schema_info = $state->{evaluator}->_fetch_from_uri($uri); abort($state, 'EXCEPTION: unable to find resource "%s"', $uri) if not $schema_info; abort($state, 'EXCEPTION: bad reference to "%s": not a schema', $schema_info->{canonical_uri}) if $schema_info->{document}->get_entity_at_location($schema_info->{document_path}) ne 'schema'; return $state->{evaluator}->_eval_subschema($data, $schema_info->{schema}, +{ $schema_info->{configs}->%*, %$state, # keyword is assumed to be json pointer-encoded (if a suffix path is needed), so we just concat traversed_schema_path => $state->{traversed_schema_path}.$state->{schema_path}.'/'.$state->{keyword}, initial_schema_uri => $schema_info->{canonical_uri}, document => $schema_info->{document}, document_path => $schema_info->{document_path}, spec_version => $schema_info->{specification_version}, schema_path => '', vocabularies => $schema_info->{vocabularies}, }); } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary - Base role for JSON Schema vocabulary classes =head1 VERSION version 0.609 =head1 SYNOPSIS package MyApp::Vocabulary::Awesome; use Moo::Role; with 'JSON::Schema::Modern::Vocabulary'; =head1 DESCRIPTION This package is the role which all all vocabulary classes for L must compose, describing the basic structure expected of a vocabulary class. =head1 ATTRIBUTES =head1 METHODS =for Pod::Coverage BUILD =for stopwords schema subschema =head2 vocabulary Returns the canonical URI(s) describing the vocabulary for each draft specification version, as described in L. Must be implemented by the composing class. =head2 evaluation_order Returns a positive integer, used as a sort key for determining the evaluation order of this vocabulary. If not overridden in a custom vocabulary class, its evaluation order will be after all built-in vocabularies. You probably don't need to define this. =head2 keywords Returns the list of keywords defined by the vocabulary. Must be implemented by the composing class. =head2 traverse Traverses a subschema. Callers are expected to establish a new C<$state> scope. =head2 traverse_subschema Recursively traverses the schema at the current keyword, as in the C keyword. =head2 traverse_array_schemas Recursively traverses the list of subschemas at the current keyword, as in the C keyword. =head2 traverse_object_schemas Recursively traverses the (subschema) values of the object at the current keyword, as in the C<$defs> keyword. =head2 traverse_property_schema Recursively traverses the subschema under one property of the object at the current keyword. =head2 eval Evaluates a subschema. Callers are expected to establish a new C<$state> scope. =head2 eval_subschema_at_uri Resolves a URI to a subschema, then evaluates that subschema (essentially the C<$ref> keyword). =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/MetaData.pm000640 000766 000024 00000010503 15001302761 025252 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::MetaData; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Meta-Data vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use JSON::Schema::Modern::Utilities qw(assert_keyword_type annotate_self); use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2019-09/vocab/meta-data' => 'draft2019-09', 'https://json-schema.org/draft/2020-12/vocab/meta-data' => 'draft2020-12'; } sub evaluation_order ($class) { 5 } sub keywords ($class, $spec_version) { return ( qw(title description default), $spec_version !~ /^draft[467]$/ ? 'deprecated' : (), $spec_version !~ /^draft[46]$/ ? qw(readOnly writeOnly) : (), $spec_version ne 'draft4' ? 'examples' : (), ); } sub _traverse_keyword_title ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string'); return 1; } sub _eval_keyword_title ($class, $data, $schema, $state) { annotate_self($state, $schema); } *_traverse_keyword_description = \&_traverse_keyword_title; *_eval_keyword_description = \&_eval_keyword_title; sub _traverse_keyword_default { 1 } *_eval_keyword_default = \&_eval_keyword_title; sub _traverse_keyword_deprecated ($class, $schema, $state) { return assert_keyword_type($state, $schema, 'boolean'); } *_eval_keyword_deprecated = \&_eval_keyword_title; *_traverse_keyword_readOnly = \&_traverse_keyword_deprecated; *_eval_keyword_readOnly = \&_eval_keyword_title; *_traverse_keyword_writeOnly = \&_traverse_keyword_deprecated; *_eval_keyword_writeOnly = \&_eval_keyword_title; sub _traverse_keyword_examples ($class, $schema, $state) { return assert_keyword_type($state, $schema, 'array'); } *_eval_keyword_examples = \&_eval_keyword_title; 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::MetaData - Implementation of the JSON Schema Meta-Data vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Meta-Data" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for =over 4 =item * the equivalent Draft 2019-09 keywords, indicated in metaschemas with the URI C and formally specified in L. =item * the equivalent Draft 7 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 6 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 4 keywords that correspond to this vocabulary and are formally specified in L. =back =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/Core.pm000640 000766 000024 00000046644 15001302761 024501 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::Core; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Core vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use Ref::Util 'is_plain_hashref'; use JSON::Schema::Modern::Utilities qw(is_type abort assert_keyword_type canonical_uri E assert_uri_reference assert_uri jsonp); use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2019-09/vocab/core' => 'draft2019-09', 'https://json-schema.org/draft/2020-12/vocab/core' => 'draft2020-12'; } sub evaluation_order ($class) { 0 } sub keywords ($class, $spec_version) { return ( '$schema', $spec_version eq 'draft4' ? 'id' : '$id', $spec_version !~ /^draft[467]$/ ? '$anchor' : (), $spec_version eq 'draft2019-09' ? '$recursiveAnchor' : (), $spec_version !~ /^draft(?:[467]|2019-09)$/ ? '$dynamicAnchor' : (), '$ref', $spec_version eq 'draft2019-09' ? '$recursiveRef' : (), $spec_version !~ /^draft(?:[467]|2019-09)$/ ? '$dynamicRef' : (), $spec_version !~ /^draft[467]$/ ? '$vocabulary' : (), $spec_version =~ /^draft[467]$/ ? 'definitions' : '$defs', $spec_version !~ /^draft[46]$/ ? '$comment' : (), ); } # adds the following keys to $state during traversal: # - identifiers: an arrayref of tuples: # $uri => { path => $path_to_identifier, canonical_uri => Mojo::URL (absolute when possible) } # this is used by the Document constructor to build its resource_index. sub _traverse_keyword_id ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string'); my $uri = Mojo::URL->new($schema->{$state->{keyword}}); if ($state->{spec_version} =~ /^draft[467]$/) { # technically the draft4 spec allows this form, but we are not supporting it. if (length($uri->fragment)) { return E($state, '%s cannot change the base uri at the same time as declaring an anchor', $state->{keyword}) if length($uri->clone->fragment(undef)); return $class->_traverse_keyword_anchor($schema, $state); } } else { return if not assert_uri_reference($state, $schema); return E($state, '%s value "%s" cannot have a non-empty fragment', $state->{keyword}, $schema->{$state->{keyword}}) if length $uri->fragment; } $uri->fragment(undef); return E($state, '%s cannot be empty', $state->{keyword}) if not length $uri; $uri = $uri->to_abs($state->{initial_schema_uri}) if not $uri->is_abs; return E($state, 'duplicate canonical uri "%s" found (original at path "%s")', $uri, $state->{identifiers}{$uri}{path}) if exists $state->{identifiers}{$uri}; $state->{initial_schema_uri} = $uri; $state->{traversed_schema_path} = $state->{traversed_schema_path}.$state->{schema_path}; # we don't set or update document_path because it is identical to traversed_schema_path $state->{schema_path} = ''; # Note that even though '$id' is considered ahead of '$schema' in the keyword list, we have # already parsed the '$schema' keyword (before we even started looping through all vocabularies) # and therefore this data (specification_version and vocabularies) is known to be correct. $state->{identifiers}{$uri} = { path => $state->{traversed_schema_path}, canonical_uri => $uri, specification_version => $state->{spec_version}, # note! $schema keyword can change this vocabularies => $state->{vocabularies}, # reference, not copy configs => $state->{configs}, }; return 1; } sub _eval_keyword_id ($class, $data, $schema, $state) { # we already indexed the anchor uri, so there is nothing more to do at evaluation time. # we explicitly do NOT set $state->{initial_schema_uri} or change any other $state values. return 1 if $state->{spec_version} =~ /^draft[467]$/ and $schema->{$state->{keyword}} =~ /^#/; # we could use _fetch_from_uri here to use the parent base uri to resolve against $id at our # current location, but looking up the current path in the resource index is faster. my $schema_info = $state->{document}->path_to_resource($state->{document_path}.$state->{schema_path}); # this should never happen, if the pre-evaluation traversal was performed correctly abort($state, 'failed to resolve "%s" to canonical uri', $state->{keyword}) if not $schema_info; $state->{initial_schema_uri} = $schema_info->{canonical_uri}->clone; # these will already be set in all cases: at document root, or if we are here via a $ref $state->{traversed_schema_path} = $state->{traversed_schema_path}.$state->{schema_path}; $state->{document_path} = $state->{document_path}.$state->{schema_path}; $state->{schema_path} = ''; # these will already be set if there is an adjacent $schema keyword, or if we are here via a $ref $state->{spec_version} = $schema_info->{specification_version}; $state->{vocabularies} = $schema_info->{vocabularies}; $state->@{keys $state->{configs}->%*} = values $state->{configs}->%*; push $state->{dynamic_scope}->@*, $state->{initial_schema_uri}; return 1; } sub _traverse_keyword_schema ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string') or not assert_uri($state, $schema); my ($spec_version, $vocabularies); if (my $metaschema_info = $state->{evaluator}->_get_metaschema_vocabulary_classes($schema->{'$schema'})) { ($spec_version, $vocabularies) = @$metaschema_info; } else { my $schema_info = $state->{evaluator}->_fetch_from_uri($schema->{'$schema'}); return E($state, 'EXCEPTION: unable to find resource "%s"', $schema->{'$schema'}) if not $schema_info; # this cannot happen unless there are other entity types in the index return E($state, 'EXCEPTION: bad reference to $schema "%s": not a schema', $schema_info->{canonical_uri}) if $schema_info->{document}->get_entity_at_location($schema_info->{document_path}) ne 'schema'; if (not is_plain_hashref($schema_info->{schema})) { ()= E($state, 'metaschemas must be objects'); } else { ($spec_version, $vocabularies) = $state->{evaluator}->_fetch_vocabulary_data({ %$state, keyword => '$vocabulary', initial_schema_uri => Mojo::URL->new($schema->{'$schema'}), traversed_schema_path => jsonp($state->{schema_path}, '$schema') }, $schema_info); } } return E($state, '"%s" is not a valid metaschema', $schema->{'$schema'}) if not $vocabularies or not @$vocabularies; # "A JSON Schema resource is a schema which is canonically identified by an absolute URI." # "A resource's root schema is its top-level schema object." # note: we need not be at the document root, but simply adjacent to an $id (or be the at the # document root) return E($state, '$schema can only appear at the schema resource root') if not exists $schema->{$spec_version eq 'draft4' ? 'id' : '$id'} and length($state->{schema_path}); # This is a bit of a chicken-and-egg situation. If we start off at draft2020-12, then all # keywords are valid, so we inspect and process the $schema keyword; this switches us to draft7 # but now only the $ref keyword is respected and everything else should be ignored, so the # $schema keyword never happened, so now we're back to draft2020-12 again, and...?! # The only winning move is not to play. return E($state, '$schema and $ref cannot be used together in older drafts') if exists $schema->{'$ref'} and $spec_version =~ /^draft[467]$/; $state->{evaluator}->_set_metaschema_vocabulary_classes($schema->{'$schema'}, [ $spec_version, $vocabularies ]); $state->@{qw(spec_version vocabularies)} = ($spec_version, $vocabularies); return 1; } sub _eval_keyword_schema ($class, $data, $schema, $state) { # we can't always rely on metaschema information being set in $state (if we recursed into this # subschema from a parent, where the data is not already set via $ref), and we need to do it now # so the evaluator knows whether to look for an 'id' or an '$id' keyword next (which sets up the # remaining data in $state). $state->@{qw(spec_version vocabularies)} = $state->{evaluator}->_get_metaschema_vocabulary_classes($schema->{'$schema'})->@*; return 1; } sub _traverse_keyword_anchor ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string'); my $anchor = $schema->{$state->{keyword}}; return E($state, '%s value "%s" does not match required syntax', $state->{keyword}, $anchor) if $state->{spec_version} =~ /^draft[467]$/ and $anchor !~ /^#[A-Za-z][A-Za-z0-9_:.-]*$/ or $state->{spec_version} eq 'draft2019-09' and $anchor !~ /^[A-Za-z][A-Za-z0-9_:.-]*$/ or $state->{spec_version} eq 'draft2020-12' and $anchor !~ /^[A-Za-z_][A-Za-z0-9._-]*$/; my $canonical_uri = canonical_uri($state); $anchor =~ s/^#// if $state->{spec_version} =~ /^draft[467]$/; my $uri = Mojo::URL->new->to_abs($canonical_uri)->fragment($anchor); my $base_uri = $canonical_uri->clone->fragment(undef); if (exists $state->{identifiers}{$base_uri}) { return E($state, 'duplicate anchor uri "%s" found (original at path "%s")', $uri, $state->{identifiers}{$base_uri}{anchors}{$anchor}{path}) if exists(($state->{identifiers}{$base_uri}{anchors}//{})->{$anchor}); use autovivification 'store'; $state->{identifiers}{$base_uri}{anchors}{$anchor} = { canonical_uri => $canonical_uri, path => $state->{traversed_schema_path}.$state->{schema_path}, }; } # we need not be at the root of the resource schema, and we need not even have an entry # in 'identifiers' for our current base uri (if there was no $id at the root, or if # initial_schema_uri was overridden in the call to traverse()) else { my $base_path = ''; if (my $fragment = $canonical_uri->fragment) { # this shouldn't happen, as we also check this at the start of traverse return E($state, 'something is wrong; "%s" is not the suffix of "%s"', $fragment, $state->{traversed_schema_path}.$state->{schema_path}) if substr($state->{traversed_schema_path}.$state->{schema_path}, -length($fragment)) ne $fragment; $base_path = substr($state->{traversed_schema_path}.$state->{schema_path}, 0, -length($fragment)); } $state->{identifiers}{$base_uri} = { # We didn't see an $id keyword at this position or above us, so a resource entry hasn't been # made yet for this identifier. However, we have all the information we need to infer its # data. If this entry is being created in a subschema (below the document root), another one # just like it may be created by another subschema using the same base canonical uri, so that # caller will need to merge the entries together before providing them to the Document's # resource index. canonical_uri => $base_uri, path => $base_path, specification_version => $state->{spec_version}, vocabularies => $state->{vocabularies}, # reference, not copy configs => $state->{configs}, anchors => { $anchor => { canonical_uri => $canonical_uri, path => $state->{traversed_schema_path}.$state->{schema_path}, }, }, }; } return 1; } # we already indexed the $anchor uri, so there is nothing more to do at evaluation time. # we explicitly do NOT set $state->{initial_schema_uri}. sub _traverse_keyword_recursiveAnchor ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'boolean'); # this is required because the location is used as the base URI for future resolution # of $recursiveRef, and the fragment would be disregarded in the base return E($state, '"$recursiveAnchor" keyword used without "$id"') if length($state->{schema_path}); return 1; } sub _eval_keyword_recursiveAnchor ($class, $data, $schema, $state) { return 1 if not $schema->{'$recursiveAnchor'} or exists $state->{recursive_anchor_uri}; # record the canonical location of the current position, to be used against future resolution # of a $recursiveRef uri -- as if it was the current location when we encounter a $ref. $state->{recursive_anchor_uri} = canonical_uri($state); return 1; } *_traverse_keyword_dynamicAnchor = \&_traverse_keyword_anchor; # we already indexed the $dynamicAnchor uri, so there is nothing more to do at evaluation time. # we explicitly do NOT set $state->{initial_schema_uri}. sub _traverse_keyword_ref ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string') or not assert_uri_reference($state, $schema); return 1; } sub _eval_keyword_ref ($class, $data, $schema, $state) { my $uri = Mojo::URL->new($schema->{'$ref'})->to_abs($state->{initial_schema_uri}); $class->eval_subschema_at_uri($data, $schema, $state, $uri); } *_traverse_keyword_recursiveRef = \&_traverse_keyword_ref; sub _eval_keyword_recursiveRef ($class, $data, $schema, $state) { my $uri = Mojo::URL->new($schema->{'$recursiveRef'})->to_abs($state->{initial_schema_uri}); my $schema_info = $state->{evaluator}->_fetch_from_uri($uri); abort($state, 'EXCEPTION: unable to find resource "%s"', $uri) if not $schema_info; abort($state, 'EXCEPTION: bad reference to "%s": not a schema', $schema_info->{canonical_uri}) if $schema_info->{document}->get_entity_at_location($schema_info->{document_path}) ne 'schema'; if (is_plain_hashref($schema_info->{schema}) and is_type('boolean', $schema_info->{schema}{'$recursiveAnchor'}) and $schema_info->{schema}{'$recursiveAnchor'}) { $uri = Mojo::URL->new($schema->{'$recursiveRef'}) ->to_abs($state->{recursive_anchor_uri} // $state->{initial_schema_uri}); } return $class->eval_subschema_at_uri($data, $schema, $state, $uri); } *_traverse_keyword_dynamicRef = \&_traverse_keyword_ref; sub _eval_keyword_dynamicRef ($class, $data, $schema, $state) { my $uri = Mojo::URL->new($schema->{'$dynamicRef'})->to_abs($state->{initial_schema_uri}); my $schema_info = $state->{evaluator}->_fetch_from_uri($uri); abort($state, 'EXCEPTION: unable to find resource "%s"', $uri) if not $schema_info; abort($state, 'EXCEPTION: bad reference to "%s": not a schema', $schema_info->{canonical_uri}) if $schema_info->{document}->get_entity_at_location($schema_info->{document_path}) ne 'schema'; # If the initially resolved starting point URI includes a fragment that was created by the # "$dynamicAnchor" keyword, ... if (length $uri->fragment and is_plain_hashref($schema_info->{schema}) and exists $schema_info->{schema}{'$dynamicAnchor'} and $uri->fragment eq (my $anchor = $schema_info->{schema}{'$dynamicAnchor'})) { # ...the initial URI MUST be replaced by the URI (including the fragment) for the outermost # schema resource in the dynamic scope that defines an identically named fragment with # "$dynamicAnchor". foreach my $base_scope ($state->{dynamic_scope}->@*) { my $test_uri = Mojo::URL->new($base_scope)->fragment($anchor); my $dynamic_anchor_subschema_info = $state->{evaluator}->_fetch_from_uri($test_uri); if (defined $dynamic_anchor_subschema_info and ($dynamic_anchor_subschema_info->{schema}{'$dynamicAnchor'}//'') eq $anchor) { $uri = $test_uri; last; } } } return $class->eval_subschema_at_uri($data, $schema, $state, $uri); } sub _traverse_keyword_vocabulary ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'object'); return E($state, '$vocabulary can only appear at the schema resource root') if length($state->{schema_path}); my $valid = 1; my @vocabulary_classes; foreach my $uri (sort keys $schema->{'$vocabulary'}->%*) { if (not is_type('boolean', $schema->{'$vocabulary'}{$uri})) { ()= E({ %$state, _schema_path_suffix => $uri }, '$vocabulary value at "%s" is not a boolean', $uri); $valid = 0; next; } $valid = 0 if not assert_uri({ %$state, _schema_path_suffix => $uri }, undef, $uri); } # we cannot return an error here for invalid or incomplete vocabulary lists, because # - the specification vocabulary schemas themselves don't list Core, # - it is possible for a metaschema to $ref to another metaschema that uses an unrecognized # vocabulary uri while still validating those vocabulary keywords (e.g. # https://spec.openapis.org/oas/3.1/schema-base/2021-05-20) # Instead, we will verify these constraints when we actually use the metaschema, in # _traverse_keyword_schema -> __fetch_vocabulary_data return $valid; } # we do nothing with $vocabulary yet at evaluation time. When we know we are in a metaschema, # we can scan the URIs included here and either abort if a vocabulary is enabled that we do not # understand, or turn on and off certain keyword behaviours based on the boolean values seen. sub _traverse_keyword_comment ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string'); return 1; } # we do nothing with $comment at evaluation time, including not collecting its value for annotations. sub _traverse_keyword_definitions { shift->traverse_object_schemas(@_) } sub _traverse_keyword_defs { shift->traverse_object_schemas(@_) } # we do nothing directly with $defs at evaluation time, including not collecting its value for # annotations. 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::Core - Implementation of the JSON Schema Core vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Core" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for =over 4 =item * the equivalent Draft 2019-09 keywords, indicated in metaschemas with the URI C and formally specified in L. =item * the equivalent Draft 7 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 6 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 4 keywords that correspond to this vocabulary and are formally specified in L. =back =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/Content.pm000640 000766 000024 00000013152 15001302761 025207 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::Content; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Content vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use Storable 'dclone'; use Feature::Compat::Try; use JSON::Schema::Modern::Utilities qw(is_type A assert_keyword_type E abort); use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2019-09/vocab/content' => 'draft2019-09', 'https://json-schema.org/draft/2020-12/vocab/content' => 'draft2020-12'; } sub evaluation_order ($class) { 4 } sub keywords ($class, $spec_version) { return ( $spec_version !~ /^draft[46]$/ ? qw(contentEncoding contentMediaType) : (), $spec_version !~ /^draft[467]$/ ? 'contentSchema' : (), ); } sub _traverse_keyword_contentEncoding ($class, $schema, $state) { return assert_keyword_type($state, $schema, 'string'); } sub _eval_keyword_contentEncoding ($class, $data, $schema, $state) { return 1 if not is_type('string', $data); if ($state->{validate_content_schemas}) { my $decoder = $state->{evaluator}->get_encoding($schema->{contentEncoding}); abort($state, 'cannot find decoder for contentEncoding "%s"', $schema->{contentEncoding}) if not $decoder; # decode the data now, so we can report errors for the right keyword try { $state->{_content_ref} = $decoder->(\$data); } catch ($e) { chomp $e; return E($state, 'could not decode %s string: %s', $schema->{contentEncoding}, $e); } } return A($state, $schema->{$state->{keyword}}); } *_traverse_keyword_contentMediaType = \&_traverse_keyword_contentEncoding; sub _eval_keyword_contentMediaType ($class, $data, $schema, $state) { return 1 if not is_type('string', $data); if ($state->{validate_content_schemas}) { my $decoder = $state->{evaluator}->get_media_type($schema->{contentMediaType}); abort($state, 'cannot find decoder for contentMediaType "%s"', $schema->{contentMediaType}) if not $decoder; # contentEncoding failed to decode the content return 1 if exists $schema->{contentEncoding} and not exists $state->{_content_ref}; # decode the data now, so we can report errors for the right keyword try { $state->{_content_ref} = $decoder->($state->{_content_ref} // \$data); } catch ($e) { chomp $e; delete $state->{_content_ref}; return E($state, 'could not decode %s string: %s', $schema->{contentMediaType}, $e); } } return A($state, $schema->{$state->{keyword}}); } sub _traverse_keyword_contentSchema ($class, $schema, $state) { $class->traverse_subschema($schema, $state); } sub _eval_keyword_contentSchema ($class, $data, $schema, $state) { return 1 if not exists $schema->{contentMediaType}; return 1 if not is_type('string', $data); if ($state->{validate_content_schemas}) { return 1 if not exists $state->{_content_ref}; # contentMediaType failed to decode the content return E($state, 'subschema is not valid') if not $class->eval($state->{_content_ref}->$*, $schema->{contentSchema}, { %$state, schema_path => $state->{schema_path}.'/contentSchema' }); } return A($state, dclone($schema->{contentSchema})); } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::Content - Implementation of the JSON Schema Content vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Content" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for =over 4 =item * the equivalent Draft 2019-09 keywords, indicated in metaschemas with the URI C and formally specified in L. =item * the equivalent Draft 7 keywords that correspond to this vocabulary and are formally specified in L. =back Assertion behaviour can be enabled by toggling the L option. New handlers for C and C can be done through L and L. =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/FormatAssertion.pm000640 000766 000024 00000031760 15001302761 026722 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::FormatAssertion; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Format-Assertion vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use JSON::Schema::Modern::Utilities qw(get_type E A assert_keyword_type abort); use Feature::Compat::Try; use List::Util 'any'; use Ref::Util 0.100 'is_plain_arrayref'; use Scalar::Util 'looks_like_number'; use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2020-12/vocab/format-assertion' => 'draft2020-12'; } sub evaluation_order ($class) { 2 } sub keywords ($class, $spec_version) { return ( $spec_version !~ /^draft(?:[467]|2019-09)$/ ? qw(format) : (), ); } # these definitions are shared with the FormatAnnotation vocabulary { # for now, all built-in formats are constrained to the 'string' type my $is_email = sub { # email, idn-email require Email::Address::XS; Email::Address::XS->VERSION(1.04); Email::Address::XS->parse_bare_address($_[0])->is_valid; }; my $is_hostname = sub { # hostname, idn-hostname # FIXME: draft7 hostname uses RFC1034, draft2019-09+ hostname uses RFC1123 require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); Data::Validate::Domain::is_domain($_[0], { domain_disable_tld_validation => 1, domain_allow_single_label => 1 }); }; my $idn_decode = sub { # idn-hostname require Net::IDN::Encode; try { return Net::IDN::Encode::domain_to_ascii($_[0]) } catch ($e) { return $_[0]; } }; my $is_ipv4 = sub { # ipv4, ipv6 my @o = split(/\./, $_[0], 5); @o == 4 && (grep /^(?:0|[1-9][0-9]{0,2})$/, @o) == 4 && (grep $_ < 256, @o) == 4; }; # https://datatracker.ietf.org/doc/html/rfc3339#appendix-A with some additions for the 2000 version # as defined in https://en.wikipedia.org/wiki/ISO_8601#Durations my $duration_re = do { # duration my $num = qr{[0-9]+(?:[.,][0-9]+)?}; my $second = qr{${num}S}; my $minute = qr{${num}M}; my $hour = qr{${num}H}; my $time = qr{T(?=[0-9])(?:$hour)?(?:$minute)?(?:$second)?}; my $day = qr{${num}D}; my $month = qr{${num}M}; my $year = qr{${num}Y}; my $week = qr{${num}W}; my $date = qr{(?=[0-9])(?:$year)?(?:$month)?(?:$day)?}; qr{^P(?:(?=.)(?:$date)?(?:$time)?|$week)$}; }; my $formats = +{ 'date-time' => sub { # https://www.rfc-editor.org/rfc/rfc3339.html#section-5.6 $_[0] =~ m/^\d{4}-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(?:\.\d+)?(?:Z|[+-](\d\d):(\d\d))$/ia && $1 >= 1 && $1 <= 12 # date-month && $2 >= 1 && $2 <= 31 # date-mday && $3 <= 23 # time-hour && $4 <= 59 # time-minute && $5 <= 60 # time-second && (!defined $6 || $6 <= 23) # time-hour in time-numoffset && (!defined $7 || $7 <= 59) # time-minute in time-numoffset # Time::Moment does month+day sanity check (with leap years), but not leap seconds && ($5 <= 59 && do { require Time::Moment; eval { Time::Moment->from_string(uc($_[0])) }; } || do { require DateTime::Format::RFC3339; eval { DateTime::Format::RFC3339->parse_datetime($_[0]) }; }); }, date => sub { # https://www.rfc-editor.org/rfc/rfc3339.html#section-5.6 full-date $_[0] =~ m/^(\d{4})-(\d\d)-(\d\d)$/a && $2 >= 1 && $2 <= 12 # date-month && $3 >= 1 && $3 <= 31 # date-mday && do { require Time::Moment; eval { Time::Moment->new(year => $1, month => $2, day => $3) }; }; }, time => sub { return if $_[0] !~ /^(\d\d):(\d\d):(\d\d)(?:\.\d+)?([Zz]|([+-])(\d\d):(\d\d))$/a or $1 > 23 or $2 > 59 or $3 > 60 or (defined($6) and $6 > 23) or (defined($7) and $7 > 59); return 1 if $3 <= 59; return $1 == 23 && $2 == 59 if uc($4) eq 'Z'; my $sign = $5 eq '+' ? 1 : -1; my $hour_zulu = $1 - $6*$sign; my $min_zulu = $2 - $7*$sign; $hour_zulu -= 1 if $min_zulu < 0; return $hour_zulu%24 == 23 && $min_zulu%60 == 59; }, duration => sub { $_[0] =~ $duration_re && $_[0] !~ m{[.,][0-9]+[A-Z].} }, email => sub { $is_email->($_[0]) && $_[0] !~ /[^[:ascii:]]/ }, 'idn-email' => $is_email, hostname => $is_hostname, 'idn-hostname' => sub { $is_hostname->($idn_decode->($_[0])) }, ipv4 => $is_ipv4, ipv6 => sub { ($_[0] =~ /^(?:[[:xdigit:]]{0,4}:){0,8}[[:xdigit:]]{0,4}$/ || $_[0] =~ /^(?:[[:xdigit:]]{0,4}:){1,6}((?:[0-9]{1,3}\.){3}[0-9]{1,3})$/ && $is_ipv4->($1)) && $_[0] !~ /:::/ && $_[0] !~ /^:[^:]/ && $_[0] !~ /[^:]:$/ && do { my $double_colons = ()= ($_[0] =~ /::/g); my $colon_components = grep length, split(/:+/, $_[0], -1); ($double_colons == 1 && ((!defined $1 && $colon_components < 8) || (defined $1 && $colon_components < 7))) || ($double_colons == 0 && ((!defined $1 && $colon_components == 8) || (defined $1 && $colon_components == 7))); }; }, uri => sub { my $uri = Mojo::URL->new($_[0]); fc($uri->to_unsafe_string) eq fc($_[0]) && $uri->is_abs && $_[0] !~ /[^[:ascii:]]/; }, 'uri-reference' => sub { fc(Mojo::URL->new($_[0])->to_unsafe_string) eq fc($_[0]) && $_[0] !~ /[^[:ascii:]]/; }, iri => sub { Mojo::URL->new($_[0])->is_abs }, uuid => sub { $_[0] =~ /^[[:xdigit:]]{8}-(?:[[:xdigit:]]{4}-){3}[[:xdigit:]]{12}$/ }, 'json-pointer' => sub { (!length($_[0]) || $_[0] =~ m{^/}) && $_[0] !~ m{~(?![01])} }, 'relative-json-pointer' => sub { $_[0] =~ m{^(?:0|[1-9][0-9]*)(?:#$|$|/)} && $_[0] !~ m{~(?![01])} }, regex => sub { local $SIG{__WARN__} = sub { die @_ }; eval { qr/$_[0]/; 1 }; }, 'iri-reference' => sub { 1 }, # uri-template is not implemented, but user can add a custom definition }; my %formats_by_spec_version = ( draft4 => [qw( date-time email hostname ipv4 ipv6 uri )], ); $formats_by_spec_version{draft6} = [$formats_by_spec_version{draft4}->@*, qw( uri-reference uri-template json-pointer )]; $formats_by_spec_version{draft7} = [$formats_by_spec_version{draft6}->@*, qw( iri iri-reference idn-email idn-hostname relative-json-pointer regex date time )]; $formats_by_spec_version{'draft2019-09'} = $formats_by_spec_version{'draft2020-12'} = [$formats_by_spec_version{draft7}->@*, qw(duration uuid)]; sub _get_default_format_validation ($class, $state, $format) { # all core formats are of type string (so far) return { type => 'string', sub => $formats->{$format} } if grep $format eq $_, $formats_by_spec_version{$state->{spec_version}}->@* and $formats->{$format}; } } my $warnings = { email => sub { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 }, hostname => sub { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 }, 'idn-hostname' => sub { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); require Net::IDN::Encode; 1 }, 'date-time' => sub { require Time::Moment; require DateTime::Format::RFC3339; 1 }, date => sub { require Time::Moment; 1 }, }; $warnings->{'idn-email'} = $warnings->{email}; sub _traverse_keyword_format ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string'); # warn when prereq is missing for a format implementation if (my $warn_sub = $warnings->{$schema->{format}}) { try { $warn_sub->() } catch ($e) { warn $e } } # §7.2.2 (draft2020-12) "When the Format-Assertion vocabulary is declared with a value of true, # implementations MUST provide full validation support for all of the formats defined by this # specification. Implementations that cannot provide full validation support MUST refuse to # process the schema." return E($state, 'unimplemented core format "%s"', $schema->{format}) if $schema->{format} eq 'uri-template' and not $state->{evaluator}->_get_format_validation($schema->{format}); # unimplemented custom formats are detected at runtime, only if actually evaluated return 1; } # Note that this method is only callable in draft2020-12 and later, because this vocabulary does not # exist in previous versions sub _eval_keyword_format ($class, $data, $schema, $state) { A($state, $schema->{format}); # unimplemented core formats were already detected in the traverse phase my $spec = $state->{evaluator}->_get_format_validation($schema->{format}) // $class->_get_default_format_validation($state, $schema->{format}); # §7.2.3 (draft2020-12) "When the Format-Assertion vocabulary is specified, implementations MUST # fail upon encountering unknown formats." abort($state, 'unimplemented custom format "%s"', $schema->{format}) if not $spec; my $type = get_type($data); $type = 'number' if $type eq 'integer'; return 1 if not is_plain_arrayref($spec->{type}) ? any { $type eq $_ } $spec->{type}->@* : $type eq $spec->{type} and not ($state->{stringy_numbers} and $type eq 'string' and is_plain_arrayref($spec->{type}) ? any { $_ eq 'number' } $spec->{type}->@* : $spec->{type} eq 'number' and looks_like_number($data)); return E($state, 'not a valid %s', $schema->{format}) if not $spec->{sub}->($data); return 1; } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::FormatAssertion - Implementation of the JSON Schema Format-Assertion vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Format-Assertion" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for =over 4 =item * the equivalent Draft 2019-09 keyword, indicated in metaschemas with the URI C and formally specified in L. =item * the equivalent Draft 7 keyword, as formally specified in L. =item * the equivalent Draft 6 keyword, as formally specified in L. =item * the equivalent Draft 4 keyword, as formally specified in L. =back Assertion behaviour can be enabled by L in a metaschema's C<$vocabulary> keyword, or by toggling the L option. When the Format-Annotation vocabulary is specified (which is the default for the draft2020-12 metaschema) and combined with the C option set to true, unimplemented formats will silently validate, but implemented formats will validate completely. Note that some formats require optional module dependencies, and the lack of these modules will generate an error. When this vocabulary (the Format-Assertion vocabulary) is specified, unimplemented formats will generate an error on use. Overrides to particular format implementations, or additions of new ones, can be done through L. Format C is not yet implemented. Use of this format will always result in an error. =head1 SEE ALSO =over 4 =item * L =item * L =back =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/Unevaluated.pm000640 000766 000024 00000015607 15001302761 026061 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::Unevaluated; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Unevaluated vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use List::Util 1.45 qw(any max); use JSON::Schema::Modern::Utilities qw(is_type jsonp local_annotations E A abort true); use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2020-12/vocab/unevaluated' => 'draft2020-12'; } sub evaluation_order ($class) { 7 } # This vocabulary should be evaluated after the Applicator vocabulary. sub keywords ($class, $spec_version) { die 'Unevaluated not implemented in '.$spec_version if $spec_version =~ /^draft(?:[467]|2019-09)$/; qw(unevaluatedItems unevaluatedProperties); } sub _traverse_keyword_unevaluatedItems ($class, $schema, $state) { $class->traverse_subschema($schema, $state); } sub _eval_keyword_unevaluatedItems ($class, $data, $schema, $state) { # these should never happen die '"unevaluatedItems" keyword present, but annotation collection is disabled' if not $state->{collect_annotations}; die '"unevaluatedItems" keyword present, but short_circuit is enabled: results unreliable' if $state->{short_circuit}; return 1 if not is_type('array', $data); my @annotations = local_annotations($state); # a relevant keyword already produced a 'true' annotation at this location my @boolean_annotation_keywords = $state->{spec_version} eq 'draft2019-09' ? qw(items additionalItems unevaluatedItems) : qw(prefixItems items contains unevaluatedItems); my %bools; @bools{@boolean_annotation_keywords} = (1)x@boolean_annotation_keywords; return 1 if any { $bools{$_->{keyword}} && is_type('boolean', $_->{annotation}) && $_->{annotation} } @annotations; # otherwise, evaluate at every instance item greater than the max of all 'prefixItems'/numeric # 'items' annotations that isn't in a 'contains' annotation my $max_index_annotation_keyword = $state->{spec_version} eq 'draft2019-09' ? 'items' : 'prefixItems'; my $last_index = max(-1, grep is_type('integer', $_), map +($_->{keyword} eq $max_index_annotation_keyword ? $_->{annotation} : ()), @annotations); return 1 if $last_index == $data->$#*; my @contains_annotation_indexes = $state->{spec_version} eq 'draft2019-09' ? () : map +($_->{keyword} eq 'contains' ? $_->{annotation}->@* : ()), @annotations; my $valid = 1; foreach my $idx ($last_index+1 .. $data->$#*) { next if any { $idx == $_ } @contains_annotation_indexes; if (is_type('boolean', $schema->{unevaluatedItems})) { next if $schema->{unevaluatedItems}; $valid = E({ %$state, data_path => $state->{data_path}.'/'.$idx }, 'additional item not permitted') } else { if ($class->eval($data->[$idx], $schema->{unevaluatedItems}, +{ %$state, data_path => $state->{data_path}.'/'.$idx, schema_path => $state->{schema_path}.'/unevaluatedItems', collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; } last if $state->{short_circuit}; } A($state, true); return E($state, 'subschema is not valid against all additional items') if not $valid; return 1; } sub _traverse_keyword_unevaluatedProperties ($class, $schema, $state) { $class->traverse_subschema($schema, $state); } sub _eval_keyword_unevaluatedProperties ($class, $data, $schema, $state) { # these should never happen die '"unevaluatedProperties" keyword present, but annotation collection is disabled' if not $state->{collect_annotations}; die '"unevaluatedProperties" keyword present, but short_circuit is enabled: results unreliable' if $state->{short_circuit}; return 1 if not is_type('object', $data); my @evaluated_properties = map { my $keyword = $_->{keyword}; (grep $keyword eq $_, qw(properties additionalProperties patternProperties unevaluatedProperties)) ? $_->{annotation}->@* : (); } local_annotations($state); my $valid = 1; my @properties; foreach my $property (sort keys %$data) { next if any { $_ eq $property } @evaluated_properties; push @properties, $property; if (is_type('boolean', $schema->{unevaluatedProperties})) { next if $schema->{unevaluatedProperties}; $valid = E({ %$state, data_path => jsonp($state->{data_path}, $property) }, 'additional property not permitted'); } else { if ($class->eval($data->{$property}, $schema->{unevaluatedProperties}, +{ %$state, data_path => jsonp($state->{data_path}, $property), schema_path => $state->{schema_path}.'/unevaluatedProperties', collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; } last if $state->{short_circuit}; } A($state, \@properties); return E($state, 'not all additional properties are valid') if not $valid; return 1; } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::Unevaluated - Implementation of the JSON Schema Unevaluated vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Unevaluated" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for the equivalent Draft 2019-09 keywords in the JSON Schema Draft 2019-09 "Applicator" vocabulary, indicated in metaschemas with the URI C and formally specified in L. =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/FormatAnnotation.pm000640 000766 000024 00000013750 15001302761 027064 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::FormatAnnotation; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Format-Annotation vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use Feature::Compat::Try; use JSON::Schema::Modern::Utilities qw(A E assert_keyword_type get_type); use JSON::Schema::Modern::Vocabulary::FormatAssertion; use List::Util 'any'; use Ref::Util 0.100 'is_plain_arrayref'; use Scalar::Util 'looks_like_number'; use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2019-09/vocab/format' => 'draft2019-09', 'https://json-schema.org/draft/2020-12/vocab/format-annotation' => 'draft2020-12'; } sub evaluation_order ($class) { 2 } sub keywords ($class, $spec_version) { qw(format); } sub _traverse_keyword_format ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string'); return 1; } sub _eval_keyword_format ($class, $data, $schema, $state) { A($state, $schema->{format}); return 1 if not $state->{validate_formats}; # { type => .., sub => .. } my $spec = $state->{evaluator}->_get_format_validation($schema->{format}) // JSON::Schema::Modern::Vocabulary::FormatAssertion->_get_default_format_validation($state, $schema->{format}); # §7.2.1 (draft2020-12) "Specifying the Format-Annotation vocabulary and enabling validation in an # implementation should not be viewed as being equivalent to specifying the Format-Assertion # vocabulary since implementations are not required to provide full validation support when the # Format-Assertion vocabulary is not specified." # §7.2.3 (draft2019-09) "An implementation MUST NOT fail validation or cease processing due to an # unknown format attribute." return 1 if not $spec; my $type = get_type($data); $type = 'number' if $type eq 'integer'; return 1 if not is_plain_arrayref($spec->{type}) ? any { $type eq $_ } $spec->{type}->@* : $type eq $spec->{type} and not ($state->{stringy_numbers} and $type eq 'string' and is_plain_arrayref($spec->{type}) ? any { $_ eq 'number' } $spec->{type}->@* : $spec->{type} eq 'number' and looks_like_number($data)); my $valid; try { $valid = $spec->{sub}->($data); } catch ($e) { # we treat a missing optional prereq as if the format is not implemented, therefore it is valid $valid = 1; } return E($state, 'not a valid %s %s', $schema->{format}, is_plain_arrayref($spec->{type}) ? join(', ', $spec->{type}->@*) : $spec->{type}) if not $valid; return 1; } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::FormatAnnotation - Implementation of the JSON Schema Format-Annotation vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Format-Annotation" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for =over 4 =item * the equivalent Draft 2019-09 keyword, indicated in metaschemas with the URI C and formally specified in L. =item * the equivalent Draft 7 keyword, as formally specified in L. =item * the equivalent Draft 6 keyword, as formally specified in L. =item * the equivalent Draft 4 keyword, as formally specified in L. =back It also implements format assertion behaviour in a relaxed mode, meaning the L option has been enabled, and unknown formats will not generate errors; this differs from the more strict behaviour in LJSON::Schema::Modern::Vocabulary::FormatAssertion> which requires all formats used in the schema to be supported and defined. When this vocabulary (the Format-Annotation vocabulary) is specified (which is the default for the draft2020-12 metaschema) and combined with the C option set to true, unimplemented formats will silently validate, but implemented formats will validate completely. Note that some formats require optional module dependencies, and the lack of these modules will generate an error. When the Format-Assertion vocabulary is specified, unimplemented formats will generate an error on use. =head1 SEE ALSO =over 4 =item * L =item * L =back =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/Validation.pm000640 000766 000024 00000040132 15001302761 025665 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::Validation; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Validation vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use List::Util 'any'; use Ref::Util 0.100 'is_plain_arrayref'; use Scalar::Util 'looks_like_number'; use JSON::Schema::Modern::Utilities qw(is_type get_type is_bignum is_equal is_elements_unique E assert_keyword_type assert_pattern jsonp sprintf_num); use Math::BigFloat; use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2019-09/vocab/validation' => 'draft2019-09', 'https://json-schema.org/draft/2020-12/vocab/validation' => 'draft2020-12'; } sub evaluation_order ($class) { 1 } sub keywords ($class, $spec_version) { return ( qw(type enum), $spec_version ne 'draft4' ? 'const' : (), qw(multipleOf maximum exclusiveMaximum minimum exclusiveMinimum maxLength minLength pattern maxItems minItems uniqueItems), $spec_version !~ /^draft[467]$/ ? qw(maxContains minContains) : (), qw(maxProperties minProperties required), $spec_version !~ /^draft[467]$/ ? 'dependentRequired' : (), ); } sub _traverse_keyword_type ($class, $schema, $state) { if (is_plain_arrayref($schema->{type})) { # Note: this is not actually in the spec, but the restriction exists in the metaschema return E($state, 'type array is empty') if not $schema->{type}->@*; foreach my $type ($schema->{type}->@*) { return E($state, 'unrecognized type "%s"', $type//'') if not any { ($type//'') eq $_ } qw(null boolean object array string number integer); } return E($state, '"type" values are not unique') if not is_elements_unique($schema->{type}); } else { return if not assert_keyword_type($state, $schema, 'string'); return E($state, 'unrecognized type "%s"', $schema->{type}//'') if not any { ($schema->{type}//'') eq $_ } qw(null boolean object array string number integer); } return 1; } sub _eval_keyword_type ($class, $data, $schema, $state) { my $type = get_type($data, $state->{spec_version} eq 'draft4' ? { legacy_ints => 1 } : ()); if (is_plain_arrayref($schema->{type})) { return 1 if any { $type eq $_ or ($_ eq 'number' and $type eq 'integer') or ($type eq 'string' and $state->{stringy_numbers} and looks_like_number($data) and ($_ eq 'number' or ($_ eq 'integer' and $data == int($data)))) or ($_ eq 'boolean' and $state->{scalarref_booleans} and $type eq 'reference to SCALAR') } $schema->{type}->@*; return E($state, 'got %s, not one of %s', $type, join(', ', $schema->{type}->@*)); } else { return 1 if $type eq $schema->{type} or ($schema->{type} eq 'number' and $type eq 'integer') or ($type eq 'string' and $state->{stringy_numbers} and looks_like_number($data) and ($schema->{type} eq 'number' or ($schema->{type} eq 'integer' and $data == int($data)))) or ($schema->{type} eq 'boolean' and $state->{scalarref_booleans} and $type eq 'reference to SCALAR'); return E($state, 'got %s, not %s', $type, $schema->{type}); } } sub _traverse_keyword_enum ($class, $schema, $state) { return assert_keyword_type($state, $schema, 'array'); } sub _eval_keyword_enum ($class, $data, $schema, $state) { my @s; my $idx = 0; my %s = $state->%{qw(scalarref_booleans stringy_numbers)}; return 1 if any { is_equal($data, $_, $s[$idx++] = {%s}) } $schema->{enum}->@*; return E($state, 'value does not match' .(!(grep $_->{path}, @s) ? '' : ' ('.join('; ', map "from enum $_ at '$s[$_]->{path}': $s[$_]->{error}", 0..$#s).')')); } sub _traverse_keyword_const ($class, $schema, $state) { 1 } sub _eval_keyword_const ($class, $data, $schema, $state) { my %s = $state->%{qw(scalarref_booleans stringy_numbers)}; return 1 if is_equal($data, $schema->{const}, \%s); return E($state, 'value does not match'.($s{path} ? " (at '$s{path}': $s{error})" : '')); } sub _traverse_keyword_multipleOf ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'number'); return E($state, 'multipleOf value is not a positive number') if $schema->{multipleOf} <= 0; return 1; } sub _eval_keyword_multipleOf ($class, $data, $schema, $state) { return 1 if not is_type('number', $data) and not ($state->{stringy_numbers} and is_type('string', $data) and looks_like_number($data) and do { $data = 0+$data; 1 }); my $remainder; # if either value is a float, use the bignum library for the calculation for an accurate remainder if (is_bignum($data) or is_bignum($schema->{multipleOf}) or get_type($data) eq 'number' or get_type($schema->{multipleOf}) eq 'number') { my $dividend = is_bignum($data) ? $data->copy : Math::BigFloat->new($data); my $divisor = is_bignum($schema->{multipleOf}) ? $schema->{multipleOf} : Math::BigFloat->new($schema->{multipleOf}); $remainder = $dividend->bmod($divisor); } else { $remainder = $data % $schema->{multipleOf}; } return 1 if $remainder == 0; return E($state, 'value is not a multiple of %s', sprintf_num($schema->{multipleOf})); } *_traverse_keyword_maximum = \&_assert_number; sub _eval_keyword_maximum ($class, $data, $schema, $state) { return 1 if not is_type('number', $data) and not ($state->{stringy_numbers} and is_type('string', $data) and looks_like_number($data)); return 1 if 0+$data < $schema->{maximum}; if ($state->{spec_version} eq 'draft4' and $schema->{exclusiveMaximum}) { return E($state, 'value is greater than or equal to %s', sprintf_num($schema->{maximum})); } else { return 1 if 0+$data == $schema->{maximum}; return E($state, 'value is greater than %s', sprintf_num($schema->{maximum})); } } sub _traverse_keyword_exclusiveMaximum ($class, $schema, $state) { return _assert_number($class, $schema, $state) if $state->{spec_version} ne 'draft4'; return if not assert_keyword_type($state, $schema, 'boolean'); return E($state, 'use of exclusiveMaximum requires the presence of maximum') if not exists $schema->{maximum}; return 1; } sub _eval_keyword_exclusiveMaximum ($class, $data, $schema, $state) { # we do the work in maximum for draft4 so we don't generate multiple errors return 1 if $state->{spec_version} eq 'draft4'; return 1 if not is_type('number', $data) and not ($state->{stringy_numbers} and is_type('string', $data) and looks_like_number($data)); return 1 if 0+$data < $schema->{exclusiveMaximum}; return E($state, 'value is greater than or equal to %s', sprintf_num($schema->{exclusiveMaximum})); } *_traverse_keyword_minimum = \&_assert_number; sub _eval_keyword_minimum ($class, $data, $schema, $state) { return 1 if not is_type('number', $data) and not ($state->{stringy_numbers} and is_type('string', $data) and looks_like_number($data)); return 1 if 0+$data > $schema->{minimum}; if ($state->{spec_version} eq 'draft4' and $schema->{exclusiveMinimum}) { return E($state, 'value is less than or equal to %s', sprintf_num($schema->{minimum})); } else { return 1 if 0+$data == $schema->{minimum}; return E($state, 'value is less than %s', sprintf_num($schema->{minimum})); } } sub _traverse_keyword_exclusiveMinimum ($class, $schema, $state) { return _assert_number($class, $schema, $state) if $state->{spec_version} ne 'draft4'; return if not assert_keyword_type($state, $schema, 'boolean'); return E($state, 'use of exclusiveMinimum requires the presence of minimum') if not exists $schema->{minimum}; return 1; } sub _eval_keyword_exclusiveMinimum ($class, $data, $schema, $state) { # we do the work in minimum for draft4 so we don't generate multiple errors return 1 if $state->{spec_version} eq 'draft4'; return 1 if not is_type('number', $data) and not ($state->{stringy_numbers} and is_type('string', $data) and looks_like_number($data)); return 1 if 0+$data > $schema->{exclusiveMinimum}; return E($state, 'value is less than or equal to %s', sprintf_num($schema->{exclusiveMinimum})); } *_traverse_keyword_maxLength = \&_assert_non_negative_integer; sub _eval_keyword_maxLength ($class, $data, $schema, $state) { return 1 if not is_type('string', $data); return 1 if length($data) <= $schema->{maxLength}; return E($state, 'length is greater than %d', $schema->{maxLength}); } *_traverse_keyword_minLength = \&_assert_non_negative_integer; sub _eval_keyword_minLength ($class, $data, $schema, $state) { return 1 if not is_type('string', $data); return 1 if length($data) >= $schema->{minLength}; return E($state, 'length is less than %d', $schema->{minLength}); } sub _traverse_keyword_pattern ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string') or not assert_pattern($state, $schema->{pattern}); return 1; } sub _eval_keyword_pattern ($class, $data, $schema, $state) { return 1 if not is_type('string', $data); return 1 if $data =~ m/(?:$schema->{pattern})/; return E($state, 'pattern does not match'); } *_traverse_keyword_maxItems = \&_assert_non_negative_integer; sub _eval_keyword_maxItems ($class, $data, $schema, $state) { return 1 if not is_type('array', $data); return 1 if @$data <= $schema->{maxItems}; return E($state, 'array has more than %d item%s', $schema->{maxItems}, $schema->{maxItems} > 1 ? 's' : ''); } *_traverse_keyword_minItems = \&_assert_non_negative_integer; sub _eval_keyword_minItems ($class, $data, $schema, $state) { return 1 if not is_type('array', $data); return 1 if @$data >= $schema->{minItems}; return E($state, 'array has fewer than %d item%s', $schema->{minItems}, $schema->{minItems} > 1 ? 's' : ''); } sub _traverse_keyword_uniqueItems ($class, $schema, $state) { return assert_keyword_type($state, $schema, 'boolean'); } sub _eval_keyword_uniqueItems ($class, $data, $schema, $state) { return 1 if not is_type('array', $data); return 1 if not $schema->{uniqueItems}; return 1 if is_elements_unique($data, my $equal_indices = [], $state); return E($state, 'items at indices %d and %d are not unique', @$equal_indices); } # Note: no effort is made to check if the 'contains' keyword has been disabled via its vocabulary. # The evaluation implementation of maxContains and minContains are in the Applicator vocabulary *_traverse_keyword_maxContains = \&_assert_non_negative_integer; *_traverse_keyword_minContains = \&_assert_non_negative_integer; *_traverse_keyword_maxProperties = \&_assert_non_negative_integer; sub _eval_keyword_maxProperties ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); return 1 if keys %$data <= $schema->{maxProperties}; return E($state, 'object has more than %d propert%s', $schema->{maxProperties}, $schema->{maxProperties} > 1 ? 'ies' : 'y'); } *_traverse_keyword_minProperties = \&_assert_non_negative_integer; sub _eval_keyword_minProperties ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); return 1 if keys %$data >= $schema->{minProperties}; return E($state, 'object has fewer than %d propert%s', $schema->{minProperties}, $schema->{minProperties} > 1 ? 'ies' : 'y'); } sub _traverse_keyword_required ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'array'); return E($state, '"required" array is empty') if $state->{spec_version} eq 'draft4' and not $schema->{required}->@*; return E($state, '"required" element is not a string') if any { !is_type('string', $_) } $schema->{required}->@*; return E($state, '"required" values are not unique') if not is_elements_unique($schema->{required}); return 1; } sub _eval_keyword_required ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my @missing = grep !exists $data->{$_}, $schema->{required}->@*; return 1 if not @missing; return E($state, 'object is missing propert%s: %s', @missing > 1 ? 'ies' : 'y', join(', ', @missing)); } sub _traverse_keyword_dependentRequired ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'object'); my $valid = 1; foreach my $property (sort keys $schema->{dependentRequired}->%*) { $valid = E({ %$state, _schema_path_suffix => $property }, 'value is not an array'), next if not is_type('array', $schema->{dependentRequired}{$property}); foreach my $index (0..$schema->{dependentRequired}{$property}->$#*) { $valid = E({ %$state, _schema_path_suffix => [ $property, $index ] }, 'element #%d is not a string', $index) if not is_type('string', $schema->{dependentRequired}{$property}[$index]); } $valid = E({ %$state, _schema_path_suffix => $property }, 'elements are not unique') if not is_elements_unique($schema->{dependentRequired}{$property}); } return $valid; } sub _eval_keyword_dependentRequired ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my $valid = 1; foreach my $property (sort keys $schema->{dependentRequired}->%*) { next if not exists $data->{$property}; if (my @missing = grep !exists($data->{$_}), $schema->{dependentRequired}{$property}->@*) { $valid = E({ %$state, _schema_path_suffix => $property }, 'object is missing propert%s: %s', @missing > 1 ? 'ies' : 'y', join(', ', @missing)); } } return 1 if $valid; return E($state, 'not all dependencies are satisfied'); } sub _assert_number ($class, $schema, $state) { return assert_keyword_type($state, $schema, 'number'); } sub _assert_non_negative_integer ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'integer'); return E($state, '%s value is not a non-negative integer', $state->{keyword}) if $schema->{$state->{keyword}} < 0; return 1; } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::Validation - Implementation of the JSON Schema Validation vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Validation" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for =over 4 =item * the equivalent Draft 2019-09 keywords, indicated in metaschemas with the URI C and formally specified in L. =item * the equivalent Draft 7 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 6 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 4 keywords that correspond to this vocabulary and are formally specified in L. =back =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/lib/JSON/Schema/Modern/Vocabulary/Applicator.pm000640 000766 000024 00000053370 15001302761 025701 0ustar00etherstaff000000 000000 use strict; use warnings; package JSON::Schema::Modern::Vocabulary::Applicator; # vim: set ts=8 sts=2 sw=2 tw=100 et : # ABSTRACT: Implementation of the JSON Schema Applicator vocabulary our $VERSION = '0.609'; use 5.020; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 0.026 qw(signatures args_array_with_signatures); no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use List::Util 1.45 qw(any uniqstr); use Ref::Util 0.100 'is_plain_arrayref'; use Sub::Install; use JSON::Schema::Modern::Utilities qw(is_type jsonp E A assert_keyword_type assert_pattern true is_elements_unique); use JSON::Schema::Modern::Vocabulary::Unevaluated; use namespace::clean; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary ($class) { 'https://json-schema.org/draft/2019-09/vocab/applicator' => 'draft2019-09', 'https://json-schema.org/draft/2020-12/vocab/applicator' => 'draft2020-12'; } sub evaluation_order ($class) { 3 } # the keyword order is arbitrary, except: # - if must be evaluated before then, else # - items must be evaluated before additionalItems # - in-place applicators (allOf, anyOf, oneOf, not, if/then/else, dependentSchemas) and items, # additionalItems must be evaluated before unevaluatedItems (in the Unevaluated vocabulary) # - properties and patternProperties must be evaluated before additionalProperties # - in-place applicators and properties, patternProperties, additionalProperties must be evaluated # before unevaluatedProperties (in the Unevaluated vocabulary) # - contains must be evaluated before maxContains, minContains (implemented here, rather than in the Validation vocabulary) sub keywords ($class, $spec_version) { return ( qw(allOf anyOf oneOf not), $spec_version !~ /^draft[46]$/ ? qw(if then else) : (), $spec_version =~ /^draft[467]$/ ? 'dependencies' : (), $spec_version !~ /^draft[467]$/ ? 'dependentSchemas' : (), $spec_version !~ /^draft(?:[467]|2019-09)$/ ? 'prefixItems' : (), 'items', $spec_version =~ /^draft(?:[467]|2019-09)$/ ? 'additionalItems' : (), $spec_version ne 'draft4' ? 'contains' : (), $spec_version !~ /^draft[467]$/ ? qw(maxContains minContains) : (), qw(properties patternProperties additionalProperties), $spec_version ne 'draft4' ? 'propertyNames' : (), $spec_version eq 'draft2019-09' ? qw(unevaluatedItems unevaluatedProperties) : (), ); } # in draft2019-09, the unevaluated keywords were part of the Applicator vocabulary foreach my $phase (qw(traverse eval)) { foreach my $type (qw(Items Properties)) { my $method = '_'.$phase.'_keyword_unevaluated'.$type; Sub::Install::install_sub({ as => $method, code => sub { shift; JSON::Schema::Modern::Vocabulary::Unevaluated->$method(@_); } }), } } sub _traverse_keyword_allOf { shift->traverse_array_schemas(@_) } sub _eval_keyword_allOf ($class, $data, $schema, $state) { my @invalid; foreach my $idx (0 .. $schema->{allOf}->$#*) { if ($class->eval($data, $schema->{allOf}[$idx], +{ %$state, schema_path => $state->{schema_path}.'/allOf/'.$idx })) { } else { push @invalid, $idx; last if $state->{short_circuit}; } } return 1 if @invalid == 0; my $pl = @invalid > 1; return E($state, 'subschema%s %s %s not valid', $pl?'s':'', join(', ', @invalid), $pl?'are':'is'); } sub _traverse_keyword_anyOf { shift->traverse_array_schemas(@_) } sub _eval_keyword_anyOf ($class, $data, $schema, $state) { my $valid = 0; my @errors; foreach my $idx (0 .. $schema->{anyOf}->$#*) { next if not $class->eval($data, $schema->{anyOf}[$idx], +{ %$state, errors => \@errors, schema_path => $state->{schema_path}.'/anyOf/'.$idx }); ++$valid; last if $state->{short_circuit}; } return 1 if $valid; push $state->{errors}->@*, @errors; return E($state, 'no subschemas are valid'); } sub _traverse_keyword_oneOf { shift->traverse_array_schemas(@_) } sub _eval_keyword_oneOf ($class, $data, $schema, $state) { my (@valid, @errors); foreach my $idx (0 .. $schema->{oneOf}->$#*) { next if not $class->eval($data, $schema->{oneOf}[$idx], +{ %$state, errors => \@errors, schema_path => $state->{schema_path}.'/oneOf/'.$idx }); push @valid, $idx; last if @valid > 1 and $state->{short_circuit}; } return 1 if @valid == 1; if (not @valid) { push $state->{errors}->@*, @errors; return E($state, 'no subschemas are valid'); } else { return E($state, 'multiple subschemas are valid: '.join(', ', @valid)); } } sub _traverse_keyword_not { shift->traverse_subschema(@_) } sub _eval_keyword_not ($class, $data, $schema, $state) { return !$schema->{not} || E($state, 'subschema is true') if is_type('boolean', $schema->{not}); return 1 if not $class->eval($data, $schema->{not}, +{ %$state, schema_path => $state->{schema_path}.'/not', short_circuit_suggested => 1, # errors do not propagate upward from this subschema collect_annotations => 0, # nor do annotations errors => [] }); return E($state, 'subschema is valid'); } sub _traverse_keyword_if { shift->traverse_subschema(@_) } sub _traverse_keyword_then { shift->traverse_subschema(@_) } sub _traverse_keyword_else { shift->traverse_subschema(@_) } sub _eval_keyword_if ($class, $data, $schema, $state) { return 1 if not exists $schema->{then} and not exists $schema->{else} and not $state->{collect_annotations}; my $keyword = $class->eval($data, $schema->{if}, +{ %$state, schema_path => $state->{schema_path}.'/if', short_circuit_suggested => !$state->{collect_annotations}, errors => [], }) ? 'then' : 'else'; return 1 if not exists $schema->{$keyword}; return $schema->{$keyword} || E({ %$state, keyword => $keyword }, 'subschema is false') if is_type('boolean', $schema->{$keyword}); return 1 if $class->eval($data, $schema->{$keyword}, +{ %$state, schema_path => $state->{schema_path}.'/'.$keyword }); return E({ %$state, keyword => $keyword }, 'subschema is not valid'); } sub _traverse_keyword_dependentSchemas { shift->traverse_object_schemas(@_) } sub _eval_keyword_dependentSchemas ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my $valid = 1; foreach my $property (sort keys $schema->{dependentSchemas}->%*) { next if not exists $data->{$property}; if ($class->eval($data, $schema->{dependentSchemas}{$property}, +{ %$state, schema_path => jsonp($state->{schema_path}, 'dependentSchemas', $property) })) { next; } $valid = 0; last if $state->{short_circuit}; } return E($state, 'not all dependencies are satisfied') if not $valid; return 1; } sub _traverse_keyword_dependencies ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'object'); my $valid = 1; foreach my $property (sort keys $schema->{dependencies}->%*) { if (is_type('array', $schema->{dependencies}{$property})) { # as in dependentRequired foreach my $index (0..$schema->{dependencies}{$property}->$#*) { $valid = E({ %$state, _schema_path_suffix => [ $property, $index ] }, 'element #%d is not a string', $index) if not is_type('string', $schema->{dependencies}{$property}[$index]); } $valid = E({ %$state, _schema_path_suffix => $property }, 'elements are not unique') if not is_elements_unique($schema->{dependencies}{$property}); $valid = E($state, '"dependencies" array for %s is empty', $property) if $state->{spec_version} eq 'draft4' and not $schema->{dependencies}{$property}->@*; } else { # as in dependentSchemas $valid = 0 if not $class->traverse_property_schema($schema, $state, $property); } } return $valid; } sub _eval_keyword_dependencies ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my $valid = 1; foreach my $property (sort keys $schema->{dependencies}->%*) { next if not exists $data->{$property}; if (is_type('array', $schema->{dependencies}{$property})) { # as in dependentRequired if (my @missing = grep !exists($data->{$_}), $schema->{dependencies}{$property}->@*) { $valid = E({ %$state, _schema_path_suffix => $property }, 'object is missing propert%s: %s', @missing > 1 ? 'ies' : 'y', join(', ', @missing)); } } else { # as in dependentSchemas if ($class->eval($data, $schema->{dependencies}{$property}, +{ %$state, schema_path => jsonp($state->{schema_path}, 'dependencies', $property) })) { next; } $valid = 0; last if $state->{short_circuit}; } } return E($state, 'not all dependencies are satisfied') if not $valid; return 1; } sub _traverse_keyword_prefixItems { shift->traverse_array_schemas(@_) } sub _eval_keyword_prefixItems { goto \&_eval_keyword__items_array_schemas } sub _traverse_keyword_items ($class, $schema, $state) { if (is_plain_arrayref($schema->{items})) { return E($state, 'array form of "items" not supported in %s', $state->{spec_version}) if $state->{spec_version} !~ /^draft(?:[467]|2019-09)$/; return $class->traverse_array_schemas($schema, $state); } $class->traverse_subschema($schema, $state); } sub _eval_keyword_items ($class, $data, $schema, $state) { goto \&_eval_keyword__items_array_schemas if is_plain_arrayref($schema->{items}); goto \&_eval_keyword__items_schema; } sub _traverse_keyword_additionalItems { shift->traverse_subschema(@_) } sub _eval_keyword_additionalItems ($class, $data, $schema, $state) { return 1 if not exists $state->{_last_items_index}; goto \&_eval_keyword__items_schema; } # prefixItems (draft 2020-12), array-based items (all drafts) sub _eval_keyword__items_array_schemas ($class, $data, $schema, $state) { return 1 if not is_type('array', $data); return 1 if ($state->{_last_items_index}//-1) == $data->$#*; my $valid = 1; foreach my $idx (0 .. $data->$#*) { last if $idx > $schema->{$state->{keyword}}->$#*; $state->{_last_items_index} = $idx; if (is_type('boolean', $schema->{$state->{keyword}}[$idx])) { next if $schema->{$state->{keyword}}[$idx]; $valid = E({ %$state, data_path => $state->{data_path}.'/'.$idx, _schema_path_suffix => $idx, collect_annotations => $state->{collect_annotations} & ~1 }, 'item not permitted'); } elsif ($class->eval($data->[$idx], $schema->{$state->{keyword}}[$idx], +{ %$state, data_path => $state->{data_path}.'/'.$idx, schema_path => $state->{schema_path}.'/'.$state->{keyword}.'/'.$idx, collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; last if $state->{short_circuit} and not exists $schema->{ $state->{keyword} eq 'prefixItems' ? 'items' : $state->{keyword} eq 'items' ? 'additionalItems' : die }; } A($state, $state->{_last_items_index} == $data->$#* ? true : $state->{_last_items_index}); return E($state, 'not all items are valid') if not $valid; return 1; } # schema-based items (all drafts), and additionalItems (up to and including draft2019-09) sub _eval_keyword__items_schema ($class, $data, $schema, $state) { return 1 if not is_type('array', $data); return 1 if ($state->{_last_items_index}//-1) == $data->$#*; my $valid = 1; foreach my $idx (($state->{_last_items_index}//-1)+1 .. $data->$#*) { if (is_type('boolean', $schema->{$state->{keyword}})) { next if $schema->{$state->{keyword}}; $valid = E({ %$state, data_path => $state->{data_path}.'/'.$idx }, '%sitem not permitted', exists $schema->{prefixItems} || $state->{keyword} eq 'additionalItems' ? 'additional ' : ''); } else { if ($class->eval($data->[$idx], $schema->{$state->{keyword}}, +{ %$state, data_path => $state->{data_path}.'/'.$idx, schema_path => $state->{schema_path}.'/'.$state->{keyword}, collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; } last if $state->{short_circuit}; } $state->{_last_items_index} = $data->$#*; A($state, true); return E($state, 'subschema is not valid against all %sitems', $state->{keyword} eq 'additionalItems' ? 'additional ' : '') if not $valid; return 1; } sub _traverse_keyword_contains { shift->traverse_subschema(@_) } sub _eval_keyword_contains ($class, $data, $schema, $state) { return 1 if not is_type('array', $data); $state->{_num_contains} = 0; my (@errors, @valid); foreach my $idx (0 .. $data->$#*) { if ($class->eval($data->[$idx], $schema->{contains}, +{ %$state, errors => \@errors, data_path => $state->{data_path}.'/'.$idx, schema_path => $state->{schema_path}.'/contains', collect_annotations => $state->{collect_annotations} & ~1 })) { ++$state->{_num_contains}; push @valid, $idx; last if $state->{short_circuit} and (not exists $schema->{maxContains} or $state->{_num_contains} > $schema->{maxContains}) and ($state->{_num_contains} >= ($schema->{minContains}//1)); } } # note: no items contained is only valid when minContains is explicitly 0 if (not $state->{_num_contains} and (($schema->{minContains}//1) > 0 or $state->{spec_version} =~ /^draft[467]$/)) { push $state->{errors}->@*, @errors; return E($state, 'subschema is not valid against any item'); } # only draft2020-12 and later can produce annotations return $state->{spec_version} =~ /^draft(?:[467]|2019-09)$/ ? 1 : A($state, @valid == @$data ? true : \@valid); } # 'maxContains' and 'minContains' are owned by the Validation vocabulary, but do nothing if the # Applicator vocabulary is omitted and depend on the result of 'contains', so they are implemented # here, to be evaluated after 'contains' sub _traverse_keyword_maxContains { 1 } sub _eval_keyword_maxContains ($class, $data, $schema, $state) { return 1 if not grep $_ eq 'JSON::Schema::Modern::Vocabulary::Validation', $state->{vocabularies}->@*; return 1 if not exists $state->{_num_contains}; return 1 if not is_type('array', $data); return E($state, 'array contains more than %d matching items', $schema->{maxContains}) if $state->{_num_contains} > $schema->{maxContains}; return 1; } sub _traverse_keyword_minContains { 1 } sub _eval_keyword_minContains ($class, $data, $schema, $state) { return 1 if not grep $_ eq 'JSON::Schema::Modern::Vocabulary::Validation', $state->{vocabularies}->@*; return 1 if not exists $state->{_num_contains}; return 1 if not is_type('array', $data); return E($state, 'array contains fewer than %d matching items', $schema->{minContains}) if $state->{_num_contains} < $schema->{minContains}; return 1; } sub _traverse_keyword_properties { shift->traverse_object_schemas(@_) } sub _eval_keyword_properties ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my $valid = 1; my @properties; foreach my $property (sort keys $schema->{properties}->%*) { next if not exists $data->{$property}; push @properties, $property; if (is_type('boolean', $schema->{properties}{$property})) { next if $schema->{properties}{$property}; $valid = E({ %$state, data_path => jsonp($state->{data_path}, $property), _schema_path_suffix => $property }, 'property not permitted'); } else { if ($class->eval($data->{$property}, $schema->{properties}{$property}, +{ %$state, data_path => jsonp($state->{data_path}, $property), schema_path => jsonp($state->{schema_path}, 'properties', $property), collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; } last if $state->{short_circuit}; } A($state, \@properties); return E($state, 'not all properties are valid') if not $valid; return 1; } sub _traverse_keyword_patternProperties ($class, $schema, $state) { return if not assert_keyword_type($state, $schema, 'object'); my $valid = 1; foreach my $property (sort keys $schema->{patternProperties}->%*) { $valid = 0 if not assert_pattern({ %$state, _schema_path_suffix => $property }, $property); $valid = 0 if not $class->traverse_property_schema($schema, $state, $property); } return $valid; } sub _eval_keyword_patternProperties ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my $valid = 1; my @properties; foreach my $property_pattern (sort keys $schema->{patternProperties}->%*) { foreach my $property (sort grep m/(?:$property_pattern)/, keys %$data) { push @properties, $property; if (is_type('boolean', $schema->{patternProperties}{$property_pattern})) { next if $schema->{patternProperties}{$property_pattern}; $valid = E({ %$state, data_path => jsonp($state->{data_path}, $property), _schema_path_suffix => $property_pattern }, 'property not permitted'); } else { if ($class->eval($data->{$property}, $schema->{patternProperties}{$property_pattern}, +{ %$state, data_path => jsonp($state->{data_path}, $property), schema_path => jsonp($state->{schema_path}, 'patternProperties', $property_pattern), collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; } last if $state->{short_circuit}; } } A($state, [ uniqstr @properties ]); return E($state, 'not all properties are valid') if not $valid; return 1; } sub _traverse_keyword_additionalProperties { shift->traverse_subschema(@_) } sub _eval_keyword_additionalProperties ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my $valid = 1; my @properties; foreach my $property (sort keys %$data) { next if exists $schema->{properties} and exists $schema->{properties}{$property}; next if exists $schema->{patternProperties} and any { $property =~ /(?:$_)/ } keys $schema->{patternProperties}->%*; push @properties, $property; if (is_type('boolean', $schema->{additionalProperties})) { next if $schema->{additionalProperties}; $valid = E({ %$state, data_path => jsonp($state->{data_path}, $property) }, 'additional property not permitted'); } else { if ($class->eval($data->{$property}, $schema->{additionalProperties}, +{ %$state, data_path => jsonp($state->{data_path}, $property), schema_path => $state->{schema_path}.'/additionalProperties', collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; } last if $state->{short_circuit}; } A($state, \@properties); return E($state, 'not all additional properties are valid') if not $valid; return 1; } sub _traverse_keyword_propertyNames { shift->traverse_subschema(@_) } sub _eval_keyword_propertyNames ($class, $data, $schema, $state) { return 1 if not is_type('object', $data); my $valid = 1; foreach my $property (sort keys %$data) { if ($class->eval($property, $schema->{propertyNames}, +{ %$state, data_path => jsonp($state->{data_path}, $property), schema_path => $state->{schema_path}.'/propertyNames', collect_annotations => $state->{collect_annotations} & ~1 })) { next; } $valid = 0; last if $state->{short_circuit}; } return E($state, 'not all property names are valid') if not $valid; return 1; } 1; __END__ =pod =encoding UTF-8 =head1 NAME JSON::Schema::Modern::Vocabulary::Applicator - Implementation of the JSON Schema Applicator vocabulary =head1 VERSION version 0.609 =head1 DESCRIPTION =for Pod::Coverage vocabulary evaluation_order keywords =for stopwords metaschema Implementation of the JSON Schema Draft 2020-12 "Applicator" vocabulary, indicated in metaschemas with the URI C and formally specified in L. Support is also provided for =over 4 =item * the equivalent Draft 2019-09 keywords, indicated in metaschemas with the URI C and formally specified in L (except for the C and C keywords, which are implemented in L); =item * the equivalent Draft 7 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 6 keywords that correspond to this vocabulary and are formally specified in L. =item * the equivalent Draft 4 keywords that correspond to this vocabulary and are formally specified in L. =back =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/script/json-schema-eval000640 000766 000024 00000017207 15001302761 021555 0ustar00etherstaff000000 000000 #!/usr/bin/perl # vim: set ts=8 sts=2 sw=2 tw=100 et : # PODNAME: json-schema-eval # ABSTRACT: A command-line interface to JSON::Schema::Modern::evaluate() use 5.020; # for fc, unicode_strings features use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Getopt::Long::Descriptive; use Path::Tiny; use Safe::Isa; use Feature::Compat::Try; use JSON::Schema::Modern; my ($opt, $usage) = Getopt::Long::Descriptive::describe_options( "$0 %o", ['help|usage|?|h', 'print usage information and exit', { shortcircuit => 1 } ], [], ['specification_version|version=s', 'which version of the JSON Schema specification to use'], ['output_format=s', 'output format (flag, basic, terse)'], ['short_circuit', 'return early in any execution path as soon as the outcome can be determined'], ['max_traversal_depth=i', 'the maximum number of levels deep a schema traversal may go'], ['validate_formats', 'treat format as an assertion, not merely an annotation'], ['validate_content_schemas', 'treat contentMediaType and contentSchema keywords as assertions'], ['collect_annotations', 'collect annotations'], ['strict', 'disallow unknown keywords'], # scalarref_booleans, stringy_numbers make no sense in json-encoded data [], ['validate-schema:s', 'validate the provided schema against its meta-schema. do not provide --data or --schema.' ], ['add-schema=s@', 'the filename of an extra schema to load, so it can be used by $ref' ], ['data=s', 'the filename to use for the instance data (if not provided, STDIN is used)'], ['schema=s', 'the filename to use for the schema (if not provided, STDIN is used)'], ['dump-identifiers', 'print a list of all identifiers found in the schema'], ); print($usage->text), exit if $opt->help; my ($data, $schema, $validate_schema) = delete $opt->@{qw(data schema validate_schema)}; die '--validate-schema and --data should not be used together' if defined $data and defined $validate_schema; die '--validate-schema and --schema should not be used together' if defined $schema and defined $validate_schema; my $js = JSON::Schema::Modern->new(%$opt); my $decoder = JSON::Schema::Modern::_JSON_BACKEND()->new->allow_nonref(1)->utf8(0); foreach my $add_schema (@{$opt->add_schema//[]}) { try { $js->add_schema($decoder->decode(path($add_schema)->slurp_utf8)); } catch ($e) { say $e->$_isa('JSON::Schema::Modern::Result') ? $e->dump: '"'.$e.'"'; exit 2; } } my $result; if (defined $validate_schema) { if (length $validate_schema) { # boolean flag is passed as ''; some other value = filename $schema = path($validate_schema)->slurp_utf8; } else { say 'enter schema, followed by ^D:'; local $/; $schema = ; say ''; } $schema = $decoder->decode($schema); $result = $js->validate_schema($schema); } else { if (defined $data) { $data = path($data)->slurp_utf8; } else { say 'enter data instance, followed by ^D:'; local $/; $data = ; STDIN->clearerr; } if (defined $schema) { $schema = path($schema)->slurp_utf8; } else { say 'enter schema, followed by ^D:'; local $/; $schema = ; say ''; } $data = $decoder->decode($data); $schema = $decoder->decode($schema); $result = $js->evaluate($data, $schema); } say $result->dump; if ($opt->dump_identifiers) { $js->add_schema($schema) if $validate_schema; my %identifiers = map +( $_->[0] => { canonical_uri => $_->[1]{canonical_uri}, document_base => $_->[1]{document}->canonical_uri, document_path => $_->[1]{path}, } ), grep $_->[0] !~ m{^https://json-schema.org/}, $js->_resource_pairs; my $encoder = JSON::Schema::Modern::_JSON_BACKEND()->new ->convert_blessed(1) ->utf8(0) ->canonical(1) ->pretty(1); $encoder->indent_length(2) if $encoder->can('indent_length'); say $encoder->encode(\%identifiers); } exit($result->valid ? 0 : $result->exception ? 2 : 1); __END__ =pod =encoding UTF-8 =head1 NAME json-schema-eval - A command-line interface to JSON::Schema::Modern::evaluate() =head1 VERSION version 0.609 =head1 SYNOPSIS json-schema-eval \ [ --specification_version|version ] \ [ --output_format ] \ [ --short_circuit ] \ [ --max_traversal_depth ] \ [ --validate_formats ] \ [ --validate_content_schemas ] \ [ --collect_annotations ] \ [ --strict ] \ [ --data ] \ [ --schema ] \ [ --validate-schema [filename] ] [ --add-schema ] [ --dump-identifiers ] =head1 DESCRIPTION A command-line interface to L. F contains: {"hello": 42.1} F contains: {"properties": {"hello": {"type": ["string", "integer"]}}} Run: json-schema-eval --data data.json --schema schema.json produces output: { "errors" : [ { "error" : "got number, not one of string, integer", "instanceLocation" : "/hello", "keywordLocation" : "/properties/hello/type" }, { "error" : "not all properties are valid", "instanceLocation" : "", "keywordLocation" : "/properties" } ], "valid" : false } Or run: json-schema-eval --validate-schema schema.json produces output: { "valid": true } The exit value (C<$?>) is 0 when the result is valid, 1 when it is invalid, and some other non-zero value if an exception occurred. =head1 OPTIONS =for stopwords schemas All boolean and string options used as L are available. Additionally, C<--data> is used to provide the filename containing a json-encoded data instance, and C<--schema> provides the filename containing a json-encoded schema. If either or both of these are not provided, STDIN is used as input. Only JSON-encoded data and schemas are supported at this time. Alternatively, you can use C<--validate-schema> and either provide a filename containing a json-encoded schema, or omit the argument to read a schema from STDIN. The schema will be evaluated against its meta-schema for the corresponding specification version. Additional schemas, that you wish to use via the C<$ref> keyword, can be added with C<< --add-schema >>. The actual filename is insignificant: Make sure you use an C<$id> keyword within that schema that matches the value you use in the C<$ref>. This option can be used more than once. =for stopwords OpenAPI =head1 AVAILABILITY This executable is available on modern Debian versions (via C) as the C package. =head1 SUPPORT Bugs may be submitted through L. I am also usually active on irc, as 'ether' at C and C. =for stopwords OpenAPI You can also find me on the L and L, which are also great resources for finding help. =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2020 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Some schema files have their own licence, in share/LICENSE. =cut JSON-Schema-Modern-0.609/xt/author/000770 000766 000024 00000000000 15001302761 017121 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/xt/release/000770 000766 000024 00000000000 15001302761 017237 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/xt/release/changes_has_content.t000644 000766 000024 00000002101 15001302761 023416 0ustar00etherstaff000000 000000 use Test::More tests => 2; note 'Checking Changes'; my $changes_file = 'Changes'; my $newver = '0.609'; my $trial_token = '-TRIAL'; my $encoding = 'UTF-8'; SKIP: { ok(-e $changes_file, "$changes_file file exists") or skip 'Changes is missing', 1; ok(_get_changes($newver), "$changes_file has content for $newver"); } done_testing; sub _get_changes { my $newver = shift; # parse changelog to find commit message open(my $fh, '<', $changes_file) or die "cannot open $changes_file: $!"; my $changelog = join('', <$fh>); if ($encoding) { require Encode; $changelog = Encode::decode($encoding, $changelog, Encode::FB_CROAK()); } close $fh; my @content = grep { /^$newver(?:$trial_token)?(?:\s+|$)/ ... /^\S/ } # from newver to un-indented split /\n/, $changelog; shift @content; # drop the version line # drop unindented last line and trailing blank lines pop @content while ( @content && $content[-1] =~ /^(?:\S|\s*$)/ ); # return number of non-blank lines return scalar @content; } JSON-Schema-Modern-0.609/xt/release/cpan-changes.t000644 000766 000024 00000000344 15001302761 021757 0ustar00etherstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::CPAN::Changes 0.013 use Test::More 0.96 tests => 1; use Test::CPAN::Changes; subtest 'changes_ok' => sub { changes_file_ok('Changes'); }; JSON-Schema-Modern-0.609/xt/author/mojibake.t000644 000766 000024 00000000151 15001302761 021067 0ustar00etherstaff000000 000000 #!perl use strict; use warnings qw(all); use Test::More; use Test::Mojibake; all_files_encoding_ok(); JSON-Schema-Modern-0.609/xt/author/kwalitee.t000644 000766 000024 00000000275 15001302761 021122 0ustar00etherstaff000000 000000 # this test was generated with Dist::Zilla::Plugin::Test::Kwalitee 2.12 use strict; use warnings; use Test::More 0.88; use Test::Kwalitee 1.21 'kwalitee_ok'; kwalitee_ok(); done_testing; JSON-Schema-Modern-0.609/xt/author/no-tabs.t000644 000766 000024 00000017003 15001302761 020655 0ustar00etherstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.15 use Test::More 0.88; use Test::NoTabs; my @files = ( 'lib/JSON/Schema/Modern.pm', 'lib/JSON/Schema/Modern/Annotation.pm', 'lib/JSON/Schema/Modern/Document.pm', 'lib/JSON/Schema/Modern/Error.pm', 'lib/JSON/Schema/Modern/Result.pm', 'lib/JSON/Schema/Modern/ResultNode.pm', 'lib/JSON/Schema/Modern/Utilities.pm', 'lib/JSON/Schema/Modern/Vocabulary.pm', 'lib/JSON/Schema/Modern/Vocabulary/Applicator.pm', 'lib/JSON/Schema/Modern/Vocabulary/Content.pm', 'lib/JSON/Schema/Modern/Vocabulary/Core.pm', 'lib/JSON/Schema/Modern/Vocabulary/FormatAnnotation.pm', 'lib/JSON/Schema/Modern/Vocabulary/FormatAssertion.pm', 'lib/JSON/Schema/Modern/Vocabulary/MetaData.pm', 'lib/JSON/Schema/Modern/Vocabulary/Unevaluated.pm', 'lib/JSON/Schema/Modern/Vocabulary/Validation.pm', 'script/json-schema-eval', 't/00-report-prereqs.dd', 't/00-report-prereqs.t', 't/add-schema.t', 't/additional-tests-draft2019-09.t', 't/additional-tests-draft2019-09/README', 't/additional-tests-draft2019-09/anchor.json', 't/additional-tests-draft2019-09/annotation-collection.json', 't/additional-tests-draft2019-09/badRef.json', 't/additional-tests-draft2019-09/faux-buggy-schemas.json', 't/additional-tests-draft2019-09/format-duration.json', 't/additional-tests-draft2019-09/format-ipv4.json', 't/additional-tests-draft2019-09/format-ipv6.json', 't/additional-tests-draft2019-09/format-relative-json-pointer.json', 't/additional-tests-draft2019-09/formats.json', 't/additional-tests-draft2019-09/id.json', 't/additional-tests-draft2019-09/integers.json', 't/additional-tests-draft2019-09/keyword-independence.json', 't/additional-tests-draft2019-09/loose-types-const-enum.json', 't/additional-tests-draft2019-09/optional/format-date-time.json', 't/additional-tests-draft2019-09/optional/format-date.json', 't/additional-tests-draft2019-09/optional/format-time.json', 't/additional-tests-draft2019-09/recursive-dynamic.json', 't/additional-tests-draft2019-09/ref-and-id.json', 't/additional-tests-draft2019-09/ref.json', 't/additional-tests-draft2019-09/short-circuit.json', 't/additional-tests-draft2019-09/unknownKeyword.json', 't/additional-tests-draft2019-09/vocabulary.json', 't/additional-tests-draft2020-12.t', 't/additional-tests-draft2020-12/README', 't/additional-tests-draft2020-12/anchor.json', 't/additional-tests-draft2020-12/annotation-collection.json', 't/additional-tests-draft2020-12/badRef.json', 't/additional-tests-draft2020-12/dynamicRef.json', 't/additional-tests-draft2020-12/faux-buggy-schemas.json', 't/additional-tests-draft2020-12/format-duration.json', 't/additional-tests-draft2020-12/format-ipv4.json', 't/additional-tests-draft2020-12/format-ipv6.json', 't/additional-tests-draft2020-12/format-relative-json-pointer.json', 't/additional-tests-draft2020-12/formats.json', 't/additional-tests-draft2020-12/id.json', 't/additional-tests-draft2020-12/integers.json', 't/additional-tests-draft2020-12/keyword-independence.json', 't/additional-tests-draft2020-12/loose-types-const-enum.json', 't/additional-tests-draft2020-12/optional/format-date-time.json', 't/additional-tests-draft2020-12/optional/format-date.json', 't/additional-tests-draft2020-12/optional/format-time.json', 't/additional-tests-draft2020-12/recursive-dynamic.json', 't/additional-tests-draft2020-12/ref-and-id.json', 't/additional-tests-draft2020-12/ref.json', 't/additional-tests-draft2020-12/short-circuit.json', 't/additional-tests-draft2020-12/unknownKeyword.json', 't/additional-tests-draft2020-12/vocabulary.json', 't/additional-tests-draft7.t', 't/additional-tests-draft7/README', 't/additional-tests-draft7/badRef.json', 't/additional-tests-draft7/faux-buggy-schemas.json', 't/additional-tests-draft7/format-ipv4.json', 't/additional-tests-draft7/format-relative-json-pointer.json', 't/additional-tests-draft7/id.json', 't/additional-tests-draft7/integers.json', 't/additional-tests-draft7/keyword-independence.json', 't/additional-tests-draft7/loose-types-const-enum.json', 't/additional-tests-draft7/not-an-anchor.json', 't/additional-tests-draft7/not-an-id.json', 't/additional-tests-draft7/optional/format-date-time.json', 't/additional-tests-draft7/optional/format-date.json', 't/additional-tests-draft7/optional/format-time.json', 't/additional-tests-draft7/ref-and-id.json', 't/additional-tests-draft7/ref.json', 't/additional-tests-draft7/short-circuit.json', 't/additional-tests-draft7/unknownKeyword.json', 't/additional-tests-draft7/vocabulary.json', 't/annotations.t', 't/boolean-data.t', 't/boolean-schemas.t', 't/cached-metaschemas.t', 't/callbacks.t', 't/checksums.t', 't/content-encoding.t', 't/dialects.t', 't/document.t', 't/equality.t', 't/errors.t', 't/evaluate_json_string.t', 't/find-identifiers.t', 't/formats.t', 't/invalid-schemas.t', 't/invalid-schemas/invalid-input.json', 't/invalid-schemas/ref.json', 't/invalid-schemas/vocabulary.json', 't/lib/Acceptance.pm', 't/lib/Helper.pm', 't/lib/MyVocabulary/BadEvaluationOrder.pm', 't/lib/MyVocabulary/BadVocabularySub1.pm', 't/lib/MyVocabulary/BadVocabularySub2.pm', 't/lib/MyVocabulary/BadVocabularySub3.pm', 't/lib/MyVocabulary/MissingRole.pm', 't/lib/MyVocabulary/MissingSub.pm', 't/lib/MyVocabulary/StringComparison.pm', 't/max_traversal_depth.t', 't/multipleOf.t', 't/output_format.t', 't/pattern.t', 't/read_serialized_file', 't/ref.t', 't/results/draft2019-09-acceptance-format.txt', 't/results/draft2019-09-acceptance.txt', 't/results/draft2019-09-additional-tests.txt', 't/results/draft2019-09-invalid-schemas.txt', 't/results/draft2020-12-acceptance-format.txt', 't/results/draft2020-12-acceptance.txt', 't/results/draft2020-12-additional-tests.txt', 't/results/draft2020-12-invalid-schemas.txt', 't/results/draft4-acceptance-format.txt', 't/results/draft4-acceptance.txt', 't/results/draft6-acceptance-format.txt', 't/results/draft6-acceptance.txt', 't/results/draft7-acceptance-format.txt', 't/results/draft7-acceptance.txt', 't/results/draft7-additional-tests.txt', 't/serialization.t', 't/specification_version.t', 't/strict.t', 't/stringy-numbers.t', 't/traverse.t', 't/type.t', 't/unsupported-keywords.t', 't/validate-schema.t', 't/vocabularies.t', 't/zzz-acceptance-draft2019-09-format.t', 't/zzz-acceptance-draft2019-09.t', 't/zzz-acceptance-draft2020-12-format.t', 't/zzz-acceptance-draft2020-12.t', 't/zzz-acceptance-draft4-format.t', 't/zzz-acceptance-draft4.t', 't/zzz-acceptance-draft6-format.t', 't/zzz-acceptance-draft6.t', 't/zzz-acceptance-draft7-format.t', 't/zzz-acceptance-draft7.t', 't/zzz-check-breaks.t', 'xt/author/00-compile.t', 'xt/author/clean-namespaces.t', 'xt/author/distmeta.t', 'xt/author/eol.t', 'xt/author/kwalitee.t', 'xt/author/minimum-version.t', 'xt/author/mojibake.t', 'xt/author/no-tabs.t', 'xt/author/pod-coverage.t', 'xt/author/pod-spell.t', 'xt/author/pod-syntax.t', 'xt/author/portability.t', 'xt/release/changes_has_content.t', 'xt/release/cpan-changes.t' ); notabs_ok($_) foreach @files; done_testing; JSON-Schema-Modern-0.609/xt/author/minimum-version.t000644 000766 000024 00000000152 15001302761 022445 0ustar00etherstaff000000 000000 use strict; use warnings; use Test::More; use Test::MinimumVersion; all_minimum_version_ok( qq{5.020} ); JSON-Schema-Modern-0.609/xt/author/00-compile.t000644 000766 000024 00000006504 15001302761 021163 0ustar00etherstaff000000 000000 use 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.058 use Test::More 0.94; plan tests => 18; my @module_files = ( 'JSON/Schema/Modern.pm', 'JSON/Schema/Modern/Annotation.pm', 'JSON/Schema/Modern/Document.pm', 'JSON/Schema/Modern/Error.pm', 'JSON/Schema/Modern/Result.pm', 'JSON/Schema/Modern/ResultNode.pm', 'JSON/Schema/Modern/Utilities.pm', 'JSON/Schema/Modern/Vocabulary.pm', 'JSON/Schema/Modern/Vocabulary/Applicator.pm', 'JSON/Schema/Modern/Vocabulary/Content.pm', 'JSON/Schema/Modern/Vocabulary/Core.pm', 'JSON/Schema/Modern/Vocabulary/FormatAnnotation.pm', 'JSON/Schema/Modern/Vocabulary/FormatAssertion.pm', 'JSON/Schema/Modern/Vocabulary/MetaData.pm', 'JSON/Schema/Modern/Vocabulary/Unevaluated.pm', 'JSON/Schema/Modern/Vocabulary/Validation.pm' ); my @scripts = ( 'script/json-schema-eval' ); # no fake home requested my @switches = ( -d 'blib' ? '-Mblib' : '-Ilib', ); use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-e', "require q[$lib]")) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } foreach my $file (@scripts) { SKIP: { open my $fh, '<', $file or warn("Unable to open $file: $!"), next; my $line = <$fh>; close $fh and skip("$file isn't perl", 1) unless $line =~ /^#!\s*(?:\S*perl\S*)((?:\s+-\w*)*)(?:\s*#.*)?$/; @switches = (@switches, split(' ', $1)) if $1; close $fh and skip("$file uses -T; not testable with PERL5LIB", 1) if grep { $_ eq '-T' } @switches and $ENV{PERL5LIB}; my $stderr = IO::Handle->new; diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} } $^X, @switches, '-c', $file)) if $ENV{PERL_COMPILE_TEST_DEBUG}; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-c', $file); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$file compiled ok"); shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/ and not eval { +require blib; blib->VERSION('1.01') }; # in older perls, -c output is simply the file portion of the path being tested if (@_warnings = grep { !/\bsyntax OK$/ } grep { chomp; $_ ne (File::Spec->splitpath($file))[2] } @_warnings) { warn @_warnings; push @warnings, @_warnings; } } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', explain(\@warnings); BAIL_OUT("Compilation problems") if !Test::More->builder->is_passing; JSON-Schema-Modern-0.609/xt/author/eol.t000644 000766 000024 00000017035 15001302761 020076 0ustar00etherstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::EOL 0.19 use Test::More 0.88; use Test::EOL; my @files = ( 'lib/JSON/Schema/Modern.pm', 'lib/JSON/Schema/Modern/Annotation.pm', 'lib/JSON/Schema/Modern/Document.pm', 'lib/JSON/Schema/Modern/Error.pm', 'lib/JSON/Schema/Modern/Result.pm', 'lib/JSON/Schema/Modern/ResultNode.pm', 'lib/JSON/Schema/Modern/Utilities.pm', 'lib/JSON/Schema/Modern/Vocabulary.pm', 'lib/JSON/Schema/Modern/Vocabulary/Applicator.pm', 'lib/JSON/Schema/Modern/Vocabulary/Content.pm', 'lib/JSON/Schema/Modern/Vocabulary/Core.pm', 'lib/JSON/Schema/Modern/Vocabulary/FormatAnnotation.pm', 'lib/JSON/Schema/Modern/Vocabulary/FormatAssertion.pm', 'lib/JSON/Schema/Modern/Vocabulary/MetaData.pm', 'lib/JSON/Schema/Modern/Vocabulary/Unevaluated.pm', 'lib/JSON/Schema/Modern/Vocabulary/Validation.pm', 'script/json-schema-eval', 't/00-report-prereqs.dd', 't/00-report-prereqs.t', 't/add-schema.t', 't/additional-tests-draft2019-09.t', 't/additional-tests-draft2019-09/README', 't/additional-tests-draft2019-09/anchor.json', 't/additional-tests-draft2019-09/annotation-collection.json', 't/additional-tests-draft2019-09/badRef.json', 't/additional-tests-draft2019-09/faux-buggy-schemas.json', 't/additional-tests-draft2019-09/format-duration.json', 't/additional-tests-draft2019-09/format-ipv4.json', 't/additional-tests-draft2019-09/format-ipv6.json', 't/additional-tests-draft2019-09/format-relative-json-pointer.json', 't/additional-tests-draft2019-09/formats.json', 't/additional-tests-draft2019-09/id.json', 't/additional-tests-draft2019-09/integers.json', 't/additional-tests-draft2019-09/keyword-independence.json', 't/additional-tests-draft2019-09/loose-types-const-enum.json', 't/additional-tests-draft2019-09/optional/format-date-time.json', 't/additional-tests-draft2019-09/optional/format-date.json', 't/additional-tests-draft2019-09/optional/format-time.json', 't/additional-tests-draft2019-09/recursive-dynamic.json', 't/additional-tests-draft2019-09/ref-and-id.json', 't/additional-tests-draft2019-09/ref.json', 't/additional-tests-draft2019-09/short-circuit.json', 't/additional-tests-draft2019-09/unknownKeyword.json', 't/additional-tests-draft2019-09/vocabulary.json', 't/additional-tests-draft2020-12.t', 't/additional-tests-draft2020-12/README', 't/additional-tests-draft2020-12/anchor.json', 't/additional-tests-draft2020-12/annotation-collection.json', 't/additional-tests-draft2020-12/badRef.json', 't/additional-tests-draft2020-12/dynamicRef.json', 't/additional-tests-draft2020-12/faux-buggy-schemas.json', 't/additional-tests-draft2020-12/format-duration.json', 't/additional-tests-draft2020-12/format-ipv4.json', 't/additional-tests-draft2020-12/format-ipv6.json', 't/additional-tests-draft2020-12/format-relative-json-pointer.json', 't/additional-tests-draft2020-12/formats.json', 't/additional-tests-draft2020-12/id.json', 't/additional-tests-draft2020-12/integers.json', 't/additional-tests-draft2020-12/keyword-independence.json', 't/additional-tests-draft2020-12/loose-types-const-enum.json', 't/additional-tests-draft2020-12/optional/format-date-time.json', 't/additional-tests-draft2020-12/optional/format-date.json', 't/additional-tests-draft2020-12/optional/format-time.json', 't/additional-tests-draft2020-12/recursive-dynamic.json', 't/additional-tests-draft2020-12/ref-and-id.json', 't/additional-tests-draft2020-12/ref.json', 't/additional-tests-draft2020-12/short-circuit.json', 't/additional-tests-draft2020-12/unknownKeyword.json', 't/additional-tests-draft2020-12/vocabulary.json', 't/additional-tests-draft7.t', 't/additional-tests-draft7/README', 't/additional-tests-draft7/badRef.json', 't/additional-tests-draft7/faux-buggy-schemas.json', 't/additional-tests-draft7/format-ipv4.json', 't/additional-tests-draft7/format-relative-json-pointer.json', 't/additional-tests-draft7/id.json', 't/additional-tests-draft7/integers.json', 't/additional-tests-draft7/keyword-independence.json', 't/additional-tests-draft7/loose-types-const-enum.json', 't/additional-tests-draft7/not-an-anchor.json', 't/additional-tests-draft7/not-an-id.json', 't/additional-tests-draft7/optional/format-date-time.json', 't/additional-tests-draft7/optional/format-date.json', 't/additional-tests-draft7/optional/format-time.json', 't/additional-tests-draft7/ref-and-id.json', 't/additional-tests-draft7/ref.json', 't/additional-tests-draft7/short-circuit.json', 't/additional-tests-draft7/unknownKeyword.json', 't/additional-tests-draft7/vocabulary.json', 't/annotations.t', 't/boolean-data.t', 't/boolean-schemas.t', 't/cached-metaschemas.t', 't/callbacks.t', 't/checksums.t', 't/content-encoding.t', 't/dialects.t', 't/document.t', 't/equality.t', 't/errors.t', 't/evaluate_json_string.t', 't/find-identifiers.t', 't/formats.t', 't/invalid-schemas.t', 't/invalid-schemas/invalid-input.json', 't/invalid-schemas/ref.json', 't/invalid-schemas/vocabulary.json', 't/lib/Acceptance.pm', 't/lib/Helper.pm', 't/lib/MyVocabulary/BadEvaluationOrder.pm', 't/lib/MyVocabulary/BadVocabularySub1.pm', 't/lib/MyVocabulary/BadVocabularySub2.pm', 't/lib/MyVocabulary/BadVocabularySub3.pm', 't/lib/MyVocabulary/MissingRole.pm', 't/lib/MyVocabulary/MissingSub.pm', 't/lib/MyVocabulary/StringComparison.pm', 't/max_traversal_depth.t', 't/multipleOf.t', 't/output_format.t', 't/pattern.t', 't/read_serialized_file', 't/ref.t', 't/results/draft2019-09-acceptance-format.txt', 't/results/draft2019-09-acceptance.txt', 't/results/draft2019-09-additional-tests.txt', 't/results/draft2019-09-invalid-schemas.txt', 't/results/draft2020-12-acceptance-format.txt', 't/results/draft2020-12-acceptance.txt', 't/results/draft2020-12-additional-tests.txt', 't/results/draft2020-12-invalid-schemas.txt', 't/results/draft4-acceptance-format.txt', 't/results/draft4-acceptance.txt', 't/results/draft6-acceptance-format.txt', 't/results/draft6-acceptance.txt', 't/results/draft7-acceptance-format.txt', 't/results/draft7-acceptance.txt', 't/results/draft7-additional-tests.txt', 't/serialization.t', 't/specification_version.t', 't/strict.t', 't/stringy-numbers.t', 't/traverse.t', 't/type.t', 't/unsupported-keywords.t', 't/validate-schema.t', 't/vocabularies.t', 't/zzz-acceptance-draft2019-09-format.t', 't/zzz-acceptance-draft2019-09.t', 't/zzz-acceptance-draft2020-12-format.t', 't/zzz-acceptance-draft2020-12.t', 't/zzz-acceptance-draft4-format.t', 't/zzz-acceptance-draft4.t', 't/zzz-acceptance-draft6-format.t', 't/zzz-acceptance-draft6.t', 't/zzz-acceptance-draft7-format.t', 't/zzz-acceptance-draft7.t', 't/zzz-check-breaks.t', 'xt/author/00-compile.t', 'xt/author/clean-namespaces.t', 'xt/author/distmeta.t', 'xt/author/eol.t', 'xt/author/kwalitee.t', 'xt/author/minimum-version.t', 'xt/author/mojibake.t', 'xt/author/no-tabs.t', 'xt/author/pod-coverage.t', 'xt/author/pod-spell.t', 'xt/author/pod-syntax.t', 'xt/author/portability.t', 'xt/release/changes_has_content.t', 'xt/release/cpan-changes.t' ); eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files; done_testing; JSON-Schema-Modern-0.609/xt/author/pod-syntax.t000644 000766 000024 00000000252 15001302761 021416 0ustar00etherstaff000000 000000 #!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); JSON-Schema-Modern-0.609/xt/author/portability.t000644 000766 000024 00000000130 15001302761 021645 0ustar00etherstaff000000 000000 use strict; use warnings; use Test::More; use Test::Portability::Files; run_tests(); JSON-Schema-Modern-0.609/xt/author/distmeta.t000644 000766 000024 00000000223 15001302761 021120 0ustar00etherstaff000000 000000 #!perl # This file was automatically generated by Dist::Zilla::Plugin::MetaTests. use strict; use warnings; use Test::CPAN::Meta; meta_yaml_ok(); JSON-Schema-Modern-0.609/xt/author/pod-spell.t000644 000766 000024 00000000714 15001302761 021212 0ustar00etherstaff000000 000000 use strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::PodSpelling 2.007006 use Test::Spelling 0.17; use Pod::Wordlist; add_stopwords(); all_pod_files_spelling_ok( qw( examples lib script t xt ) ); __DATA__ Annotation Applicator Content Core Document Error Etheridge FormatAnnotation FormatAssertion JSON Karen MetaData Modern Result ResultNode Schema Unevaluated Utilities Validation Vocabulary ether irc json lib script JSON-Schema-Modern-0.609/xt/author/clean-namespaces.t000644 000766 000024 00000000361 15001302761 022510 0ustar00etherstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::CleanNamespaces 0.006 use Test::More 0.94; use Test::CleanNamespaces 0.15; subtest all_namespaces_clean => sub { all_namespaces_clean() }; done_testing; JSON-Schema-Modern-0.609/xt/author/pod-coverage.t000644 000766 000024 00000001765 15001302761 021675 0ustar00etherstaff000000 000000 # This file was automatically generated by Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe v1.0.1 use strict; use warnings; use Test::More; use Test::Pod::Coverage::TrustMe; my $config = {}; my $modules = [ "JSON::Schema::Modern", "JSON::Schema::Modern::Annotation", "JSON::Schema::Modern::Document", "JSON::Schema::Modern::Error", "JSON::Schema::Modern::Result", "JSON::Schema::Modern::ResultNode", "JSON::Schema::Modern::Utilities", "JSON::Schema::Modern::Vocabulary", "JSON::Schema::Modern::Vocabulary::Applicator", "JSON::Schema::Modern::Vocabulary::Content", "JSON::Schema::Modern::Vocabulary::Core", "JSON::Schema::Modern::Vocabulary::FormatAnnotation", "JSON::Schema::Modern::Vocabulary::FormatAssertion", "JSON::Schema::Modern::Vocabulary::MetaData", "JSON::Schema::Modern::Vocabulary::Unevaluated", "JSON::Schema::Modern::Vocabulary::Validation", ]; plan tests => scalar @$modules; for my $module (@$modules) { pod_coverage_ok($module, $config); } done_testing; JSON-Schema-Modern-0.609/t/dialects.t000640 000766 000024 00000160723 15001302761 017414 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Warnings qw(warnings :no_end_test had_no_warnings allow_warnings); use Test::Fatal; use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new(short_circuit => 0, validate_formats => 1); subtest 'invalid use of the $schema keyword' => sub { cmp_result( $js->evaluate( 1, { allOf => [ true, { '$schema' => 'https://json-schema.org/draft/2019-09/schema' }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/1/$schema', error => '$schema can only appear at the schema resource root', }, ], }, '$schema can only appear at the root of a schema, when there is no canonical URI', ); cmp_result( $js->evaluate( 1, { '$id' => 'https://bloop.com', allOf => [ true, { '$schema' => 'https://json-schema.org/draft/2019-09/schema' }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/1/$schema', absoluteKeywordLocation => 'https://bloop.com#/allOf/1/$schema', error => '$schema can only appear at the schema resource root', }, ], }, '$schema can only appear where the canonical URI has no fragment, when there is a canonical URI', ); cmp_result( $js->evaluate( 1, { '$id' => 'https://bloop3.com', '$defs' => { my_def => { '$schema' => 'https://json-schema.org/draft/2019-09/schema', }, }, '$ref' => '#/$defs/my_def', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$defs/my_def/$schema', absoluteKeywordLocation => 'https://bloop3.com#/$defs/my_def/$schema', error => '$schema can only appear at the schema resource root', }, ], }, 'this is still not a resource root, even in a $ref target', ); }; subtest 'defaults without a $schema keyword' => sub { cmp_result( $js->evaluate(1, true)->TO_JSON, { valid => true }, 'boolean schema: no $id, no $schema', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft2020-12', vocabularies => ignore, # for boolean schemas, vocabularies do not matter }), 'boolean schema: defaults to draft2020-12 without a $schema keyword', ); cmp_result( $js->evaluate( { foo => 1 }, { unevaluatedProperties => false }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo', keywordLocation => '/unevaluatedProperties', error => 'additional property not permitted', }, { instanceLocation => '', keywordLocation => '/unevaluatedProperties', error => 'not all additional properties are valid', }, ], }, 'object schema: no $id, no $schema', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft2020-12', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated) ], }), 'object schema: defaults to draft2020-12 without a $schema keyword', ); cmp_result( $js->evaluate( 1, { '$defs' => { foo => { not => 'invalid subschema' } } }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$defs/foo/not', error => 'invalid schema type: string', }, ], }, '"not" keyword, from the Applicator vocabulary, is traversed at the root level', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'https://id-no-schema1', unevaluatedProperties => false, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo', keywordLocation => '/unevaluatedProperties', absoluteKeywordLocation => 'https://id-no-schema1#/unevaluatedProperties', error => 'additional property not permitted', }, { instanceLocation => '', keywordLocation => '/unevaluatedProperties', absoluteKeywordLocation => 'https://id-no-schema1#/unevaluatedProperties', error => 'not all additional properties are valid', }, ], }, 'object schema: $id, no $schema', ); cmp_result( $js->{_resource_index}{'https://id-no-schema1'}, superhashof({ specification_version => 'draft2020-12', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated) ], }), 'named resource defaults to draft2020-12 without a $schema keyword', ); my $js = JSON::Schema::Modern->new(short_circuit => 0, specification_version => 'draft7'); cmp_result( $js->evaluate(1, true)->TO_JSON, { valid => true }, 'boolean schema: no $id, no $schema', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft7', vocabularies => ignore, # for boolean schemas, vocabularies do not matter }), 'boolean schema: specification_version overridden', ); cmp_result( $js->evaluate( { foo => 1 }, { unevaluatedProperties => 'not a schema' }, )->TO_JSON, { valid => true }, 'object schema: no $id, no $schema, specification version overridden, other keywords are ignored during traversal', ); cmp_result( $js->evaluate( { foo => 1 }, { unevaluatedProperties => false }, )->TO_JSON, { valid => true }, 'object schema: no $id, no $schema, specification version overridden, other keywords are ignored during evaluation', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'object schema: overridden to draft7', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'https://id-no-schema2', unevaluatedProperties => 'not a schema', }, )->TO_JSON, { valid => true }, 'object schema: $id, no $schema, unrecognized+invalid keywords are ignored during traversal', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'https://id-no-schema3', unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, 'object schema: $id, no $schema', ); cmp_result( $js->{_resource_index}{'https://id-no-schema3'}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'object schema: overridden to draft7 and other keywords are ignored', ); }; subtest 'behaviour with a $schema keyword' => sub { cmp_result( $js->evaluate( { foo => 1 }, { '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => 'not a schema', }, )->TO_JSON, { valid => true }, 'object schema: no $id, has $schema, unrecognized+invalid keywords are ignored during traversal', ); cmp_result( $js->evaluate( { foo => 1 }, { '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, 'object schema: no $id, has $schema, unrecognized keywords are ignored during evaluation', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'semantics can be changed to another draft version', ); cmp_result( $js->evaluate( { foo => 1 }, { '$schema' => 'http://json-schema.org/draft-07/schema', unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, 'schema is accepted with $schema without an empty fragment', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), '..and is still recognized as draft7', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'https://id-and-schema1', '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => 'not a schema', }, )->TO_JSON, { valid => true }, '$id and $schema, unrecognized+invalid keywords are ignored during traversal', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'https://id-and-schema2', '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, '$id and $schema', ); cmp_result( $js->{_resource_index}{'https://id-and-schema2'}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'named resource can be changed to another draft version and other keywords are ignored', ); my $js = JSON::Schema::Modern->new(short_circuit => 0, specification_version => 'draft2019-09'); cmp_result( $js->evaluate( { foo => 1 }, { '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => 'not a schema', }, )->TO_JSON, { valid => true }, 'no $id, specification version overridden twice; unrecognized+invalid keywords are ignored during traversal', ); cmp_result( $js->evaluate( { foo => 1 }, { '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, 'no $id, specification version overridden twice, other keywords are ignored during evaluation', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'unnamed resource can be changed to another draft version', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'https://id-and-schema3', '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => 'not a schema', }, )->TO_JSON, { valid => true }, 'no $id, specification version overridden twice; unrecognized+invalid keywords are ignored during traversal', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'https://id-and-schema4', '$schema' => 'http://json-schema.org/draft-07/schema#', unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, 'no $id, specification version overridden twice, other keywords are ignored during evaluation', ); cmp_result( $js->{_resource_index}{''}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'unnamed resource can be changed to another draft version', ); }; subtest 'setting or changing specification versions in a single document' => sub { cmp_result( $js->evaluate( 1, { '$id' => 'https://bloop2.com', allOf => [ true, { '$id' => 'https://newid.com', '$schema' => 'https://json-schema.org/draft/2020-12/schema', }, ], }, )->TO_JSON, { valid => true }, '$schema can appear adjacent to any $id', ); }; subtest 'changing specification versions across documents' => sub { my $expected = [ re(qr!^\Qno-longer-supported "dependencies" keyword present (at location "https://iam.draft2019-09.com")!) ]; $expected = superbagof(@$expected) if not $ENV{AUTHOR_TESTING}; cmp_result( [ warnings { $js->add_schema({ '$id' => 'https://iam.draft2019-09.com', '$schema' => 'https://json-schema.org/draft/2019-09/schema', '$ref' => 'https://iam.draft7.com', dependencies => { foo => false }, dependentSchemas => { foo => false }, additionalProperties => { format => 'ipv6' }, }) } ], $expected, 'no unexpected warnings', ); $js->add_schema({ '$id' => 'https://iam.draft7.com', '$schema' => 'http://json-schema.org/draft-07/schema#', dependencies => { foo => false }, dependentSchemas => { foo => false }, additionalProperties => { format => 'ipv4' }, unevaluatedProperties => false, # this should be ignored }); cmp_result( $js->evaluate({ foo => 'hi' }, 'https://iam.draft2019-09.com')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/dependencies/foo', absoluteKeywordLocation => 'https://iam.draft7.com#/dependencies/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/$ref/dependencies', absoluteKeywordLocation => 'https://iam.draft7.com#/dependencies', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/$ref/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft7.com#/additionalProperties/format', error => 'not a valid ipv4 string', }, { instanceLocation => '', keywordLocation => '/$ref/additionalProperties', absoluteKeywordLocation => 'https://iam.draft7.com#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/dependentSchemas/foo', absoluteKeywordLocation => 'https://iam.draft2019-09.com#/dependentSchemas/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/dependentSchemas', absoluteKeywordLocation => 'https://iam.draft2019-09.com#/dependentSchemas', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft2019-09.com#/additionalProperties/format', error => 'not a valid ipv6 string', }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://iam.draft2019-09.com#/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'switching between specification versions is acceptable when crossing document boundaries', ); cmp_result( $js->{_resource_index}{'https://iam.draft2019-09.com'}, superhashof({ specification_version => 'draft2019-09', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'resources for top level schema', ); cmp_result( $js->{_resource_index}{'https://iam.draft7.com'}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'resources for subschema', ); $expected = [ re(qr!^\Qno-longer-supported "dependencies" keyword present (at location "https://iam.draft2020-12-2.com")!) ]; $expected = superbagof(@$expected) if not $ENV{AUTHOR_TESTING}; $js->add_schema({ '$id' => 'https://iam.draft7-2.com', '$schema' => 'http://json-schema.org/draft-07/schema#', allOf => [ { '$ref' => 'https://iam.draft2020-12-2.com' } ], dependencies => { foo => false }, dependentSchemas => { foo => false }, additionalProperties => { format => 'ipv4' }, unevaluatedProperties => false, # this should be ignored }); cmp_result( [ warnings { $js->add_schema({ '$id' => 'https://iam.draft2020-12-2.com', '$schema' => 'https://json-schema.org/draft/2020-12/schema', dependencies => { foo => false }, dependentSchemas => { foo => false }, additionalProperties => { format => 'ipv6' }, }) } ], $expected, 'no unexpected warnings', ); cmp_result( $js->evaluate({ foo => 'hi' }, 'https://iam.draft7-2.com')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/$ref/dependentSchemas/foo', absoluteKeywordLocation => 'https://iam.draft2020-12-2.com#/dependentSchemas/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf/0/$ref/dependentSchemas', absoluteKeywordLocation => 'https://iam.draft2020-12-2.com#/dependentSchemas', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/allOf/0/$ref/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft2020-12-2.com#/additionalProperties/format', error => 'not a valid ipv6 string', }, { instanceLocation => '', keywordLocation => '/allOf/0/$ref/additionalProperties', absoluteKeywordLocation => 'https://iam.draft2020-12-2.com#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://iam.draft7-2.com#/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '', keywordLocation => '/dependencies/foo', absoluteKeywordLocation => 'https://iam.draft7-2.com#/dependencies/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/dependencies', absoluteKeywordLocation => 'https://iam.draft7-2.com#/dependencies', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft7-2.com#/additionalProperties/format', error => 'not a valid ipv4 string', }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://iam.draft7-2.com#/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'switching between specification versions is acceptable when crossing document boundaries', ); cmp_result( $js->{_resource_index}{'https://iam.draft7-2.com'}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'resources for top level schema', ); cmp_result( $js->{_resource_index}{'https://iam.draft2020-12-2.com'}, superhashof({ specification_version => 'draft2020-12', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated) ], }), 'resources for subschema', ); }; subtest 'changing specification versions within documents' => sub { allow_warnings(1); cmp_result( $js->evaluate( { foo => 'hi' }, { '$id' => 'https://iam.draft2019-09-3.com', '$schema' => 'https://json-schema.org/draft/2019-09/schema', allOf => [ { '$id' => 'https://iam.draft7-3.com', '$schema' => 'http://json-schema.org/draft-07/schema#', dependencies => { foo => false }, dependentSchemas => 'blurp', # this should be ignored additionalProperties => { format => 'ipv4' }, unevaluatedProperties => 'blurp', # this should be ignored }, ], dependencies => 'blurp', # this should be ignored dependentSchemas => { foo => false }, additionalProperties => { format => 'ipv6' }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/dependencies/foo', absoluteKeywordLocation => 'https://iam.draft7-3.com#/dependencies/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf/0/dependencies', absoluteKeywordLocation => 'https://iam.draft7-3.com#/dependencies', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/allOf/0/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft7-3.com#/additionalProperties/format', error => 'not a valid ipv4 string', }, { instanceLocation => '', keywordLocation => '/allOf/0/additionalProperties', absoluteKeywordLocation => 'https://iam.draft7-3.com#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://iam.draft2019-09-3.com#/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '', keywordLocation => '/dependentSchemas/foo', absoluteKeywordLocation => 'https://iam.draft2019-09-3.com#/dependentSchemas/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/dependentSchemas', absoluteKeywordLocation => 'https://iam.draft2019-09-3.com#/dependentSchemas', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft2019-09-3.com#/additionalProperties/format', error => 'not a valid ipv6 string', }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://iam.draft2019-09-3.com#/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'switching between specification versions is acceptable within a document, draft2019-09 -> draft7', ); allow_warnings(0); cmp_result( $js->{_resource_index}{'https://iam.draft2019-09-3.com'}, superhashof({ specification_version => 'draft2019-09', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'resources for top level schema', ); cmp_result( $js->{_resource_index}{'https://iam.draft7-3.com'}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'resources for subschema', ); allow_warnings(1); cmp_result( $js->evaluate( { foo => 'hi' }, { '$id' => 'https://iam.draft7-4.com', '$schema' => 'http://json-schema.org/draft-07/schema#', allOf => [ { '$id' => 'https://iam.draft2020-12-4.com', '$schema' => 'https://json-schema.org/draft/2020-12/schema', dependencies => { foo => false }, # this should be ignored dependentSchemas => { foo => false }, additionalProperties => { format => 'ipv4' }, }, ], dependencies => { foo => false }, dependentSchemas => { foo => false }, # this should be ignored additionalProperties => { format => 'ipv6' }, unevaluatedProperties => false, # this should be ignored }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/dependentSchemas/foo', absoluteKeywordLocation => 'https://iam.draft2020-12-4.com#/dependentSchemas/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf/0/dependentSchemas', absoluteKeywordLocation => 'https://iam.draft2020-12-4.com#/dependentSchemas', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/allOf/0/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft2020-12-4.com#/additionalProperties/format', error => 'not a valid ipv4 string', }, { instanceLocation => '', keywordLocation => '/allOf/0/additionalProperties', absoluteKeywordLocation => 'https://iam.draft2020-12-4.com#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://iam.draft7-4.com#/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '', keywordLocation => '/dependencies/foo', absoluteKeywordLocation => 'https://iam.draft7-4.com#/dependencies/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/dependencies', absoluteKeywordLocation => 'https://iam.draft7-4.com#/dependencies', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft7-4.com#/additionalProperties/format', error => 'not a valid ipv6 string', }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://iam.draft7-4.com#/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'switching between specification versions is acceptable within a document, draft7 -> draf2020-12', ); allow_warnings(0); cmp_result( $js->{_resource_index}{'https://iam.draft7-4.com'}, superhashof({ specification_version => 'draft7', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), 'resources for top level schema', ); cmp_result( $js->{_resource_index}{'https://iam.draft2020-12-4.com'}, superhashof({ specification_version => 'draft2020-12', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated) ], }), 'resources for subschema', ); allow_warnings(1); cmp_result( $js->evaluate( { foo => 'hi' }, { '$id' => 'https://iam.draft2020-12-5.com', '$schema' => 'https://json-schema.org/draft/2020-12/schema', allOf => [ { id => 'https://iam.draft4-5.com', '$schema' => 'http://json-schema.org/draft-04/schema#', definitions => { blah => false }, dependencies => { foo => false }, dependentSchemas => { foo => false }, # this should be ignored allOf => [ { '$ref' => '#/definitions/blah' } ], additionalProperties => { format => 'ipv4' }, }, ], dependencies => { foo => false }, # this should be ignored dependentSchemas => { foo => false }, additionalProperties => { format => 'ipv6' }, unevaluatedProperties => false, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/allOf/0/$ref', absoluteKeywordLocation => 'https://iam.draft4-5.com#/definitions/blah', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf/0/allOf', absoluteKeywordLocation => 'https://iam.draft4-5.com#/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '', keywordLocation => '/allOf/0/dependencies/foo', absoluteKeywordLocation => 'https://iam.draft4-5.com#/dependencies/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf/0/dependencies', absoluteKeywordLocation => 'https://iam.draft4-5.com#/dependencies', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/allOf/0/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft4-5.com#/additionalProperties/format', error => 'not a valid ipv4 string', }, { instanceLocation => '', keywordLocation => '/allOf/0/additionalProperties', absoluteKeywordLocation => 'https://iam.draft4-5.com#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://iam.draft2020-12-5.com#/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '', keywordLocation => '/dependentSchemas/foo', absoluteKeywordLocation => 'https://iam.draft2020-12-5.com#/dependentSchemas/foo', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/dependentSchemas', absoluteKeywordLocation => 'https://iam.draft2020-12-5.com#/dependentSchemas', error => 'not all dependencies are satisfied', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/format', absoluteKeywordLocation => 'https://iam.draft2020-12-5.com#/additionalProperties/format', error => 'not a valid ipv6 string', }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://iam.draft2020-12-5.com#/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'switching between specification versions is acceptable within a document, draft2020-12 -> draft4', ); allow_warnings(0); cmp_result( $js->{_resource_index}{'https://iam.draft2020-12-5.com'}, superhashof({ specification_version => 'draft2020-12', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated) ], }), 'resources for top level schema', ); cmp_result( $js->{_resource_index}{'https://iam.draft4-5.com'}, superhashof({ specification_version => 'draft4', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator MetaData) ], }), 'resources for subschema', ); }; undef $js; subtest '$vocabulary syntax' => sub { cmp_result( JSON::Schema::Modern->new->evaluate( 1, { '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, '#/notauri' => false, 'https://foo' => 1, 'https://json-schema.org/draft/2019-09/vocab/validation' => true, 'https://json-schema.org/draft/2020-12/vocab/applicator' => true, 'https://unknown' => true, # ignored.. for now 'https://unknown2' => false, # "" }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$vocabulary/#~1notauri', error => '"#/notauri" is not a valid URI', }, { instanceLocation => '', keywordLocation => '/$vocabulary/https:~1~1foo', error => '$vocabulary value at "https://foo" is not a boolean', }, ], }, '$vocabulary syntax checks', ); cmp_result( JSON::Schema::Modern->new->evaluate( 1, { '$id' => 'http://mymetaschema', items => { '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/applicator' => true } }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/items/$vocabulary', absoluteKeywordLocation => 'http://mymetaschema#/items/$vocabulary', error => '$vocabulary can only appear at the schema resource root', }, ], }, '$vocabulary location check - resource root', ); cmp_result( JSON::Schema::Modern->new->evaluate( 1, { items => { '$id' => 'foobar', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true }, }, }, )->TO_JSON, { valid => true }, '$vocabulary location check - document root', ); my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( 1, { '$id' => 'http://mymetaschema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/applicator' => false, }, }, )->TO_JSON, { valid => true }, 'successfully evaluated a metaschema that specifies vocabularies', ); cmp_result( $js->{_resource_index}{'http://mymetaschema'}, { canonical_uri => str('http://mymetaschema'), path => '', specification_version => 'draft2020-12', document => ignore, vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated), ], configs => {}, }, 'metaschemas are not saved on the resource', ); cmp_result( $js->evaluate(1, { '$schema' => 'http://mymetaschema' })->TO_JSON, { valid => true }, '..but once we use the schema as a metaschema,', ); cmp_result( $js->{_metaschema_vocabulary_classes}{'http://mymetaschema'}, [ 'draft2020-12', [ 'JSON::Schema::Modern::Vocabulary::Core', 'JSON::Schema::Modern::Vocabulary::Applicator', ], ], '... the vocabulary information is now cached in the evaluator', ); }; subtest 'changing dialects (same specification version)' => sub { my $js = JSON::Schema::Modern->new(collect_annotations => 1); $js->add_schema({ '$id' => 'https://my_metaschema', '$schema' => 'https://json-schema.org/draft/2020-12/schema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/validation' => true, # no applicator! }, }); $js->add_schema({ '$id' => 'https://my_other_schema', '$schema' => 'https://my_metaschema', type => 'object', properties => { bar => false }, # this keyword should only annotate zeta => 1, }); cmp_result( $js->evaluate( { foo => { bar => 1 } }, { '$id' => 'https://example.com', additionalProperties => { '$ref' => 'https://my_other_schema', }, }, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '/foo', keywordLocation => '/additionalProperties/$ref/properties', absoluteKeywordLocation => 'https://my_other_schema#/properties', annotation => { bar => false }, }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/$ref/zeta', absoluteKeywordLocation => 'https://my_other_schema#/zeta', annotation => 1, }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://example.com#/additionalProperties', annotation => ['foo'], }, ], }, 'evaluation of the subschema in another document correctly uses the new $id and $schema', ); cmp_result( $js->evaluate( { foo => { bar => 1 } }, { '$id' => 'https://example2.com', '$defs' => { 'my_def' => { '$id' => 'https://my_other_schema2', '$schema' => 'https://my_metaschema', type => 'object', properties => { bar => false }, # this keyword should only annotate zeta => 1, }, }, additionalProperties => { '$ref' => 'https://my_other_schema2', }, }, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '/foo', keywordLocation => '/additionalProperties/$ref/properties', absoluteKeywordLocation => 'https://my_other_schema2#/properties', annotation => { bar => false }, }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/$ref/zeta', absoluteKeywordLocation => 'https://my_other_schema2#/zeta', annotation => 1, }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://example2.com#/additionalProperties', annotation => ['foo'], }, ], }, 'evaluation of the subschema in the same document via a $ref correctly uses the new $id and $schema', ); cmp_result( $js->evaluate( { foo => { bar => 1 } }, { '$id' => 'https://example3.com', additionalProperties => { '$id' => 'https://my_other_schema3', '$schema' => 'https://my_metaschema', type => 'object', properties => { bar => false }, # this keyword should only annotate zeta => 1, }, }, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '/foo', keywordLocation => '/additionalProperties/properties', absoluteKeywordLocation => 'https://my_other_schema3#/properties', annotation => { bar => false }, }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/zeta', absoluteKeywordLocation => 'https://my_other_schema3#/zeta', annotation => 1, }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://example3.com#/additionalProperties', annotation => ['foo'], }, ], }, 'evaluation of the subschema in the same document with no $ref correctly uses the new $id and $schema', ); cmp_result( $js->traverse({ '$id' => 'https://example4.com', additionalProperties => { '$id' => 'https://my_other_schema4', '$schema' => 'https://my_metaschema', type => 'object', properties => 1, # this is not a real keyword as the assertion vocabulary is not present }, }), superhashof({ errors => [] }), 'no errors found when traversing a document with a malformed keyword outside the dialect', ); }; subtest 'standard metaschemas' => sub { my $js = JSON::Schema::Modern->new; my ($draft202012_metaschema) = $js->get('https://json-schema.org/draft/2020-12/schema'); cmp_result( $js->evaluate($draft202012_metaschema, 'https://json-schema.org/draft/2020-12/schema')->TO_JSON, { valid => true }, 'main metaschema evaluated against its own URI', ); cmp_result( $js->evaluate($draft202012_metaschema, $draft202012_metaschema)->TO_JSON, { valid => true }, 'main metaschema evaluated against its own content', ); my ($draft202012_core_metaschema) = $js->get('https://json-schema.org/draft/2020-12/meta/core'); cmp_result( $js->evaluate($draft202012_core_metaschema, 'https://json-schema.org/draft/2020-12/schema')->TO_JSON, { valid => true }, 'core metaschema evaluated against the main metaschema URI', ); cmp_result( $js->evaluate($draft202012_core_metaschema, $draft202012_core_metaschema)->TO_JSON, { valid => true }, 'core metaschema evaluated against its own content', ); }; subtest 'custom metaschemas, without custom vocabularies' => sub { my $js = JSON::Schema::Modern->new; my $metaschema_document = $js->add_schema(my $metaschema = { '$id' => 'http://localhost:1234/my-meta-schema', '$schema' => 'https://json-schema.org/draft/2019-09/schema', type => 'object', '$recursiveAnchor' => true, allOf => [ { '$ref' => 'https://json-schema.org/draft/2019-09/schema' } ], }); is($metaschema_document->_get_resource($metaschema->{'$id'})->{specification_version}, 'draft2019-09', 'specification version detected from standard metaschema URI'); cmp_result( $js->evaluate(false, 'http://localhost:1234/my-meta-schema')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/type', absoluteKeywordLocation => 'http://localhost:1234/my-meta-schema#/type', error => 'got boolean, not object', }, ], }, 'custom metaschema restricts schemas to objects', ); # the evaluation of $recursiveAnchor in the schema proves that the proper specification version # was detected via the $schema keyword cmp_result( $js->evaluate( { allOf => [ false ] }, 'http://localhost:1234/my-meta-schema', )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/allOf/0', keywordLocation => '/allOf/0/$ref/allOf/1/$ref/properties/allOf/$ref/items/$recursiveRef/type', absoluteKeywordLocation => 'http://localhost:1234/my-meta-schema#/type', error => 'got boolean, not object', }, { instanceLocation => '/allOf', keywordLocation => '/allOf/0/$ref/allOf/1/$ref/properties/allOf/$ref/items', absoluteKeywordLocation => 'https://json-schema.org/draft/2019-09/meta/applicator#/$defs/schemaArray/items', error => 'subschema is not valid against all items', }, { instanceLocation => '', keywordLocation => '/allOf/0/$ref/allOf/1/$ref/properties', absoluteKeywordLocation => 'https://json-schema.org/draft/2019-09/meta/applicator#/properties', error => 'not all properties are valid', }, { instanceLocation => '', keywordLocation => '/allOf/0/$ref/allOf', absoluteKeywordLocation => 'https://json-schema.org/draft/2019-09/schema#/allOf', error => 'subschema 1 is not valid', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'http://localhost:1234/my-meta-schema#/allOf', error => 'subschema 0 is not valid', }, ], }, 'custom metaschema recurses to standard metaschema', ); cmp_result( $js->evaluate({ allOf => [ {} ] }, 'http://localhost:1234/my-meta-schema')->TO_JSON, { valid => true }, 'objects are acceptable schemas to this metaschema', ); cmp_result( $js->evaluate( 1, { '$id' => 'https://localhost:1234/my-schema', '$schema' => 'http://localhost:1234/my-meta-schema', }, )->TO_JSON, { valid => true }, 'metaschemas without $vocabulary can still be used in the $schema keyword', ); cmp_result( $js->{_resource_index}{'https://localhost:1234/my-schema'}, superhashof({ specification_version => 'draft2019-09', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData) ], }), '..and schema uses the correct spec version and vocabularies', ); }; subtest 'custom metaschemas, with custom vocabularies' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate(1, { '$schema' => 'https://unknown/metaschema' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema', error => 'EXCEPTION: unable to find resource "https://unknown/metaschema"', }, ], }, 'custom metaschemas are okay, but the document must be known', ); $js->add_schema({ '$id' => 'https://metaschema/with/misplaced/vocabulary/keyword/base', items => { '$id' => 'subschema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true }, }, }); cmp_result( $js->evaluate(1, { '$schema' => 'https://metaschema/with/misplaced/vocabulary/keyword/subschema' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema/$vocabulary', absoluteKeywordLocation => 'https://metaschema/with/misplaced/vocabulary/keyword/subschema#/$vocabulary', error => '$vocabulary can only appear at the document root', }, { instanceLocation => '', keywordLocation => '/$schema', error => '"https://metaschema/with/misplaced/vocabulary/keyword/subschema" is not a valid metaschema', }, ], }, '$vocabulary location check - document root', ); $js->add_schema('https://metaschema/with/no/id', { '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true } }); cmp_result( $js->evaluate(1, { '$schema' => 'https://metaschema/with/no/id' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema/$vocabulary', absoluteKeywordLocation => 'https://metaschema/with/no/id#/$vocabulary', error => 'metaschemas must have an $id', }, { instanceLocation => '', keywordLocation => '/$schema', error => '"https://metaschema/with/no/id" is not a valid metaschema', }, ], }, 'metaschemas must have an i$id', ); $js->add_schema({ '$id' => 'https://metaschema/with/wrong/spec', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2019-09/vocab/validation' => true, 'https://json-schema.org/draft/2020-12/vocab/applicator' => true, 'https://unknown' => true, 'https://unknown2' => false, }, }); cmp_result( $js->evaluate(1, { '$schema' => 'https://metaschema/with/wrong/spec' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema/$vocabulary/https:~1~1json-schema.org~1draft~12019-09~1vocab~1validation', absoluteKeywordLocation => 'https://metaschema/with/wrong/spec#/$vocabulary/https:~1~1json-schema.org~1draft~12019-09~1vocab~1validation', error => '"https://json-schema.org/draft/2019-09/vocab/validation" uses draft2019-09, but the metaschema itself uses draft2020-12', }, { instanceLocation => '', keywordLocation => '/$schema/$vocabulary/https:~1~1unknown', absoluteKeywordLocation => 'https://metaschema/with/wrong/spec#/$vocabulary/https:~1~1unknown', error => '"https://unknown" is not a known vocabulary', }, { instanceLocation => '', keywordLocation => '/$schema', error => '"https://metaschema/with/wrong/spec" is not a valid metaschema', }, ], }, '$vocabulary validation that must be deferred until used as a metaschema', ); $js->add_schema({ '$id' => 'https://metaschema/missing/vocabs', '$vocabulary' => {}, }); cmp_result( $js->evaluate(1, { '$schema' => 'https://metaschema/missing/vocabs' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema/$vocabulary', absoluteKeywordLocation => 'https://metaschema/missing/vocabs#/$vocabulary', error => 'the first vocabulary (by evaluation_order) must be Core', }, { instanceLocation => '', keywordLocation => '/$schema', error => '"https://metaschema/missing/vocabs" is not a valid metaschema', }, ], }, 'metaschemas using "$vocabulary" must contain vocabularies', ); $js->add_schema({ '$id' => 'https://metaschema/missing/core', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/applicator' => true, }, }); cmp_result( $js->evaluate(1, { '$schema' => 'https://metaschema/missing/core' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema/$vocabulary', absoluteKeywordLocation => 'https://metaschema/missing/core#/$vocabulary', error => 'the first vocabulary (by evaluation_order) must be Core', }, { instanceLocation => '', keywordLocation => '/$schema', error => '"https://metaschema/missing/core" is not a valid metaschema', }, ], }, 'metaschemas must contain the Core vocabulary', ); $js->add_schema({ '$id' => 'https://my/first/metaschema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/applicator' => true, 'https://json-schema.org/draft/2020-12/vocab/core' => true, # note: no validation! }, }); cmp_result( $js->evaluate( 1, { '$id' => my $id = 'https://my/first/schema/with/custom/metaschema', '$schema' => 'https://my/first/metaschema', minimum => 10, }, )->TO_JSON, { valid => true }, 'validation succeeds because "minimum" never gets run', ); cmp_result( $js->{_resource_index}{$id}, { canonical_uri => str($id), path => '', specification_version => 'draft2020-12', document => ignore, vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Applicator), ], configs => {}, }, 'determined vocabularies to use for this schema', ); }; subtest 'custom vocabulary classes with add_vocabulary()' => sub { my $js = JSON::Schema::Modern->new; like( exception { $js->add_vocabulary('MyVocabulary::Does::Not::Exist') }, qr!an't locate MyVocabulary/Does/Not/Exist.pm in \@INC!, 'vocabulary class must exist', ); like( exception { $js->add_vocabulary('MyVocabulary::MissingRole') }, qr/Value "MyVocabulary::MissingRole" did not pass type constraint/, 'vocabulary class must implement the role', ); like( exception { $js->add_vocabulary('MyVocabulary::MissingSub') }, qr/Can't apply JSON::Schema::Modern::Vocabulary to MyVocabulary::MissingSub - missing vocabulary, keywords/, 'vocabulary class must implement some subs', ); cmp_result( [ warnings { like( exception { $js->add_vocabulary('MyVocabulary::BadVocabularySub1') }, qr/Undef did not pass type constraint/, 'vocabulary() sub in the vocabulary class must return uri => specification_version pairs', ) } ], [ re(qr/Odd number of elements in pairs/) ], 'parse error from bad vocab sub', ); like( exception { $js->add_vocabulary('MyVocabulary::BadVocabularySub2') }, qr!Value "https://some/uri#/invalid/uri" did not pass type constraint!, 'vocabulary() sub in the vocabulary class must contain valid absolute, fragmentless URIs', ); like( exception { $js->add_vocabulary('MyVocabulary::BadVocabularySub3') }, qr/Value "wrongdraft" did not pass type constraint/, 'vocabulary() sub in the vocabulary class must reference a known specification version', ); is( exception { $js->add_vocabulary('MyVocabulary::BadEvaluationOrder') }, undef, 'added a vocabulary sub', ); cmp_result( $js->{_vocabulary_classes}, superhashof({ 'https://vocabulary/with/bad/evaluation/order' => [ 'draft2020-12', 'MyVocabulary::BadEvaluationOrder' ] }), 'vocabulary was successfully added', ); $js->add_schema({ '$id' => 'https://my/first/metaschema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/validation' => true, 'https://vocabulary/with/bad/evaluation/order' => true, }, }); cmp_result( $js->evaluate(1, { '$schema' => 'https://my/first/metaschema' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema/$vocabulary', absoluteKeywordLocation => 'https://my/first/metaschema#/$vocabulary', error => 'JSON::Schema::Modern::Vocabulary::Validation and MyVocabulary::BadEvaluationOrder have a conflicting evaluation_order', }, { instanceLocation => '', keywordLocation => '/$schema', error => '"https://my/first/metaschema" is not a valid metaschema', }, ], }, 'custom vocabulary class has a conflicting evaluation_order', ); is( exception { $js->add_vocabulary('MyVocabulary::StringComparison') }, undef, 'added another vocabulary sub', ); $js->add_schema({ '$id' => 'https://my/first/working/metaschema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://vocabulary/string/comparison' => true, }, }); cmp_result( $js->evaluate( 'bloop', { '$id' => 'https://my/first/schema/with/custom/metaschema', '$schema' => 'https://my/first/working/metaschema', stringLessThan => 'alpha', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/stringLessThan', absoluteKeywordLocation => 'https://my/first/schema/with/custom/metaschema#/stringLessThan', error => 'value is not stringwise less than alpha', }, ], }, 'custom vocabulary class used by a custom metaschema used by a schema', ); }; subtest '$schema points to a boolean schema' => sub { my $js = JSON::Schema::Modern->new; $js->add_schema('https://my_boolean_schema' => true); cmp_result( my $result = $js->evaluate( 1, { '$id' => '/foo', '$schema' => 'https://my_boolean_schema', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema', # we haven't processed $id yet, so we don't know the absolute location error => 'metaschemas must be objects', }, { instanceLocation => '', keywordLocation => '/$schema', error => '"https://my_boolean_schema" is not a valid metaschema', }, ], }, '$schema cannot reference a boolean schema', ); }; had_no_warnings() if $ENV{AUTHOR_TESTING}; done_testing; JSON-Schema-Modern-0.609/t/boolean-data.t000640 000766 000024 00000011555 15001302761 020150 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Data::Dumper; use lib 't/lib'; use Helper; sub serialize { Data::Dumper->new([ $_[0] ])->Indent(0)->Terse(1)->Sortkeys(1)->Dump } my ($test_schema, $failure_result); subtest 'strict booleans (default)' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate($_, { type => 'boolean' })->TO_JSON, { valid => true }, 'in data, '.serialize($_).' is a boolean', ) foreach ( false, true, ); cmp_result( $js->evaluate($_->[1], { type => 'boolean' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/type', error => 'got '.$_->[0].', not boolean', }, ], }, 'correct error generated from type for '.serialize($_->[1]), ) foreach ( [ null => undef ], [ integer => 0 ], [ integer => 1 ], [ string => '0' ], [ string => '1' ], [ string => 'false' ], [ string => 'true' ], [ 'reference to SCALAR' => \0 ], [ 'reference to SCALAR' => \1 ], ); cmp_result( $js->evaluate( $_->[1], $test_schema = { allOf => [ { type => 'boolean' }, { type => ['boolean','object'] } ], anyOf => [ { const => false }, { const => true } ], enum => [ false, true ], } )->TO_JSON, $failure_result = { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/enum', error => 'value does not match', }, { instanceLocation => '', keywordLocation => '/allOf/0/type', error => 'got '.$_->[0].', not boolean', }, { instanceLocation => '', keywordLocation => '/allOf/1/type', error => 'got '.$_->[0].', not one of boolean, object', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschemas 0, 1 are not valid', }, { instanceLocation => '', keywordLocation => '/anyOf/0/const', error => 'value does not match', }, { instanceLocation => '', keywordLocation => '/anyOf/1/const', error => 'value does not match', }, { instanceLocation => '', keywordLocation => '/anyOf', error => 'no subschemas are valid', }, ], }, 'in data, '.serialize($_->[1]).' not is a boolean', ) foreach ( [ null => undef ], [ integer => 0 ], [ integer => 1 ], [ string => '0' ], [ string => '1' ], [ string => 'false' ], [ string => 'true' ], [ 'reference to SCALAR' => \0 ], [ 'reference to SCALAR' => \1 ], ); }; subtest 'scalarref_booleans = 1' => sub { my $js = JSON::Schema::Modern->new(scalarref_booleans => 1); cmp_result( $js->evaluate($_, $test_schema)->TO_JSON, { valid => true }, 'in data, '.serialize($_).' is a boolean', ) foreach ( false, true, \0, \1, ); cmp_result( $js->evaluate($_->[1], $test_schema)->TO_JSON, { valid => false, errors => [ do { my ($type, $value) = $_->@*; map +{ $_->%*, $_->{keywordLocation} =~ /\/type$/ ? (error => $_->{error} =~ s/^got .*, not/got $type, not/r) : (), }, $failure_result->{errors}->@*, }, ], }, 'correct error generated from type for '.serialize($_), ) foreach ( [ null => undef ], [ integer => 0 ], [ integer => 1 ], [ string => '0' ], [ string => '1' ], [ string => 'false' ], [ string => 'true' ], ); cmp_result( $js->evaluate( [ undef, 0, 1, '0', '1', 'false', 'true', \0, \1, ], { uniqueItems => true }, )->TO_JSON, { valid => true }, 'items are all considered unique when types differ, even when perl treats them similarly', ); cmp_result( $js->evaluate($_, { uniqueItems => true })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/uniqueItems', error => 'items at indices 0 and 1 are not unique', }, ], }, 'scalarrefs compare as identical to their counterpart booleans', ) foreach ( [ \0, false ], [ false, \0 ], [ \1, true ], [ true, \1 ], ); }; done_testing; JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12.t000640 000766 000024 00000003524 15001302761 022711 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Warnings 'warnings', ':no_end_test'; use lib 't/lib'; use Helper; use Acceptance; my $version = 'draft2020-12'; my @warnings = warnings { acceptance_tests( acceptance => { specification => $version, test_dir => 't/additional-tests-'.$version, }, evaluator => { specification_version => $version, validate_formats => 1, collect_annotations => 0, }, output_file => $version.'-additional-tests.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ # these all depend on optional prereqs !eval { require Time::Moment; 1 } ? map "optional/format-$_.json", qw(date-time date time) : (), !eval { require DateTime::Format::RFC3339; 1 } ? 'optional/format-date-time.json' : (), ] }, ] ), }, ); }; my $test_sub = $ENV{AUTHOR_TESTING} ? sub { bag(@_) } : sub { superbagof(@_) }; cmp_result( \@warnings, $test_sub->( # these are all in unknownKeyword.json map +( ( re(qr/^no-longer-supported "\Q$_\E" keyword present/) ) x (4 * ($ENV{NO_SHORT_CIRCUIT} ? 1 : 2)), ), qw(dependencies id additionalItems $recursiveAnchor $recursiveRef), ), 'got unsupported keyword warnings'.($ENV{AUTHOR_TESTING} ? '; no unexpected warnings' : ''), ); done_testing; __END__ see t/results/draft2020-12-additional-tests.txt for test results JSON-Schema-Modern-0.609/t/invalid-schemas/000770 000766 000024 00000000000 15001302761 020476 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/type.t000640 000766 000024 00000025765 15001302761 016613 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use utf8; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Fatal; use Scalar::Util qw(isdual dualvar); use JSON::Schema::Modern::Utilities qw(is_type get_type); use Math::BigInt; use Math::BigFloat; use lib 't/lib'; use Helper; my %inflated_data = ( null => [ undef ], boolean => [ false, true ], object => [ {}, { a => 1 } ], array => [ [], [ 1 ] ], number => [ 3.1, 1.23456789012e10, Math::BigFloat->new('0.123') ], integer => [ 0, -1, 2, 2.0, 2**31-1, 2**31, 2**63-1, 2**63, 2**64, 2**65, 1000000000000000, Math::BigInt->new('1e20'), Math::BigInt->new('1'), Math::BigInt->new('1.0'), Math::BigFloat->new('2e1'), Math::BigFloat->new('1'), Math::BigFloat->new('1.0') ], string => [ '', '0', '-1', '2', '2.0', '3.1', 'école', 'ಠ_ಠ' ], ); my %json_data = ( null => [ 'null' ], boolean => [ 'false', 'true' ], object => [ '{}', '{"a":1}' ], array => [ '[]', '[1]' ], number => [ '3.1', '1.23456789012e10' ], integer => [ '0', '-1', '2.0', (map $_.'', 2**31-1, 2**31, 2**63-1, 2**63, 2**64, 2**65), '1000000000000000', '2e1' ], string => [ '""', '"0"', '"-1"', '"2.0"', '"3.1"', qq{"\x{c3}\x{a9}cole"}, qq{"\x{e0}\x{b2}\x{a0}_\x{e0}\x{b2}\x{a0}"} ], ); foreach my $type (sort keys %inflated_data) { subtest 'inflated data, type: '.$type => sub { foreach my $value ($inflated_data{$type}->@*) { my $value_copy = $value; ok(is_type($type, $value), json_sprintf(('is_type("'.$type.'", %s) is true'), $value_copy )); ok(is_type('number', $value), json_sprintf(('is_type("number", %s) is true'), $value_copy )) if $type eq 'integer'; is(get_type($value), $type, json_sprintf(('get_type(%s) = '.$type), $value_copy)); foreach my $other_type (sort keys %inflated_data) { next if $other_type eq $type; next if $type eq 'integer' and $other_type eq 'number'; ok(!is_type($other_type, $value), json_sprintf('is_type("'.$other_type.'", %s) is false', $value)); } ok(!isdual($value), 'data is not tampered with while it is tested (not dualvar)'); } }; } my $decoder = JSON::Schema::Modern::_JSON_BACKEND()->new ->allow_nonref(1) ->canonical(1) ->utf8(1) ->allow_bignum(1); foreach my $type (sort keys %json_data) { subtest 'JSON-encoded data, type: '.$type => sub { foreach my $value ($json_data{$type}->@*) { $value = $decoder->decode($value); my $value_copy = $value; ok(is_type($type, $value), json_sprintf(('is_type("'.$type.'", %s) is true'), $value_copy )); ok(is_type('number', $value), json_sprintf(('is_type("number", %s) is true'), $value_copy )) if $type eq 'integer'; is(get_type($value), $type, json_sprintf(('get_type(%s) = '.$type), $value_copy)); foreach my $other_type (sort keys %json_data) { next if $other_type eq $type; next if $type eq 'integer' and $other_type eq 'number'; ok(!is_type($other_type, $value), json_sprintf('is_type("'.$other_type.'", %s) is false', $value)); } ok(!isdual($value), 'data is not tampered with while it is tested (not dualvar)'); } }; } my %draft4_inflated_data = ( number => [ 3.1, 1.23456789012e10, Math::BigFloat->new('0.123'), 2.0 ], integer => [ 0, -1, 2, Math::BigInt->new('2'), Math::BigInt->new('1.0') ], ); my %draft4_json_data = ( number => [ '3.1', '1.23456789012e10' ], integer => [ '0', '-1', '3' ], ); subtest 'integers and numbers in draft4' => sub { foreach my $type (sort keys %draft4_inflated_data) { foreach my $value ($draft4_inflated_data{$type}->@*) { my $value_copy = $value; ok(is_type($type, $value, { legacy_ints => 1 }), json_sprintf(('is_type("'.$type.'", %s) is true'), $value_copy )); ok(is_type('number', $value, { legacy_ints => 1 }), json_sprintf(('is_type("number", %s) is true'), $value_copy )) if $type eq 'integer'; is(get_type($value, { legacy_ints => 1 }), $type, json_sprintf(('get_type(%s) = '.$type), $value_copy)); foreach my $other_type (qw(null boolean object array string), sort keys %draft4_inflated_data) { next if $other_type eq $type; next if $type eq 'integer' and $other_type eq 'number'; ok(!is_type($other_type, $value, { legacy_ints => 1 }), json_sprintf('is_type("'.$other_type.'", %s) is false', $value)); } ok(!isdual($value), 'data is not tampered with while it is tested (not dualvar)'); } foreach my $value ($draft4_json_data{$type}->@*) { $value = $decoder->decode($value); my $value_copy = $value; ok(is_type($type, $value, { legacy_ints => 1 }), json_sprintf(('is_type("'.$type.'", %s) is true'), $value_copy )); ok(is_type('number', $value, { legacy_ints => 1 }), json_sprintf(('is_type("number", %s) is true'), $value_copy )) if $type eq 'integer'; is(get_type($value, { legacy_ints => 1 }), $type, json_sprintf(('get_type(%s) = '.$type), $value_copy)); foreach my $other_type (qw(null boolean object array string), sort keys %draft4_json_data) { next if $other_type eq $type; next if $type eq 'integer' and $other_type eq 'number'; ok(!is_type($other_type, $value, { legacy_ints => 1 }), json_sprintf('is_type("'.$other_type.'", %s) is false', $value)); } ok(!isdual($value), 'data is not tampered with while it is tested (not dualvar)'); } } }; ok(!is_type('foo', 'wharbarbl'), 'non-existent type does not result in exception'); subtest 'ambiguous types' => sub { subtest 'integers' => sub { my $integer = dualvar(5, 'five'); is(get_type($integer), 'ambiguous type', 'dualvar integers with different values are ambiguous'); ok(!is_type($_, $integer), "dualvar integers with different values are not ${_}s") foreach qw(integer number string); $integer = 5; ()= sprintf('%s', $integer); # legacy behaviour (this only happens for IVs, not NVs) SKIP: { skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1 if "$]" >= 5.035009; is(get_type($integer), 'string', 'older perls only: integer that is later used as a string is now identified as a string'); ok(is_type('integer', $integer), 'integer that is later used as a string is still an integer'); ok(is_type('number', $integer), 'integer that is later used as a string is still a number'); ok(is_type('string', $integer), 'older perls only: integer that is later used as a string is now a string'); } # modern behaviour SKIP: { skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1 if "$]" < 5.035009; is(get_type($integer), 'integer', 'integer that is later used as a string is still identified as a integer'); ok(is_type('integer', $integer), 'integer that is later used as a string is still an integer'); ok(is_type('number', $integer), 'integer that is later used as a string is still a number'); ok(!is_type('string', $integer), 'integer that is later used as a string is not a string'); } }; subtest 'numbers' => sub { my $number = dualvar(5.1, 'five'); is(get_type($number), 'ambiguous type', 'dualvar numbers are ambiguous in get_type'); ok(!is_type($_, $number), "dualvar numbers are not ${_}s") foreach qw(integer number string); $number = 5.1; ()= sprintf('%s', $number); is(get_type($number), 'number', 'number that is later used as a string is still identified as a number'); ok(!is_type('integer', $number), 'number that is later used as a string is not an integer'); ok(is_type('number', $number), 'number that is later used as a string is still a number'); ok(!is_type('string', $number), 'number that is later used as a string is not a string'); }; subtest 'strings' => sub { my $string = dualvar(5.1, 'five'); is(get_type($string), 'ambiguous type', 'dualvar strings are ambiguous in get_type'); ok(!is_type($_, $string), "dualvar strings are not ${_}s") foreach qw(integer number string); $string = '5'; ()= 0+$string; is(get_type($string), 'string', 'string that is later used as an integer is still identified as a string'); # legacy behaviour SKIP: { skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1 if "$]" >= 5.035009; ok(is_type('integer', $string), 'older perls only: string that is later used as an integer becomes an integer'); ok(is_type('number', $string), 'older perls only: string that is later used as an integer becomes a number'); } # modern behaviour SKIP: { skip 'on perls < 5.35.9, reading the integer form of a string value sets the flag SVf_IOK', 1 if "$]" < 5.035009; ok(!is_type('integer', $string), 'string that is later used as an integer is not an integer'); ok(!is_type('number', $string), 'string that is later used as an integer is not a number'); } ok(is_type('string', $string), 'string that is later used as an integer is still a string'); $string = '5.1'; ()= 0+$string; is(get_type($string), 'string', 'string that is later used as a number is still identified as a string'); ok(!is_type('integer', $string), 'string that is later used as a number is not an integer'); # legacy behaviour SKIP: { skip 'on perls < 5.35.9, reading the string form of an integer value sets the flag SVf_POK', 1 if "$]" >= 5.035009; ok(is_type('number', $string), 'older perls only: string that is later used as a number becomes a number'); } # modern behaviour SKIP: { skip 'on perls < 5.35.9, reading the numeric form of a string value sets the flag SVf_NOK', 1 if "$]" < 5.035009; ok(!is_type('number', $string), 'string that is later used as a number is not a number'); } ok(is_type('string', $string), 'string that is later used as a number is still a string'); }; }; subtest 'is_type and get_type for references' => sub { foreach my $test ( [ \1, 'reference to SCALAR' ], [ \\2, 'reference to REF' ], [ sub { 1 }, 'reference to CODE' ], [ \*stdout, 'reference to GLOB' ], [ \substr('a', '1'), 'reference to LVALUE' ], [ \v1.2.3, 'reference to VSTRING' ], [ qr/foo/, 'Regexp' ], [ *STDIN{IO}, 'IO::File' ], [ bless({}, 'Foo'), 'Foo' ], [ bless({}, '0'), '0' ], ) { is(get_type($test->[0]), $test->[1], $test->[1].' type is reported without exception'); ok(is_type($test->[1], $test->[0]), 'value is a '.$test->[1]); foreach my $type (qw(null object array boolean string number integer)) { ok(!is_type($type, $test->[0]), 'value is not a '.$type); } } }; done_testing; JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09.t000640 000766 000024 00000003441 15001302761 022725 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Warnings 'warnings', ':no_end_test'; use lib 't/lib'; use Helper; use Acceptance; my $version = 'draft2019-09'; my @warnings = warnings { acceptance_tests( acceptance => { specification => $version, test_dir => 't/additional-tests-'.$version, }, evaluator => { specification_version => $version, validate_formats => 1, collect_annotations => 0, }, output_file => $version.'-additional-tests.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ # these all depend on optional prereqs !eval { require Time::Moment; 1 } ? map "optional/format-$_.json", qw(date-time date time) : (), !eval { require DateTime::Format::RFC3339; 1 } ? 'optional/format-date-time.json' : (), ] }, ] ), }, ); }; my $test_sub = $ENV{AUTHOR_TESTING} ? sub { bag(@_) } : sub { superbagof(@_) }; cmp_result( \@warnings, $test_sub->( # these are all in unknownKeyword.json map +( ( re(qr/^no-longer-supported "$_" keyword present/) ) x (4 * ($ENV{NO_SHORT_CIRCUIT} ? 1 : 2)), ), qw(dependencies id), ), 'got unsupported keyword warnings'.($ENV{AUTHOR_TESTING} ? '; no unexpected warnings' : ''), ); done_testing; __END__ see t/results/draft2019-09-additional-tests.txt for test results JSON-Schema-Modern-0.609/t/zzz-acceptance-draft2019-09-format.t000640 000766 000024 00000007203 15001302761 023664 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Needs; use List::Util 1.50 'head'; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } if ($ENV{EXTENDED_TESTING}) { test_needs { 'Time::Moment' => 0, 'DateTime::Format::RFC3339' => 0, 'Email::Address::XS' => '1.04', 'Data::Validate::Domain' => 0.13, 'Net::IDN::Encode' => 0, }; } if ($ENV{AUTHOR_TESTING}) { eval { require Time::Moment; 1 } or fail $@; eval { require DateTime::Format::RFC3339; 1 } or fail $@; eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } or fail $@; eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } or fail $@; eval { require Net::IDN::Encode; 1 } or fail $@; } my $version = 'draft2019-09'; acceptance_tests( acceptance => { specification => $version, test_subdir => 'optional/format', }, evaluator => { specification_version => $version, validate_formats => 1, # not the default for the Format vocabulary for this draft collect_annotations => 0, }, output_file => $version.'-acceptance-format.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ 'iri-reference.json', # all strings are considered valid 'uri-template.json', # not yet implemented # these all depend on optional prereqs !$ENV{AUTHOR_TESTING} && !eval { require Time::Moment; 1 } ? qw(date-time.json date.json) : (), !$ENV{AUTHOR_TESTING} && !eval { require DateTime::Format::RFC3339; 1 } ? 'date-time.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } ? qw(email.json idn-email.json) : (), !$ENV{AUTHOR_TESTING} && !eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } ? 'hostname.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Net::IDN::Encode; 1 } ? 'idn-hostname.json' : (), ] }, # various edge cases that are difficult to accomodate { file => 'iri.json', group_description => 'validation of IRIs', # see test suite issue 395 test_description => 'an invalid IRI based on IPv6' }, { file => 'idn-hostname.json', group_description => 'validation of internationalized host names' }, # IDN decoder, Data::Validate::Domain both have issues { file => 'uri.json', test_description => 'validation of URIs', test_description => 'an invalid URI with comma in scheme' }, # Mojo::URL does not fully validate ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft2019-09-acceptance-format.txt for test results JSON-Schema-Modern-0.609/t/output_format.t000640 000766 000024 00000044077 15001302761 020537 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Fatal; use builtin::compat 'refaddr'; use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new(short_circuit => 0, collect_annotations => 1); is($js->output_format, 'basic', 'output_format defaults to basic'); my $result = $js->evaluate( { alpha => 1, beta => 1, foo => 1, gamma => [ 0, 1 ], theta => [ 1 ], zulu => 2 }, { required => [ 'bar' ], allOf => [ { type => 'number' }, { oneOf => [ { type => 'number' } ] }, { oneOf => [ true, true ] }, ], anyOf => [ { type => 'number' }, { if => true, then => { type => 'array' }, else => false } ], if => false, then => false, else => { type => 'number' }, not => true, properties => { alpha => false, beta => { multipleOf => 2 }, gamma => { prefixItems => [ false ], items => false, unevaluatedItems => false, }, theta => { items => false }, }, patternProperties => { 'o' => false }, additionalProperties => false, unevaluatedProperties => false, propertyNames => { pattern => '[ao]' }, }, ); is($result->output_format, 'basic', 'Result object gets the output_format from the evaluator'); cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/required', error => 'object is missing property: bar', }, { instanceLocation => '', keywordLocation => '/allOf/0/type', error => 'got object, not number', }, { instanceLocation => '', keywordLocation => '/allOf/1/oneOf/0/type', error => 'got object, not number', }, { instanceLocation => '', keywordLocation => '/allOf/1/oneOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/allOf/2/oneOf', error => 'multiple subschemas are valid: 0, 1', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschemas 0, 1, 2 are not valid', }, { instanceLocation => '', keywordLocation => '/anyOf/0/type', error => 'got object, not number', }, { instanceLocation => '', keywordLocation => '/anyOf/1/then/type', error => 'got object, not array', }, { instanceLocation => '', keywordLocation => '/anyOf/1/then', error => 'subschema is not valid', }, { instanceLocation => '', keywordLocation => '/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/not', error => 'subschema is true', }, { instanceLocation => '', keywordLocation => '/else/type', error => 'got object, not number', }, { instanceLocation => '', keywordLocation => '/else', error => 'subschema is not valid', }, { instanceLocation => '/alpha', keywordLocation => '/properties/alpha', error => 'property not permitted', }, { instanceLocation => '/beta', keywordLocation => '/properties/beta/multipleOf', error => 'value is not a multiple of 2', }, { instanceLocation => '/gamma/0', keywordLocation => '/properties/gamma/prefixItems/0', error => 'item not permitted', }, { instanceLocation => '/gamma', keywordLocation => '/properties/gamma/prefixItems', error => 'not all items are valid', }, { instanceLocation => '/gamma/1', keywordLocation => '/properties/gamma/items', error => 'additional item not permitted', }, { instanceLocation => '/gamma', keywordLocation => '/properties/gamma/items', error => 'subschema is not valid against all items', }, { instanceLocation => '/theta/0', keywordLocation => '/properties/theta/items', error => 'item not permitted', }, { instanceLocation => '/theta', keywordLocation => '/properties/theta/items', error => 'subschema is not valid against all items', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, { instanceLocation => '/foo', keywordLocation => '/patternProperties/o', error => 'property not permitted', }, { instanceLocation => '', keywordLocation => '/patternProperties', error => 'not all properties are valid', }, { instanceLocation => '/zulu', keywordLocation => '/additionalProperties', error => 'additional property not permitted', }, { instanceLocation => '', keywordLocation => '/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '/zulu', keywordLocation => '/propertyNames/pattern', error => 'pattern does not match', }, { instanceLocation => '', keywordLocation => '/propertyNames', error => 'not all property names are valid', }, ], }, 'basic format includes all errors linearly', ); $result->output_format('flag'); cmp_result( $result->TO_JSON, { valid => false, }, 'flag format only includes the valid property', ); $result->output_format('terse'); cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/required', error => 'object is missing property: bar', }, { instanceLocation => '', keywordLocation => '/allOf/0/type', error => 'got object, not number', }, { instanceLocation => '', keywordLocation => '/allOf/1/oneOf/0/type', error => 'got object, not number', }, # - "summary" error from /allOf/1/oneOf is omitted { instanceLocation => '', keywordLocation => '/allOf/2/oneOf', error => 'multiple subschemas are valid: 0, 1', }, # - "summary" error from /allOf is omitted { instanceLocation => '', keywordLocation => '/anyOf/0/type', error => 'got object, not number', }, { instanceLocation => '', keywordLocation => '/anyOf/1/then/type', error => 'got object, not array', }, # - "summary" error from /anyOf/1/then is omitted # - "summary" error from /anyOf is omitted { instanceLocation => '', keywordLocation => '/not', error => 'subschema is true', }, { instanceLocation => '', keywordLocation => '/else/type', error => 'got object, not number', }, # - "summary" error from /else is omitted { instanceLocation => '/alpha', keywordLocation => '/properties/alpha', error => 'property not permitted', }, { instanceLocation => '/beta', keywordLocation => '/properties/beta/multipleOf', error => 'value is not a multiple of 2', }, { instanceLocation => '/gamma/0', keywordLocation => '/properties/gamma/prefixItems/0', error => 'item not permitted', }, # - "summary" error from /properties/gamma/prefixItems is omitted { instanceLocation => '/gamma/1', keywordLocation => '/properties/gamma/items', error => 'additional item not permitted', }, # - "summary" error from /properties/gamma/items is omitted # - "summary" error from /properties/gamma/unevaluatedItems is omitted { instanceLocation => '/theta/0', keywordLocation => '/properties/theta/items', error => 'item not permitted', }, # - "summary" error from /properties/theta/items is omitted # - "summary" error from /properties is omitted { instanceLocation => '/foo', keywordLocation => '/patternProperties/o', error => 'property not permitted', }, # - "summary" error from /patternProperties is omitted { instanceLocation => '/zulu', keywordLocation => '/additionalProperties', error => 'additional property not permitted', }, # - "summary" error from /additionalProperties is omitted # - "summary" error from /unevaluatedProperties is omitted { instanceLocation => '/zulu', keywordLocation => '/propertyNames/pattern', error => 'pattern does not match', }, # - "summary" error from /propertyNames is omitted ], }, 'terse format omits errors from redundant applicator keywords', ); $js = JSON::Schema::Modern->new(validate_formats => 1); { $result = $js->evaluate( 'foo', { format => 'uuid'}, ); cmp_result( $result->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '', keywordLocation => '/format', error => 'not a valid uuid string', }, ], }, 'basic format includes all errors linearly', ); $result->output_format('terse'); cmp_result( $result->TO_JSON, { valid => false, errors => $errors, }, 'terse format does not omit these crucial errors', ); } subtest 'strict_basic' => sub { # see "JSON pointer escaping" in t/errors.t cmp_result( JSON::Schema::Modern->new(specification_version => 'draft2019-09', output_format => 'strict_basic')->evaluate( { '{}' => { 'my~tilde/slash-property' => 1 } }, { '$id' => 'foo.json', properties => { '{}' => { properties => { 'my~tilde/slash-property' => false, }, patternProperties => { '/' => { minimum => 6 }, '[~/]' => { minimum => 7 }, '~' => { minimum => 5 }, '~.*/' => false, }, }, }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '#/%7B%7D/my~0tilde~1slash-property', keywordLocation => '#/properties/%7B%7D/properties/my~0tilde~1slash-property', absoluteKeywordLocation => 'foo.json#/properties/%7B%7D/properties/my~0tilde~1slash-property', error => 'property not permitted', }, { instanceLocation => '#/%7B%7D', keywordLocation => '#/properties/%7B%7D/properties', absoluteKeywordLocation => 'foo.json#/properties/%7B%7D/properties', error => 'not all properties are valid', }, { instanceLocation => '#/%7B%7D/my~0tilde~1slash-property', keywordLocation => '#/properties/%7B%7D/patternProperties/~1/minimum', # / absoluteKeywordLocation => 'foo.json#/properties/%7B%7D/patternProperties/~1/minimum', # / error => 'value is less than 6', }, { instanceLocation => '#/%7B%7D/my~0tilde~1slash-property', keywordLocation => '#/properties/%7B%7D/patternProperties/%5B~0~1%5D/minimum', # [~/] absoluteKeywordLocation => 'foo.json#/properties/%7B%7D/patternProperties/%5B~0~1%5D/minimum', # [~/] error => 'value is less than 7', }, { instanceLocation => '#/%7B%7D/my~0tilde~1slash-property', keywordLocation => '#/properties/%7B%7D/patternProperties/~0/minimum', # ~ absoluteKeywordLocation => 'foo.json#/properties/%7B%7D/patternProperties/~0/minimum', # ~ error => 'value is less than 5', }, { instanceLocation => '#/%7B%7D/my~0tilde~1slash-property', keywordLocation => '#/properties/%7B%7D/patternProperties/~0.*~1', # ~.*/ absoluteKeywordLocation => 'foo.json#/properties/%7B%7D/patternProperties/~0.*~1', # ~.*/ error => 'property not permitted', }, { instanceLocation => '#/%7B%7D', keywordLocation => '#/properties/%7B%7D/patternProperties', absoluteKeywordLocation => 'foo.json#/properties/%7B%7D/patternProperties', error => 'not all properties are valid', }, { instanceLocation => '#', keywordLocation => '#/properties', absoluteKeywordLocation => 'foo.json#/properties', error => 'not all properties are valid', }, ], }, 'strict_basic turns json pointers into URIs, including uri escapes', ); }; subtest 'AND two result objects together' => sub { my @results = map { my $count = $_; my $valid = $count % 2; JSON::Schema::Modern::Result->new( valid => $valid, ($valid ? 'annotations' : 'errors') => [ map ${\ ('JSON::Schema::Modern::'.($valid ? 'Annotation' : 'Error'))}->new( depth => 0, mode => 'evaluate', keyword => 'keyword '.$count.'-'.$_, instance_location => 'instance location '.$count.'-'.$_, keyword_location => 'keyword location '.$count.'-'.$_, $valid ? (annotation => 'annotation '.$count.'-'.$_) : (error => 'error '.$count.'-'.$_), ), 0..1 ], ) } 0..3; cmp_result( (my $one_true = $results[0] & $results[1]), all( methods(valid => bool(0)), listmethods( errors => [ map methods(TO_JSON => { instanceLocation => 'instance location 0-'.$_, keywordLocation => 'keyword location 0-'.$_, error => 'error 0-'.$_, }), 0..1 ], annotations => [ map methods(TO_JSON => { instanceLocation => 'instance location 1-'.$_, keywordLocation => 'keyword location 1-'.$_, annotation => 'annotation 1-'.$_, }), 0..1 ], ), ), 'ANDing true and false results = invalid, but errors and annotations both preserved', ); cmp_result( (my $both_true = $results[1] & $results[3]), all( methods(valid => bool(1)), listmethods( annotations => [ map { my $count = $_; map methods(TO_JSON => { instanceLocation => 'instance location '.$count.'-'.$_, keywordLocation => 'keyword location '.$count.'-'.$_, annotation => 'annotation '.$count.'-'.$_, }), 0..1 } 1,3 ], ), ), 'ANDing two true results = valid', ); cmp_result( (my $both_false = $results[0] & $results[2]), all( methods(valid => bool(0)), listmethods( errors => [ map { my $count = $_; map methods(TO_JSON => { instanceLocation => 'instance location '.$count.'-'.$_, keywordLocation => 'keyword location '.$count.'-'.$_, error => 'error '.$count.'-'.$_, }), 0..1 } 0,2 ], ), ), 'ANDing two false results = invalid', ); like( exception { $results[0] & 0 }, qr/wrong type for \& operation/, 'only Result objects can be processed', ); is( refaddr(my $itself = $results[0] & $results[0]), refaddr($results[0]), 'ANDing a result with itself is a no-op', ); }; subtest annotations => sub { my %args = ( valid => 1, annotations => [ JSON::Schema::Modern::Annotation->new( depth => 0, keyword => 'foo', instance_location => 'instance location', keyword_location => 'keyword location ', annotation => 'annotation', ) ], ); cmp_result( JSON::Schema::Modern::Result->new(%args)->TO_JSON, { valid => true, annotations => [ { instanceLocation => 'instance location', keywordLocation => 'keyword location ', annotation => 'annotation', }, ], }, 'by default, annotations are included in the formatted output', ); cmp_result( JSON::Schema::Modern::Result->new(%args, formatted_annotations => 0)->TO_JSON, { valid => true }, 'but inclusion can be disabled', ); }; subtest 'data_only' => sub { my $result = JSON::Schema::Modern::Result->new( valid => 0, errors => [ JSON::Schema::Modern::Error->new( depth => 1, mode => 'evaluate', keyword => 'hello', instance_location => '/foo/bar', keyword_location => '/allOf/0/hello', error => 'schema is invalid', ), JSON::Schema::Modern::Error->new( depth => 1, mode => 'evaluate', keyword => 'goodbye', instance_location => '/foo/bar', keyword_location => '/allOf/1/goodbye', error => 'schema is invalid', ), JSON::Schema::Modern::Error->new( depth => 0, mode => 'evaluate', keyword => 'allOf', instance_location => '/foo/bar', keyword_location => '/allOf', error => 'subschemas 0, 1 are not valid', ), ], ); is( $result->format('data_only'), "'/foo/bar': schema is invalid\n'/foo/bar': subschemas 0, 1 are not valid", 'data_only format outputs a string of data locations only, with duplicates removed', ); is( JSON::Schema::Modern::Result->new( valid => 0, errors => [ map JSON::Schema::Modern::Error->new( do { my $e = $_; map +( $_ => $e->$_ ), qw(depth keyword instance_location keyword_location error) }, mode => 'traverse', ), $result->errors ], )->format('data_only'), "'/allOf/0/hello': schema is invalid\n'/allOf/1/goodbye': schema is invalid\n'/allOf': subschemas 0, 1 are not valid", 'data_only format uses keyword locations when result came from traverse', ); }; done_testing; JSON-Schema-Modern-0.609/t/equality.t000640 000766 000024 00000020503 15001302761 017450 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use utf8; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use JSON::Schema::Modern::Utilities qw(is_type get_type is_equal); use Scalar::Util qw(dualvar isdual); use lib 't/lib'; use Helper; subtest 'equality, using inflated data' => sub { foreach my $test ( [ undef, undef, true ], [ undef, false, false, '', 'wrong type: null vs boolean' ], [ undef, true , false, '', 'wrong type: null vs boolean' ], [ undef, 1, false, '', 'wrong type: null vs integer' ], [ undef, '1', false, '', 'wrong type: null vs string' ], [ [qw(a b c)], [qw(a b c)], true ], [ [qw(a b c)], [qw(a b)], false, '', 'element count differs: 3 vs 2' ], [ [qw(a b)], [qw(b a)], false, '/0', 'strings not equal' ], [ 1, 1, true ], [ 1, 1.0, true ], [ 1, '1.0', false, '', 'wrong type: integer vs string' ], [ '1.1', 1.1, false, '', 'wrong type: string vs number' ], [ '1', 1, false, '', 'wrong type: string vs integer' ], [ '1.1', 1.1, false, '', 'wrong type: string vs number' ], [ [1,2], [2,1], false, '/0', 'integers not equal' ], [ { a => 1, b => 2 }, { b => 2, a => 1 }, true ], [ { a => 1 }, { a => 1.0 }, true ], [ [qw(école ಠ_ಠ)], ["\x{e9}cole", "\x{0ca0}_\x{0ca0}"], true ], [ { a => 1, b => 2 }, { a => 1, b => 3 }, false, '/b', 'integers not equal' ], [ { a => { b => 1, c => 2 }, d => { e => 3, f => 4 } }, { a => { b => 1, c => 2 }, d => { e => 3, f => 5 } }, false, '/d/f', 'integers not equal' ], [ [ { a => 1 } ], [ { a => 1, b => 2 } ], false, '/0', 'property count differs: 1 vs 2' ], [ [ { a => 1 } ], [ { b => 2 } ], false, '/0', 'property names differ starting at position 0 ("a" vs "b")' ], [ { foo => [ [ 0 ] ] }, { foo => [ [ 0, 1 ] ] }, false, '/foo/0', 'element count differs: 1 vs 2' ], ) { my ($x, $y, $expected, $diff_path, $error) = @$test; my @types = map get_type($_), $x, $y; my $result = is_equal($x, $y, my $state = {}); ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected)); is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected; is($state->{error}, $error // '', 'error is correct') if not $expected; is($state->{error}, undef, 'error is undefined') if $expected; isnt($state->{error}, 'uh oh', 'no unexpected error encountered'); ok(is_type($types[0], $x), 'type of arg 0 was not mutated while making equality check'); ok(is_type($types[1], $y), 'type of arg 1 was not mutated while making equality check'); foreach my $idx (0, 1) { ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK") if $types[$idx] eq 'integer' or $types[$idx] eq 'number'; ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK") if $types[$idx] eq 'string'; } note ''; } }; my $decoder = JSON::Schema::Modern::_JSON_BACKEND()->new->allow_nonref(1)->utf8(0); subtest 'equality, using JSON strings' => sub { foreach my $test ( [ 'null', 'null', true ], [ 'null', 1, false ], [ '["a","b","c"]', '["a","b","c"]', true ], [ '["a","b","c"]', '["a","b"]', false ], [ '["a","b"]', '["b","a"]', false, '/0' ], [ '1', '1', true ], [ '1', '1.0', true ], [ '10', '1e1', true ], [ '[1,2]', '[2,1]', false, '/0' ], [ '{"a":1,"b":2}', '{"a":1,"b":2}', true ], [ '{"a":1}', '{"a":1.0}', true ], [ '["école","ಠ_ಠ"]', qq{["\x{e9}cole", "\x{0ca0}_\x{0ca0}"]}, true ], [ '{"a":1,"b":2}', '{"b":3,"a":1}', false, '/b' ], [ '{"a":{"b":1,"c":2},"d":{"e":3,"f":4}}', '{"a":{"b":1,"c":2},"d":{"e":3,"f":5}}', false, '/d/f' ], ) { my ($x, $y, $expected, $diff_path) = @$test; ($x, $y) = map $decoder->decode($_), $x, $y; my @types = map get_type($_), $x, $y; my $result = is_equal($x, $y, my $state = {}); ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected)); is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected; isnt($state->{error}, 'uh oh', 'no unexpected error encountered'); ok(is_type($types[0], $x), 'type of arg 0 was not mutated while making equality check'); ok(is_type($types[1], $y), 'type of arg 1 was not mutated while making equality check'); foreach my $idx (0, 1) { ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK") if $types[$idx] eq 'integer' or $types[$idx] eq 'number'; ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK") if $types[$idx] eq 'string'; } note ''; } }; subtest 'equality, using scalarref_booleans' => sub { foreach my $test ( [ \0, true, false ], [ \1, true, true ], [ \0, false, true ], [ \1, false, false ], [ undef, \0, false ], [ undef, false, false ], ) { my ($x, $y, $expected, $diff_path) = @$test; my @types = map get_type($_), $x, $y; my $result = is_equal($x, $y, my $state = { scalarref_booleans => 1}); ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected)); is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected; isnt($state->{error}, 'uh oh', 'no unexpected error encountered'); ok(is_type($types[0], $x), 'type of arg 0 was not mutated while making equality check'); ok(is_type($types[1], $y), 'type of arg 1 was not mutated while making equality check'); foreach my $idx (0, 1) { ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK") if $types[$idx] eq 'integer' or $types[$idx] eq 'number'; ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK") if $types[$idx] eq 'string'; } note ''; } }; subtest 'equality, using stringy_numbers' => sub { foreach my $test ( [ 1, 1, true ], [ 1, 1.0, true ], [ 1, '1.0', true ], [ '1.1', 1.1, true ], [ '1', 1, true ], [ '1.1', 1.1, true ], [ '1', '1.00', true ], [ '1.10', '1.1000', true ], [ 'x', 'x', true ], [ 'x', 'y', false ], [ 'x', 0, false ], [ 0, 'y', false ], [ '5', dualvar(5, '5'), true ], [ 5, dualvar(5, '5'), true ], [ '5', dualvar(5, 'five'), false ], [ 5, dualvar(5, 'five'), false ], [ dualvar(5, 'five'), dualvar(5, 'five'), false ], ) { my ($x, $y, $expected, $diff_path) = @$test; my @types = map get_type($_), $x, $y; my $result = is_equal($x, $y, my $state = { stringy_numbers => 1 }); ok(!($result xor $expected), json_sprintf('%s == %s is %s', $x, $y, $expected)); is($state->{path}, $diff_path // '', 'two instances differ at the expected place') if not $expected; isnt($state->{error}, 'uh oh', 'no unexpected error encountered'); is(get_type($x), $types[0], 'type of arg 0 was not mutated while making equality check (get_type returns '.$types[0].')'); is(get_type($y), $types[1], 'type of arg 1 was not mutated while making equality check (get_type returns '.$types[1].')'); ok( is_type($types[0], $x), "type of arg 0 was not mutated while making equality check (is_type('$types[0]') returns true)", ) if $types[0] ne 'ambiguous type'; ok( is_type($types[1], $y), "type of arg 1 was not mutated while making equality check (is_type('$types[1]') returns true)", ) if $types[1] ne 'ambiguous type'; foreach my $idx (0, 1) { ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & B::SVf_POK ), "arg $idx did not gain a POK") if $types[$idx] eq 'integer' or $types[$idx] eq 'number'; ok(!(B::svref_2object(\[$x, $y]->[$idx])->FLAGS & (B::SVf_IOK | B::SVf_NOK)), "arg $idx did not gain an NOK or IOK") if not ($idx == 1 and isdual($y) and $types[1] ne 'ambiguous type') and $types[$idx] eq 'string'; } note ''; } }; done_testing; JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/000770 000766 000024 00000000000 15001302761 022521 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/formats.t000640 000766 000024 00000073167 15001302761 017304 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Warnings qw(warnings :no_end_test had_no_warnings allow_warnings); use Test::Fatal; use JSON::Schema::Modern::Utilities qw(get_type); use lib 't/lib'; use Helper; use Test::Without::Module 0.19 qw( Time::Moment DateTime::Format::RFC3339 Data::Validate::Domain Email::Address::XS Net::IDN::Encode ); use constant ALL_FORMATS => [ qw( date-time email hostname ipv4 ipv6 uri uri-reference uri-template json-pointer iri iri-reference idn-email idn-hostname relative-json-pointer regex date time duration uuid ) ]; my ($annotation_result, $validation_result); subtest 'no validation' => sub { cmp_result( JSON::Schema::Modern->new(collect_annotations => 1, validate_formats => 0) ->evaluate('abc', { format => 'uuid' })->TO_JSON, $annotation_result = { valid => true, annotations => [ { instanceLocation => '', keywordLocation => '/format', annotation => 'uuid', }, ], }, 'validate_formats=0 disables format assertion behaviour; annotation is still produced', ); cmp_result( JSON::Schema::Modern->new(collect_annotations => 1, validate_formats => 1) ->evaluate('abc', { format => 'uuid' }, { validate_formats => 0 })->TO_JSON, $annotation_result, 'format validation can be turned off in evaluate()', ); }; subtest 'simple validation' => sub { my $js = JSON::Schema::Modern->new(collect_annotations => 1, validate_formats => 1); cmp_result( $js->evaluate(123, { format => 'uuid' })->TO_JSON, $annotation_result, 'non-string values are valid, and produce an annotation', ); cmp_result( $js->evaluate( '2eb8aa08-aa98-11ea-b4aa-73b441d16380', { format => 'uuid' }, )->TO_JSON, $annotation_result, 'simple success', ); cmp_result( $js->evaluate('123', { format => 'uuid' })->TO_JSON, $validation_result = { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/format', error => 'not a valid uuid string', }, ], }, 'simple failure', ); $js = JSON::Schema::Modern->new(collect_annotations => 1); ok(!$js->validate_formats, 'format_validation defaults to false'); cmp_result( $js->evaluate('123', { format => 'uuid' }, { validate_formats => 1 })->TO_JSON, $validation_result, 'format validation can be turned on in evaluate()', ); ok(!$js->validate_formats, '...but the value is still false on the object'); }; subtest 'override a format sub' => sub { like( exception { JSON::Schema::Modern->new( validate_formats => 1, format_validations => +{ uuid => 1 }, ) }, qr/Reference .* did not pass type constraint /, 'check syntax of override to existing format via constructor', ); my $js = JSON::Schema::Modern->new(validate_formats => 1); like( exception { $js->add_format_validation([] => 1) }, qr/Value .* did not pass type constraint /, 'check syntax of override format name to existing format via setter', ); like( exception { $js->add_format_validation(uuid => 1) }, qr/Value .* did not pass type constraint /, 'check syntax of override definition value to existing format via setter', ); like( exception { $js->add_format_validation(uuid => { sub => sub { 0 }}) }, qr/Reference .* did not pass type constraint /, 'type is required if passing a hashref', ); like( exception { $js->add_format_validation(uuid => { type => 'number', sub => sub { 0 }}) }, qr/Type for override of format uuid does not match original type/, 'cannot override a core format to support a different data type', ); $js->add_format_validation(uuid => sub { $_[0] =~ /^[a-z0-9-]+$/ }); cmp_result( $js->evaluate( [ 0, 1, [], {}, 'a', 'foobie!', ], { items => { format => 'uuid' } }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/5', keywordLocation => '/items/format', error => 'not a valid uuid string', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'can override a core format definition, as long as it uses the same type', ); like( exception { JSON::Schema::Modern->new( validate_formats => 1, format_validations => +{ mult_5 => 1 }, ) }, qr/Value "1" did not pass type constraint "(Dict\[|Ref").../, 'check syntax of implementation for a new format', ); $js = JSON::Schema::Modern->new( collect_annotations => 1, validate_formats => 1, format_validations => +{ uuid => sub { $_[0] =~ /^[A-Z]+$/ }, mult_5 => +{ type => 'number', sub => sub { ($_[0] % 5) == 0 } }, }, ); like( exception { $js->add_format_validation(uuid_bad => 1) }, qr/Value "1" did not pass type constraint "(Dict\[|Ref").../, 'check syntax of implementation when adding an override to existing format', ); like( exception { $js->add_format_validation(mult_5_bad => 1) }, qr/Value "1" did not pass type constraint "(Dict\[|Ref").../, 'check syntax of implementation when adding a new format', ); cmp_result( $js->evaluate( [ { uuid => '2eb8aa08-aa98-11ea-b4aa-73b441d16380', mult_5 => 3 }, { uuid => 3, mult_5 => 'abc' }, ], { items => { properties => { uuid => { format => 'uuid' }, mult_5 => { format => 'mult_5' }, }, }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0/mult_5', keywordLocation => '/items/properties/mult_5/format', error => 'not a valid mult_5 number', }, { instanceLocation => '/0/uuid', keywordLocation => '/items/properties/uuid/format', error => 'not a valid uuid string', }, { instanceLocation => '/0', keywordLocation => '/items/properties', error => 'not all properties are valid', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'swapping out format implementation turns success into failure; wrong types are still valid', ); # do allow overriding mult_5 to support a different type than originally defined. $js->add_format_validation(mult_5 => +{ type => 'object', sub => sub { keys($_[0]->%*) > 2 } }); cmp_result( $js->evaluate( [ {}, { a => 1 }, { a => 1, b => 2 }, { a => 1, b => 2, c => 3 }, [], 'a', ], { items => { format => 'mult_5' } }, )->TO_JSON, { valid => false, errors => [ (map +{ instanceLocation => '/'.$_, keywordLocation => '/items/format', error => 'not a valid mult_5 object', }, 0, 1, 2), { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'can override a custom format definition to use a different type', ); }; subtest 'toggle validate_formats after adding schema' => sub { my $js = JSON::Schema::Modern->new; my $document = $js->add_schema(my $uri = 'http://localhost:1234/ipv4', { format => 'ipv4' }); cmp_result( $js->evaluate('hello', $uri)->TO_JSON, { valid => true }, 'assertion behaviour is off initially', ); cmp_result( $js->evaluate('hello', $uri, { validate_formats => 1 })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/format', absoluteKeywordLocation => 'http://localhost:1234/ipv4#/format', error => 'not a valid ipv4 string', }, ], }, 'assertion behaviour can be enabled later with an already-loaded schema', ); cmp_result( $js->evaluate('127.0.0.1', $uri, { validate_formats => 1 })->TO_JSON, { valid => true }, 'valid assertion behaviour does not die', ); my $js2 = JSON::Schema::Modern->new(validate_formats => 1); $js2->add_document($uri, $document); cmp_result( $js2->evaluate('hello', $uri)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/format', absoluteKeywordLocation => 'http://localhost:1234/ipv4#/format', error => 'not a valid ipv4 string', }, ], }, 'a schema document can be used with another evaluator with assertion behaviour', ); cmp_result( $js2->evaluate('127.0.0.1', $uri)->TO_JSON, { valid => true }, 'valid assertion behaviour does not die', ); }; subtest 'custom metaschemas' => sub { my $js = JSON::Schema::Modern->new; $js->add_schema({ '$id' => 'https://metaschema/format-assertion/false', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/format-assertion' => false, }, }); $js->add_schema({ '$id' => 'https://metaschema/format-assertion/true', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/format-assertion' => true, }, }); cmp_result( $js->evaluate( 'not-an-ip', { '$id' => 'https://schema/ipv4/false', '$schema' => 'https://metaschema/format-assertion/false', type => 'string', format => 'ipv4', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/format', absoluteKeywordLocation => 'https://schema/ipv4/false#/format', error => 'not a valid ipv4', }, ], }, 'custom metaschema using format-assertion=false validates formats', ); cmp_result( $js->evaluate( 'not-an-ip', { '$id' => 'https://schema/ipv4/true', '$schema' => 'https://metaschema/format-assertion/true', type => 'string', format => 'ipv4', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/format', absoluteKeywordLocation => 'https://schema/ipv4/true#/format', error => 'not a valid ipv4', }, ], }, 'custom metaschema using format-assertion=true validates formats', ); }; subtest 'core formats added after draft7' => sub { my $js = JSON::Schema::Modern->new(specification_version => 'draft7', validate_formats => 1); cmp_result( $js->evaluate('123', { format => 'duration' })->TO_JSON, { valid => true }, 'duration is not implemented in draft7', ); cmp_result( $js->evaluate('123', { format => 'uuid' })->TO_JSON, { valid => true }, 'uuid is not implemented in draft7', ); }; subtest 'unimplemented core formats' => sub { # all specification versions foreach my $spec_version (JSON::Schema::Modern::SPECIFICATION_VERSIONS_SUPPORTED->@*) { my $js = JSON::Schema::Modern->new(specification_version => $spec_version, validate_formats => 1); cmp_result( my $res = $js->evaluate( 'hello', { format => 'uri-template', }, )->TO_JSON, { valid => true }, $spec_version . ' with validate_formats = 1 and default dialect, no error when an unimplemented core format is used', ); } # specification version draft2020-12 and later, format-assertion vocabulary foreach my $spec_version (JSON::Schema::Modern::SPECIFICATION_VERSIONS_SUPPORTED->@*) { next if $spec_version =~ /^draft(?:[467]|2019-09)$/; my $js = JSON::Schema::Modern->new(specification_version => $spec_version); $js->add_schema({ '$id' => 'https://my_metaschema', '$schema' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, '$vocabulary' => { JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/core}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/applicator}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/format-assertion}r => true, }, '$ref' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, }); cmp_result( $js->evaluate( 'hello', { '$schema' => 'https://my_metaschema', format => 'uri-template', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/format', error => 'unimplemented core format "uri-template"', }, ], }, $spec_version . ' with Format-Assertion vocabulary: error when using a core format that is unimplemented', ); cmp_result( $js->evaluate( 'hello', { '$schema' => 'https://my_metaschema', anyOf => [ { minLength => 1 }, { format => 'uri-template' }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/anyOf/1/format', error => 'unimplemented core format "uri-template"', }, ], }, $spec_version . ' with Format-Assertion vocabulary: error is seen even when containing subschema would be true, and evaluation is short-circuited', ); # add uri-template definition that allows lower-cased characters $js->add_format_validation('uri-template' => sub { $_[0] !~ /[A-Z]/ }); cmp_result( $js->evaluate( 'hello', { '$schema' => 'https://my_metaschema', format => 'uri-template', }, )->TO_JSON, { valid => true }, 'unimplemented core format can have a custom definition provided', ); } }; subtest 'unknown custom formats' => sub { # see https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.7.2.3 # "An implementation MUST NOT fail validation or cease processing due to an unknown format # attribute." foreach my $spec_version (JSON::Schema::Modern::SPECIFICATION_VERSIONS_SUPPORTED->@*) { my $js = JSON::Schema::Modern->new( specification_version => $spec_version, $spec_version !~ /^draft[467]$/ ? ( collect_annotations => 1 ) : (), validate_formats => 1, ); cmp_result( $js->evaluate('hello', { format => 'whargarbl' })->TO_JSON, { valid => true, $spec_version =~ /^draft[467]$/ ? () : (annotations => [ { instanceLocation => '', keywordLocation => '/format', annotation => 'whargarbl', }, ]), }, $spec_version . ': for format validation with the Format-Annotation vocabulary, unrecognized format attributes do not cause validation failure' . ($spec_version !~ /^draft[467]$/ ? '; annotation is still produced' : ''), ); } # see https://json-schema.org/draft/2020-12/json-schema-validation#section-7.2.3 # "When the Format-Assertion vocabulary is specified, implementations MUST fail upon encountering # unknown formats." foreach my $spec_version (JSON::Schema::Modern::SPECIFICATION_VERSIONS_SUPPORTED->@*) { next if $spec_version =~ /^draft[467]$/ or $spec_version eq 'draft2019-09'; my $js = JSON::Schema::Modern->new(specification_version => $spec_version); $js->add_schema({ '$id' => 'https://my_metaschema', '$schema' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, '$vocabulary' => { JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/core}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/applicator}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/validation}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/format-assertion}r => true, }, '$ref' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, }); my $doc = $js->add_schema({ '$schema' => 'https://my_metaschema', anyOf => [ { minLength => 3 }, { format => 'bloop' }, ], }); is($doc->errors, 0, $spec_version . ': for format validation with the Format-Assertion vocabulary, no errors during traversal when using an unknown custom format'); cmp_result( $js->evaluate('hi', $doc)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/anyOf/1/format', error => 'unimplemented custom format "bloop"', }, ], }, $spec_version . ': for format validation with the Format-Assertion vocabulary, unrecognized custom formats are detected at evaluation time', ); cmp_result( $js->evaluate('hello', $doc, { short_circuit => 1 })->TO_JSON, { valid => true }, '...but this error can be avoided if the keyword is never evaluated', ); } }; subtest 'format: pure_integer' => sub { my $js = JSON::Schema::Modern->new( validate_formats => 1, format_validations => +{ pure_integer => +{ type => 'number', sub => sub ($value) { B::svref_2object(\$value)->FLAGS & B::SVf_IOK } }, }, ); my $decoder = JSON::Schema::Modern::_JSON_BACKEND()->new->allow_nonref(1)->utf8(0); my $int = 5; cmp_result( $js->evaluate( [ (map $decoder->decode($_), '"hello"', '3.1', '3.0', '3', ), bless(\$int, 'Local::MyInteger'), ], { items => { type => 'integer', format => 'pure_integer', }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/items/type', error => 'got string, not integer', }, { instanceLocation => '/1', keywordLocation => '/items/type', error => 'got number, not integer', }, { instanceLocation => '/1', keywordLocation => '/items/format', error => 'not a valid pure_integer number', }, { instanceLocation => '/2', keywordLocation => '/items/format', error => 'not a valid pure_integer number', }, { instanceLocation => '/4', keywordLocation => '/items/type', error => 'got Local::MyInteger, not integer', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'pure_integer format with type', ); cmp_result( $js->evaluate( [ (map $decoder->decode($_), '"hello"', # string, will not apply format '3.1', # number, will apply format '3.0', # "" '3', # "" ), bless(\$int, 'Local::MyInteger'), # blessed type, will not apply format ], { items => { format => 'pure_integer', }, }, )->TO_JSON, { valid => false, errors => [ # strings are not applied to the format { instanceLocation => '/1', keywordLocation => '/items/format', error => 'not a valid pure_integer number', }, { instanceLocation => '/2', keywordLocation => '/items/format', error => 'not a valid pure_integer number', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'pure_integer format without type', ); }; subtest 'formats supporting multiple core types' => sub { # this is int64 from the OAI format registry: https://spec.openapis.org/registry/format/ my $js = JSON::Schema::Modern->new( validate_formats => 1, format_validations => +{ # a signed 64-bit integer; see https://spec.openapis.org/api/format.json int64 => +{ type => ['number', 'string'], sub => sub ($value) { my $type = get_type($value); return if not grep $type eq $_, qw(integer number string); $value = Math::BigInt->new($value) if $type eq 'string'; return if $value eq 'NaN'; # using the literal numbers rather than -2**63, 2**63 -1 to maintain precision $value >= Math::BigInt->new('-9223372036854775808') && $value <= Math::BigInt->new('9223372036854775807'); } }, }, ); my @values = ( '{}', # object is valid '[]', # array is valid 'true', # boolean is valid 'null', # null is valid # string '"-9223372036854775809"', # 4: out of bounds '"-9223372036854775808"', # minimum value '"-9223372036854775807"', # within bounds '"0"', '"9223372036854775806"', # within bounds '"9223372036854775807"', # maximum value '"9223372036854775808"', # out of bounds '"Inf"', '"NaN"', # number '-9223372036854775809', # 13: out of bounds '-9223372036854775808', # minimum value; difficult to use on most architectures without Math::BigInt '-9223372036854775807', # within bounds '0', '9223372036854775806', # within bounds '9223372036854775807', # maximum value '9223372036854775808', # 19: out of bounds # numeric Inf and NaN are not valid JSON ); # note: results may vary on 32-bit architectures when not using Math::BigFloat foreach my $decoder ( JSON::Schema::Modern::_JSON_BACKEND()->new->allow_nonref(1)->utf8(0), JSON::Schema::Modern::_JSON_BACKEND()->new->allow_nonref(1)->utf8(0)->allow_bignum(1)) { cmp_result( my $result = $js->evaluate( [ map $decoder->decode($_), @values ], { items => { format => 'int64', }, }, )->TO_JSON, { valid => false, errors => [ (map +{ instanceLocation => "/$_", keywordLocation => '/items/format', error => 'not a valid int64 number, string', }, 4, 10, 11, 12, 13, 19), { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'int64 format without type - accepts both numbers and strings', ); } }; subtest 'stringy numbers with a numeric format' => sub { my $js = JSON::Schema::Modern->new( validate_formats => 1, stringy_numbers => 1, format_validations => +{ mult_5 => +{ type => 'number', sub => sub { ($_[0] % 5) == 0 } }, }, ); cmp_result( my $res = $js->evaluate( [ 3, '3', 5, '5', 'abc', ], { items => { format => 'mult_5' } }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/items/format', error => 'not a valid mult_5 number', }, { instanceLocation => '/1', keywordLocation => '/items/format', error => 'not a valid mult_5 number', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'FormatAnnotation+validate_formats: strings that look like numbers can be validated against a numeric format when stringy_numbers=1', ); $js = JSON::Schema::Modern->new( stringy_numbers => 1, format_validations => +{ mult_5 => +{ type => 'number', sub => sub { ($_[0] % 5) == 0 } }, }, ); my $spec_version = $js->SPECIFICATION_VERSION_DEFAULT; $js->add_schema({ '$id' => 'https://my_metaschema', '$schema' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, '$vocabulary' => { JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/core}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/applicator}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/format-assertion}r => true, }, '$ref' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, }); cmp_result( $js->evaluate( [ 3, '3', 5, '5', 'abc', ], { '$schema' => 'https://my_metaschema', items => { format => 'mult_5' }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/items/format', error => 'not a valid mult_5', }, { instanceLocation => '/1', keywordLocation => '/items/format', error => 'not a valid mult_5', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'FormatAssertion: strings that look like numbers can be validated against a numeric format when stringy_numbers=1', ); }; # we do have support for these formats, but we do not force that their dependencies be installed # unless the formats are actually to be used. # Therefore we will allow them to be tested against other data types (e.g. in acceptance tests) # even without these dependencies installed, without throwing an exception. subtest 'annotation formats using implementations that rely on optional dependencies' => sub { cmp_result( # relying on default format-assertion behaviour JSON::Schema::Modern->new->evaluate( [ undef, true, {}, [], 1 ], { items => { allOf => [ map +{ format => $_ }, ALL_FORMATS->@* ] } }, )->TO_JSON, { valid => true }, 'can annotate a non-string against formats without their optional dependencies, without dying', ); }; subtest 'assertion formats using implementations that rely on optional dependencies' => sub { foreach my $spec_version (JSON::Schema::Modern::SPECIFICATION_VERSIONS_SUPPORTED->@*) { my $js = JSON::Schema::Modern->new( specification_version => $spec_version, validate_formats => 1, ); cmp_result( $js->evaluate( [ undef, true, {}, [], 1 ], { type => 'array', items => { allOf => [ map +{ format => $_ }, ALL_FORMATS->@* ] } }, )->TO_JSON, { valid => true }, $spec_version . ': for format validation with the Format-Annotation vocabulary, can assert a non-string against formats without their optional dependencies, without dying', ); cmp_result( $js->evaluate( '2025-01-01T00:00:00Z', { type => 'string', allOf => [ { format => 'date-time' }, true, ], }, )->TO_JSON, { valid => true }, $spec_version . ': for format validation with the Format-Annotation vocabulary, in assertion mode, we treat missing prereqs as the format being valid', ); } foreach my $spec_version (JSON::Schema::Modern::SPECIFICATION_VERSIONS_SUPPORTED->@*) { next if $spec_version =~ /^draft[467]$/ or $spec_version eq 'draft2019-09'; my $js = JSON::Schema::Modern->new(specification_version => $spec_version); $js->add_schema({ '$id' => 'https://my_metaschema', '$schema' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, '$vocabulary' => { JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/core}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/applicator}r => true, JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version} =~ s{schema$}{vocab/format-assertion}r => true, }, '$ref' => JSON::Schema::Modern::METASCHEMA_URIS->{$spec_version}, }); my $doc; my @warnings = warnings { $doc = $js->add_schema({ '$schema' => 'https://my_metaschema', allOf => [ { format => 'date-time' }, true, ], }); }; is($doc->errors, 0, $spec_version . ': for format validation with the Format-Assertion vocabulary, no errors during traversal when using an unknown custom format'); cmp_deeply( \@warnings, [ re(qr{Can't locate Time/Moment\.pm}) ], '...but we do warn for the missing module', ); cmp_result( $js->evaluate('2025-01-01T00:00:00Z', $doc)->TO_JSON, { valid => false, errors => [ { error => re(qr{^EXCEPTION: Can't locate Time/Moment\.pm}), instanceLocation => '', keywordLocation => '/allOf/0/format', }, ], }, $spec_version . ': for Format-Asertion vocabulary, we immediately abort when encountering a format that throws an exception', ); } }; had_no_warnings() if $ENV{AUTHOR_TESTING}; done_testing; JSON-Schema-Modern-0.609/t/additional-tests-draft7/000770 000766 000024 00000000000 15001302761 022064 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/zzz-acceptance-draft6-format.t000640 000766 000024 00000005754 15001302761 023221 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Needs; use List::Util 1.50 'head'; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } if ($ENV{EXTENDED_TESTING}) { test_needs { 'Time::Moment' => 0, 'DateTime::Format::RFC3339' => 0, 'Email::Address::XS' => '1.04', 'Data::Validate::Domain' => 0.13, }; } if ($ENV{AUTHOR_TESTING}) { eval { require Time::Moment; 1 } or fail $@; eval { require DateTime::Format::RFC3339; 1 } or fail $@; eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } or fail $@; eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } or fail $@; } my $version = 'draft6'; acceptance_tests( acceptance => { specification => $version, test_subdir => 'optional/format', }, evaluator => { specification_version => $version, # validate_formats behaviour should default to true for draft6 collect_annotations => 0, }, output_file => $version.'-acceptance-format.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ 'uri-template.json', # not yet implemented # these all depend on optional prereqs !$ENV{AUTHOR_TESTING} && !eval { require Time::Moment; 1 } ? 'date-time.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require DateTime::Format::RFC3339; 1 } ? 'date-time.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } ? 'email.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } ? 'hostname.json' : (), ] }, # various edge cases that are difficult to accomodate { file => 'uri.json', test_description => 'validation of URIs', test_description => 'an invalid URI with comma in scheme' }, # Mojo::URL does not fully validate ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft6-acceptance-format.txt for test results JSON-Schema-Modern-0.609/t/zzz-acceptance-draft7-format.t000640 000766 000024 00000007157 15001302761 023221 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Needs; use List::Util 1.50 'head'; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } if ($ENV{EXTENDED_TESTING}) { test_needs { 'Time::Moment' => 0, 'DateTime::Format::RFC3339' => 0, 'Email::Address::XS' => '1.04', 'Data::Validate::Domain' => 0.13, 'Net::IDN::Encode' => 0, }; } if ($ENV{AUTHOR_TESTING}) { eval { require Time::Moment; 1 } or fail $@; eval { require DateTime::Format::RFC3339; 1 } or fail $@; eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } or fail $@; eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } or fail $@; eval { require Net::IDN::Encode; 1 } or fail $@; } my $version = 'draft7'; acceptance_tests( acceptance => { specification => $version, test_subdir => 'optional/format', }, evaluator => { specification_version => $version, # validate_formats behaviour should default to true for this draft collect_annotations => 0, }, output_file => $version.'-acceptance-format.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ 'iri-reference.json', # all strings are considered valid 'uri-template.json', # not yet implemented # these all depend on optional prereqs !$ENV{AUTHOR_TESTING} && !eval { require Time::Moment; 1 } ? qw(date-time.json date.json time.json) : (), !$ENV{AUTHOR_TESTING} && !eval { require DateTime::Format::RFC3339; 1 } ? 'date-time.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } ? qw(email.json idn-email.json) : (), !$ENV{AUTHOR_TESTING} && !eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } ? 'hostname.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Net::IDN::Encode; 1 } ? 'idn-hostname.json' : (), ] }, # various edge cases that are difficult to accomodate { file => 'iri.json', group_description => 'validation of IRIs', # see test suite issue 395 test_description => 'an invalid IRI based on IPv6' }, { file => 'idn-hostname.json', group_description => 'validation of internationalized host names' }, # IDN decoder, Data::Validate::Domain both have issues { file => 'uri.json', test_description => 'validation of URIs', test_description => 'an invalid URI with comma in scheme' }, # Mojo::URL does not fully validate ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft7-acceptance-format.txt for test results JSON-Schema-Modern-0.609/t/callbacks.t000640 000766 000024 00000012457 15001302761 017543 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new; subtest 'evaluation callbacks' => sub { my @used_ref_at; my $result = $js->evaluate( [ { a => { b => { c => { d => 'e' } } } } ], my $schema = { '$defs' => { object_or_string => { anyOf => [ { type => 'object', additionalProperties => { '$ref' => '#/$defs/object_or_string' }, }, { type => 'string' }, ], }, }, contains => { '$ref' => '#/$defs/object_or_string' }, }, my $config = { callbacks => { '$ref' => sub ($data, $schema, $state) { push @used_ref_at, $state->{data_path}; }, }, }, ); ok($result->valid, 'evaluation was successful'); cmp_result( \@used_ref_at, bag( '/0', '/0/a', '/0/a/b', '/0/a/b/c', '/0/a/b/c/d', ), 'identified all data paths where a $ref was used', ); undef @used_ref_at; $result = $js->evaluate( [ { a => { b => 2 } } ], $schema, $config, ); ok(!$result->valid, 'evaluation was not successful'); cmp_result( \@used_ref_at, [], 'no callbacks on failure: innermost $ref failed, so all other $refs failed too', ); undef @used_ref_at; $result = $js->evaluate( [ { a => { b => 'c' } }, { x => { y => 1 } }, ], { '$defs' => { object_or_string => { anyOf => [ { type => 'object', additionalProperties => { '$ref' => '#/$defs/object_or_string' }, }, { type => 'string' }, ], }, }, contains => { '$ref' => '#/$defs/object_or_string' }, }, $config, ); ok($result->valid, 'evaluation was successful'); cmp_result( \@used_ref_at, bag( '/0', '/0/a', '/0/a/b', ), 'successful subschemas have callbacks called, but not failed subschemas', ); }; subtest 'callbacks for keywords without eval subs' => sub { my %keywords; my $result = $js->evaluate( 'hello', { '$id' => 'my_weird_schema', '$schema' => 'https://json-schema.org/draft/2020-12/schema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true }, '$anchor' => 'my_anchor', '$comment' => 'my comment', '$defs' => { foo => true }, '$dynamicAnchor' => 'dynamicanchor', if => true, then => true, else => true, }, { callbacks => { map +($_ => sub ($data, $schema, $state) { ++$keywords{$state->{keyword}} }), qw($anchor $comment $defs $dynamicAnchor if then else $schema $vocabulary), }, }, ); ok($result->valid, 'evaluation was successful'); cmp_result( \%keywords, { map +($_ => 1), qw($anchor $comment $defs $dynamicAnchor if then else $schema $vocabulary) }, 'callbacks are triggered for keywords even when they lack evaluation subs', ); }; subtest 'callbacks that produce errors' => sub { my $result = $js->evaluate( my $data = { alpha => 1, beta => 'foo', }, my $schema = { properties => { alpha => { type => 'number' } }, additionalProperties => { type => 'number' }, }, my $configs = { callbacks => { type => sub ($data, $schema, $state) { JSON::Schema::Modern::Utilities::E($state, 'this is a callback error'); }, }, }, ); cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '/alpha', keywordLocation => '/properties/alpha/type', error => 'this is a callback error', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, { instanceLocation => '/beta', keywordLocation => '/additionalProperties/type', error => 'got string, not number', }, { instanceLocation => '', keywordLocation => '/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'result object contains the callback error, and the other errors', ); $result = $js->evaluate($data, $schema, { %$configs, short_circuit => 1 }); cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '/alpha', keywordLocation => '/properties/alpha/type', error => 'this is a callback error', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'result object contains the callback error, and short-circuits execution', ); }; done_testing; JSON-Schema-Modern-0.609/t/max_traversal_depth.t000640 000766 000024 00000004737 15001302761 021662 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new(max_traversal_depth => 6); cmp_result( $js->evaluate( [ [ [ [ [ 1 ] ] ] ] ], { items => { '$ref' => '#' }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0/0/0/0', keywordLocation => '/items/$ref/items/$ref/items/$ref/items', absoluteKeywordLocation => '#/items', error => 'EXCEPTION: maximum evaluation depth (6) exceeded', }, ], }, 'evaluation is halted when traversal gets too deep', ); cmp_result( $js->evaluate( 1, { '$defs' => { loop_a => { '$ref' => '#/$defs/loop_b', }, loop_b => { '$ref' => '#/$defs/loop_a', }, }, '$ref' => '#/$defs/loop_a', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/$ref/$ref', absoluteKeywordLocation => '#/$defs/loop_a', error => 'EXCEPTION: infinite loop detected (same location evaluated twice)', }, ], }, 'evaluation is halted when an instance location is evaluated against the same schema location a second time', ); cmp_result( $js->evaluate( { foo => 1 }, { '$defs' => { mydef => { '$id' => '/properties/foo' } }, properties => { foo => { '$ref' => '/properties/foo', }, }, }, )->TO_JSON, { valid => true }, 'the seen counter does not confuse URI paths and fragments: /properties/foo vs #/properties/foo', ); cmp_result( $js->evaluate( { foo => 1 }, { '$defs' => { int => { type => 'integer' }, }, anyOf => [ { additionalProperties => { '$ref' => '#/$defs/int' } }, { additionalProperties => { '$ref' => '#/$defs/int' } }, ], } )->TO_JSON, { valid => true }, 'the seen counter does not confuse two subschemas that both apply the same definition to the same instance location', ); done_testing; JSON-Schema-Modern-0.609/t/zzz-acceptance-draft6.t000640 000766 000024 00000007164 15001302761 021730 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use List::Util 1.50 'head'; use Config; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } my $version = 'draft6'; my $orig_warn_handler = $SIG{__WARN__}; $SIG{__WARN__} = sub { return if $_[0] =~ /^no-longer-supported "id" keyword present \(at location "[^"]+"\): this should be rewritten as "\$id"/; goto &$orig_warn_handler if $orig_warn_handler; }; acceptance_tests( acceptance => { specification => $version, skip_dir => 'optional/format', }, evaluator => { specification_version => $version, validate_formats => 0, # not the default for the Format vocabulary for this draft collect_annotations => 0, }, output_file => $version.'-acceptance.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ # various edge cases that are difficult to accomodate $Config{ivsize} < 8 ? { file => 'multipleOf.json', group_description => 'small multiple of large integer', test_description => 'any integer is a multiple of 1e-8' } : (), { file => 'optional/ecmascript-regex.json', group_description => '\w in patterns matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in pattern matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => '\w in patternProperties matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in patternProperties matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => [ 'ECMA 262 \d matches ascii digits only', 'ECMA 262 \D matches everything but ascii digits', 'ECMA 262 \w matches ascii letters only', 'ECMA 262 \W matches everything but ascii letters' ] }, # TODO, see test suite PR#505 { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \s matches whitespace', test_description => 'zero-width whitespace matches' }, { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \S matches everything but whitespace', test_description => 'zero-width whitespace does not match' }, ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft6-acceptance.txt for test results JSON-Schema-Modern-0.609/t/annotations.t000640 000766 000024 00000110503 15001302761 020150 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use builtin::compat 'load_module'; use Test::Fatal; use lib 't/lib'; use Helper; subtest 'draft7' => sub { like( exception { JSON::Schema::Modern->new(collect_annotations => 1, specification_version => 'draft7'); }, qr/collect_annotations cannot be used with specification_version draft7/, 'user cannot enable annotations for draft7', ); cmp_result( JSON::Schema::Modern->new(specification_version => 'draft7')->evaluate(1, true, { collect_annotations => 1 })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'EXCEPTION: collect_annotations cannot be used with specification_version draft7', }, ], }, 'user cannot enable annotations for draft7 even as an override', ); }; my $js = JSON::Schema::Modern->new(collect_annotations => 1, short_circuit => 0); my $initial_state = { depth => 0, short_circuit => 0, collect_annotations => 1<<8, initial_schema_uri => Mojo::URL->new, data_path => '', schema_path => '', traversed_schema_path => '', spec_version => 'draft2019-09', vocabularies => [ (map load_module($_), map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Applicator Validation MetaData)), ], evaluator => $js, }; subtest 'allOf' => sub { my $state = { %$initial_state, keyword => 'allOf', annotations => [], errors => [], }; my $fail_schema = { allOf => [ false, # fails; creates errors { title => 'allOf title' }, # passes; creates annotations ], }; ok( !$state->{vocabularies}[0]->_eval_keyword_allOf(1, $fail_schema, $state), 'evaluation of the allOf keyword fails', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/allOf/1/title', annotation => 'allOf title', }), ], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/allOf/0', error => 'subschema is false' }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/allOf', error => 'subschema 0 is not valid' }), ], }, 'failing allOf: state is correct after evaluating', ); my $pass_schema = { allOf => [ true, { title => 'allOf title' }, # passes; creates annotations true, ], }; $state->{annotations} = []; $state->{errors} = []; ok( $state->{vocabularies}[0]->_eval_keyword_allOf(1, $pass_schema, $state), 'evaluation of the allOf keyword succeeds', ); cmp_result( $state, { %$new_state, annotations => [ superhashof({ instance_location => '', keyword_location => '/allOf/1/title', annotation => 'allOf title', }), ], errors => [], }, 'passing allOf: state is correct after evaluating', ); cmp_result( $js->evaluate(1, $pass_schema, { collect_annotations => 0 })->TO_JSON, { valid => true }, 'annotation collection can be turned off in evaluate()', ); ok($js->collect_annotations, '...but the value is still true on the object'); { my $js = JSON::Schema::Modern->new; ok(!$js->collect_annotations, 'collect_annotations defaults to false'); cmp_result( $js->evaluate(1, $pass_schema, { collect_annotations => 1 })->TO_JSON, { valid => true, annotations => [ { instanceLocation => '', keywordLocation => '/allOf/1/title', annotation => 'allOf title', }, ], }, 'annotation collection can be turned on in evaluate() also', ); } }; subtest 'oneOf' => sub { my $state = { %$initial_state, keyword => 'oneOf', annotations => [], errors => [], }; my $fail_schema = { oneOf => [ false, # fails; creates errors { title => 'oneOf title' }, # passes; creates annotations { title => 'oneOf title2' }, # passes; creates annotations ], }; ok( !$state->{vocabularies}[0]->_eval_keyword_oneOf(1, $fail_schema, $state), 'evaluation of the oneOf keyword fails', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/oneOf/1/title', annotation => 'oneOf title', }), superhashof({ instance_location => '', keyword_location => '/oneOf/2/title', annotation => 'oneOf title2', }), ], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/oneOf', error => 'multiple subschemas are valid: 1, 2' }), ], }, 'failing oneOf: state is correct after evaluating', ); my $pass_schema = { oneOf => [ false, { title => 'oneOf title' }, # passes; creates annotations false, ], }; $state->{annotations} = []; $state->{errors} = []; ok( $state->{vocabularies}[0]->_eval_keyword_oneOf(1, $pass_schema, $state), 'evaluation of the oneOf keyword succeeds', ); cmp_result( $state, { %$new_state, annotations => [ superhashof({ instance_location => '', keyword_location => '/oneOf/1/title', annotation => 'oneOf title', }), ], errors => [], }, 'passing oneOf: state is correct after evaluating', ); }; subtest 'not' => sub { my $state = { %$initial_state, keyword => 'not', annotations => [], errors => [], }; my $fail_schema = { not => { title => 'not title' }, # passes; skips annotations because nothing needs them }; ok( !$state->{vocabularies}[0]->_eval_keyword_not(1, $fail_schema, $state), 'evaluation of the not keyword fails', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/not', error => 'subschema is valid' }), ], }, 'failing not: state is correct after evaluating', ); $state = { %$initial_state, keyword => 'not', annotations => [], errors => [], }; $fail_schema = { not => { properties => { foo => true }, unevaluatedProperties => false, }, # passes; annotations are collected because unevaluated* needs them }; ok( !$state->{vocabularies}[0]->_eval_keyword_not(1, $fail_schema, $state), 'evaluation of the not keyword fails', ); cmp_result( $state, $new_state = { %$state, initial_schema_uri => str(''), annotations => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/not', error => 'subschema is valid' }), ], }, 'failing not: state is correct after evaluating (annotations will be ultimately discarded)', ); my $pass_schema = { not => { not => { title => 'not title' } }, }; $state->{annotations} = []; $state->{errors} = []; ok( $state->{vocabularies}[0]->_eval_keyword_not(1, $pass_schema, $state), 'evaluation of the not keyword succeeds', ); cmp_result( $state, { %$new_state, annotations => [], errors => [], }, 'passing not: state is correct after evaluating', ); cmp_result( $js->evaluate( { foo => 1 }, { not => { not => { '$comment' => 'this subschema must still produce annotations internally, even though the "not" will ultimately discard them', anyOf => [ true, { properties => { foo => true } }, ], unevaluatedProperties => false, }, }, }, )->TO_JSON, { valid => true, }, 'annotations are still collected inside a "not", otherwise the unevaluatedProperties would have returned false', ); }; subtest 'prefixItems' => sub { my $state = { %$initial_state, keyword => 'prefixItems', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_prefixItems([], { prefixItems => [ true ] }, $state), 'no items means that "prefixItems" succeeds', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [], errors => [], }, 'no items: no annotation is produced by prefixItems', ); $state = { %$initial_state, keyword => 'prefixItems', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_prefixItems([ 1 ], { prefixItems => [ true ] }, $state), 'one item', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/prefixItems', annotation => true, }), ], errors => [], }, 'passing prefixItems: one item is annotated', ); $state = { %$initial_state, keyword => 'prefixItems', annotations => [], errors => [], }; ok( !$state->{vocabularies}[0]->_eval_keyword_prefixItems( [ 1, 5, 9 ], { prefixItems => [ { title => 'hi', maximum => 3 }, { title => 'hi', maximum => 3 } ] }, $state), 'two items, one failing', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '/0', keyword_location => '/prefixItems/0/title', annotation => 'hi', }), superhashof({ instance_location => '', keyword_location => '/prefixItems', annotation => 1, }), ], errors => [ methods(TO_JSON => { instanceLocation => '/1', keywordLocation => '/prefixItems/1/maximum', error => 'value is greater than 3', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/prefixItems', error => 'not all items are valid', }), ], }, 'failing prefixItems still collects annotations', ); }; subtest 'schema-items' => sub { my $state = { %$initial_state, keyword => 'items', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_items([], { items => true }, $state), 'no items means that "items" succeeds', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [], errors => [], }, 'no items: no annotation is produced by items', ); $state = { %$initial_state, keyword => 'items', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_items([ 1 ], { items => true }, $state), 'one item', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/items', annotation => true, }), ], errors => [], }, 'passing items: one item is annotated', ); $state = { %$initial_state, keyword => 'items', annotations => [], errors => [], }; ok( !$state->{vocabularies}[0]->_eval_keyword_items( [ 1, 5 ], { items => { title => 'hi', maximum => 3 } }, $state), 'two items, one failing', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '/0', keyword_location => '/items/title', annotation => 'hi', }), superhashof({ instance_location => '', keyword_location => '/items', annotation => true, }), ], errors => [ methods(TO_JSON => { instanceLocation => '/1', keywordLocation => '/items/maximum', error => 'value is greater than 3', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }), ], }, 'failing items still collects annotations', ); }; subtest 'additionalItems' => sub { my $state = { %$initial_state, keyword => 'additionalItems', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_items([], { additionalItems => true }, $state), 'no items means that "additionalItems" succeeds', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [], errors => [], }, 'no items: no annotation is produced by additionaltems', ); $state = { %$initial_state, keyword => 'additionalItems', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_additionalItems([ 1 ], { additionalItems => false }, $state), 'one item', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [], errors => [], }, 'additionalItems does nothing without items', ); }; subtest 'properties' => sub { my $state = { %$initial_state, keyword => 'properties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_properties({}, { properties => { foo => true } }, $state), 'no items means that "properties" succeeds', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/properties', annotation => [], }), ], errors => [], }, 'no properties: annotation is still produced by properties', ); $state = { %$initial_state, keyword => 'properties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_properties({ foo => 1 }, { properties => { foo => true } }, $state), 'one property', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/properties', annotation => [ 'foo' ], }), ], errors => [], }, 'passing properties: one property is annotated', ); $state = { %$initial_state, keyword => 'properties', annotations => [], errors => [], }; ok( !$state->{vocabularies}[0]->_eval_keyword_properties( { foo => 1, bar => 5 }, { properties => { foo => { title => 'hi', maximum => 3 }, bar => { title => 'hi', maximum => 3 }, }, }, $state), 'two properties, one failing', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '/foo', keyword_location => '/properties/foo/title', annotation => 'hi', }), superhashof({ instance_location => '', keyword_location => '/properties', annotation => [ qw(bar foo) ], }), ], errors => [ methods(TO_JSON => { instanceLocation => '/bar', keywordLocation => '/properties/bar/maximum', error => 'value is greater than 3', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }), ], }, 'failing properties still collects annotations', ); }; subtest 'patternProperties' => sub { my $state = { %$initial_state, keyword => 'patternProperties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_patternProperties({}, { patternProperties => { foo => true } }, $state), 'no items means that "patternProperties" succeeds', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/patternProperties', annotation => [], }), ], errors => [], }, 'no pProperties: annotation is still produced by patternProperties', ); $state = { %$initial_state, keyword => 'patternProperties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_patternProperties({ foo => 1 }, { patternProperties => { foo => true } }, $state), 'one property', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/patternProperties', annotation => [ 'foo' ], }), ], errors => [], }, 'passing properties: one property is annotated', ); $state = { %$initial_state, keyword => 'patternProperties', annotations => [], errors => [], }; ok( !$state->{vocabularies}[0]->_eval_keyword_patternProperties( { foo => 1, bar => 5 }, { patternProperties => { foo => { title => 'hi', maximum => 3 }, bar => { title => 'hi', maximum => 3 }, }, }, $state), 'two properties, one failing', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '/foo', keyword_location => '/patternProperties/foo/title', annotation => 'hi', }), superhashof({ instance_location => '', keyword_location => '/patternProperties', annotation => [ qw(bar foo) ], }), ], errors => [ methods(TO_JSON => { instanceLocation => '/bar', keywordLocation => '/patternProperties/bar/maximum', error => 'value is greater than 3', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/patternProperties', error => 'not all properties are valid', }), ], }, 'failing patternProperties still collects annotations', ); }; subtest 'additionalProperties' => sub { my $state = { %$initial_state, keyword => 'additionalProperties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_additionalProperties([], { additionalProperties => true }, $state), 'no items means that "additionalProperties" succeeds', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [], errors => [], }, 'no properties: no annotation is produced by additionalProperties', ); $state = { %$initial_state, keyword => 'additionalProperties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_additionalProperties({ foo => 1 }, { additionalProperties => true }, $state), 'one property', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/additionalProperties', annotation => [ 'foo' ], }), ], errors => [], }, 'passing additionalProperties: one property is annotated', ); $state = { %$initial_state, keyword => 'additionalProperties', annotations => [], errors => [], }; ok( !$state->{vocabularies}[0]->_eval_keyword_additionalProperties( { foo => 1, bar => 3, baz => 5 }, { properties => { foo => true }, additionalProperties => { title => 'hi', maximum => 3 }, }, $state), 'two properties, one failing', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '/bar', keyword_location => '/additionalProperties/title', annotation => 'hi', }), superhashof({ instance_location => '', keyword_location => '/additionalProperties', annotation => [ qw(bar baz) ], }), ], errors => [ methods(TO_JSON => { instanceLocation => '/baz', keywordLocation => '/additionalProperties/maximum', error => 'value is greater than 3', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/additionalProperties', error => 'not all additional properties are valid', }), ], }, 'failing properties still collects annotations', ); }; subtest 'unevaluatedProperties' => sub { my $state = { %$initial_state, keyword => 'unevaluatedProperties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_unevaluatedProperties([], { unevaluatedProperties => true }, $state), 'no items means that "unevaluatedProperties" succeeds', ); cmp_result( $state, my $new_state = { %$state, initial_schema_uri => str(''), annotations => [], errors => [], }, 'no properties: no annotation is produced by unevaluatedProperties', ); $state = { %$initial_state, keyword => 'unevaluatedProperties', annotations => [], errors => [], }; ok( $state->{vocabularies}[0]->_eval_keyword_unevaluatedProperties({ foo => 1 }, { unevaluatedProperties => true }, $state), 'one property', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ superhashof({ instance_location => '', keyword_location => '/unevaluatedProperties', annotation => [ 'foo' ], }), ], errors => [], }, 'passing unevaluatedProperties: one property is annotated', ); $state = { %$initial_state, keyword => 'unevaluatedProperties', annotations => [], errors => [], }; ok( !$state->{vocabularies}[0]->_eval_keyword_unevaluatedProperties( { foo => 1, bar => 3, baz => 5 }, { properties => { foo => true }, unevaluatedProperties => { title => 'hi', maximum => 3 }, }, $state), 'two properties, one failing', ); cmp_result( $state, { %$state, initial_schema_uri => str(''), annotations => [ (map superhashof({ instance_location => '/'.$_, keyword_location => '/unevaluatedProperties/title', annotation => 'hi', }), qw(bar foo)), superhashof({ instance_location => '', keyword_location => '/unevaluatedProperties', annotation => [ qw(bar baz foo) ], }), ], errors => [ methods(TO_JSON => { instanceLocation => '/baz', keywordLocation => '/unevaluatedProperties/maximum', error => 'value is greater than 3', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/unevaluatedProperties', error => 'not all additional properties are valid', }), ], }, 'failing unevaluatedProperties still collects annotations', ); }; subtest 'collect_annotations and unevaluated keywords' => sub { my $js = JSON::Schema::Modern->new(collect_annotations => 0); cmp_result( $js->evaluate( [ 1 ], my $schema = { '$id' => 'unevaluatedItems.json', prefixItems => [ true ], unevaluatedItems => false, }, )->TO_JSON, { valid => true }, 'when "collect_annotations" is explicitly set to false, unevaluatedItems can still be used (valid result, no annotations in result)', ); cmp_result( $js->evaluate( [ 1, 2 ], $schema, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/1', keywordLocation => '/unevaluatedItems', absoluteKeywordLocation => 'unevaluatedItems.json#/unevaluatedItems', error => 'additional item not permitted', }, { instanceLocation => '', keywordLocation => '/unevaluatedItems', absoluteKeywordLocation => 'unevaluatedItems.json#/unevaluatedItems', error => 'subschema is not valid against all additional items', }, ], }, 'when "collect_annotations" is explicitly set to false, unevaluatedItems can still be used (invalid result)', ); cmp_result( $js->evaluate( { foo => 1 }, $schema = { '$id' => 'unevaluatedProperties.json', properties => { foo => true }, unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, 'when "collect_annotations" is explicitly set to false, unevaluatedProperties can still be used (valid result, no annotations)', ); cmp_result( $js->evaluate( { foo => 1, bar => 2 }, $schema, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/bar', keywordLocation => '/unevaluatedProperties', absoluteKeywordLocation => 'unevaluatedProperties.json#/unevaluatedProperties', error => 'additional property not permitted', }, { instanceLocation => '', keywordLocation => '/unevaluatedProperties', absoluteKeywordLocation => 'unevaluatedProperties.json#/unevaluatedProperties', error => 'not all additional properties are valid', }, ], }, 'when "collect_annotations" is explicitly set to false, unevaluatedProperties can still be used (invalid result)', ); cmp_result( $js->evaluate( { item => [ 1 ], property => { foo => 1 }, }, $schema = { properties => { item => { '$ref' => 'unevaluatedItems.json' }, property => { '$ref' => 'unevaluatedProperties.json' }, }, }, )->TO_JSON, { valid => true }, 'when "collect_annotations" is explicitly set to false, unevaluatedProperties still be used, even in other documents (valid result)', ); cmp_result( $js->evaluate( { item => [ 1, 2 ], property => { foo => 1, bar => 2 }, }, $schema, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/item/1', keywordLocation => '/properties/item/$ref/unevaluatedItems', absoluteKeywordLocation => 'unevaluatedItems.json#/unevaluatedItems', error => 'additional item not permitted', }, { instanceLocation => '/item', keywordLocation => '/properties/item/$ref/unevaluatedItems', absoluteKeywordLocation => 'unevaluatedItems.json#/unevaluatedItems', error => 'subschema is not valid against all additional items', }, { instanceLocation => '/property/bar', keywordLocation => '/properties/property/$ref/unevaluatedProperties', absoluteKeywordLocation => 'unevaluatedProperties.json#/unevaluatedProperties', error => 'additional property not permitted', }, { instanceLocation => '/property', keywordLocation => '/properties/property/$ref/unevaluatedProperties', absoluteKeywordLocation => 'unevaluatedProperties.json#/unevaluatedProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'when "collect_annotations" is explicitly set to false, unevaluatedProperties still be used, even in other documents (invalid result)', ); $js = JSON::Schema::Modern->new(collect_annotations => 1); cmp_result( $js->evaluate( [ 1 ], { prefixItems => [ true ], unevaluatedItems => false, }, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '', keywordLocation => '/prefixItems', annotation => true, }, ], }, 'when "collect_annotations" is set to true, unevaluatedItems works, and annotations are returned', ); cmp_result( $js->evaluate( { foo => 1 }, { properties => { foo => true }, unevaluatedProperties => false, }, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '', keywordLocation => '/properties', annotation => [ 'foo' ], }, { instanceLocation => '', keywordLocation => '/unevaluatedProperties', annotation => [], }, ], }, 'when "collect_annotations" is set to true, unevaluatedProperties passes, and annotations are returned', ); $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( [ 1 ], { '$id' => 'unevaluatedItems.json', prefixItems => [ true ], unevaluatedItems => false, }, )->TO_JSON, { valid => true, }, 'when "collect_annotations" is not set, unevaluatedItems still works, but annotations are not returned', ); cmp_result( $js->evaluate( { foo => 1 }, { '$id' => 'unevaluatedProperties.json', properties => { foo => true }, unevaluatedProperties => false, }, )->TO_JSON, { valid => true, }, 'when "collect_annotations" is not set, unevaluatedProperties still works, but annotations are not returned', ); cmp_result( $js->evaluate( { item => [ 1 ], property => { foo => 1 }, }, { properties => { item => { '$ref' => 'unevaluatedItems.json' }, property => { '$ref' => 'unevaluatedProperties.json' }, }, }, )->TO_JSON, { valid => true, }, '... still works when unevaluated keywords are in a separate document', ); my $doc_items = $js->add_schema('prefixItems.json', { prefixItems => [ true ] }); my $doc_properties = $js->add_schema('properties.json', { properties => { foo => true } }); cmp_result( $js->_get_resource('prefixItems.json')->{configs}, {}, 'items.json does not need collect_annotations => 1 to evaluate itself', ); cmp_result( $js->_get_resource('properties.json')->{configs}, {}, 'properties.json does not need collect_annotations => 1 to evaluate itself', ); cmp_result( $js->evaluate( { item => [ 1 ], property => { foo => 1 }, }, { properties => { item => { '$ref' => 'prefixItems.json', unevaluatedItems => false, }, property => { '$ref' => 'properties.json', unevaluatedProperties => false, }, }, }, )->TO_JSON, { valid => true, }, 'referenced schemas still produce annotations internally when needed, even when not required to evaluate themselves in isolation', ); }; subtest 'annotate unknown keywords' => sub { my $data = { item => [ 1 ], property => { foo => 1 }, }; my $schema = { properties => { item => { items => true, unevaluatedItems => false, bloop => 5, }, property => { properties => { foo => true }, unevaluatedProperties => false, blap => { hi => 1 }, }, }, blip => [ 1, 2, 3 ], }; cmp_result( JSON::Schema::Modern->new->evaluate( $data, $schema, )->TO_JSON, { valid => true, }, 'no annotations even when collect_annotations is false', ); cmp_result( (my $result = JSON::Schema::Modern->new(collect_annotations => 1)->evaluate( $data, $schema, ))->TO_JSON, { valid => true, annotations => [ { instanceLocation => '/item', keywordLocation => '/properties/item/items', annotation => true, }, { instanceLocation => '/item', keywordLocation => '/properties/item/bloop', annotation => 5, }, { instanceLocation => '/property', keywordLocation => '/properties/property/properties', annotation => [ 'foo' ], }, { instanceLocation => '/property', keywordLocation => '/properties/property/unevaluatedProperties', annotation => [], }, { instanceLocation => '/property', keywordLocation => '/properties/property/blap', annotation => { hi => 1 }, }, { instanceLocation => '', keywordLocation => '/properties', annotation => [ 'item', 'property' ], }, { instanceLocation => '', keywordLocation => '/blip', annotation => [ 1, 2, 3 ], }, ], }, 'unknown keywords are collected as annotations', ); cmp_result( [ $result->annotations ], [ methods(keyword => 'items', unknown => bool(0)), methods(keyword => 'bloop', unknown => bool(1)), methods(keyword => 'properties', unknown => bool(0)), methods(keyword => 'unevaluatedProperties', unknown => bool(0)), methods(keyword => 'blap', unknown => bool(1)), methods(keyword => 'properties', unknown => bool(0)), methods(keyword => 'blip', unknown => bool(1)), ], '"unknown" keyword is set on the annotation objects for unknown keywords', ); cmp_result( $result = JSON::Schema::Modern->new(specification_version => 'draft2019-09', collect_annotations => 1) ->evaluate( $data, $schema, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '/item', keywordLocation => '/properties/item/items', annotation => true, }, # no bloop { instanceLocation => '/property', keywordLocation => '/properties/property/properties', annotation => [ 'foo' ], }, { instanceLocation => '/property', keywordLocation => '/properties/property/unevaluatedProperties', annotation => [], }, # no blap { instanceLocation => '', keywordLocation => '/properties', annotation => [ 'item', 'property' ], }, # no blip ], }, 'no annotations from unknown keywords in draft2019-09', ); }; subtest 'items + additionalItems, prefixItems + items' => sub { cmp_result( JSON::Schema::Modern->new(specification_version => 'draft2019-09', collect_annotations => 1) ->evaluate( [ 1, 2, 3 ], { items => { maximum => 5 }, additionalItems => { maximum => 0 }, } )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '', keywordLocation => '/items', annotation => true, }, # no error nor annotation from additionalItems ], }, 'schema-based items + additionalItems', ); cmp_result( my $result = JSON::Schema::Modern->new(collect_annotations => 1)->evaluate( [ 1, 2, 3 ], { prefixItems => [ { maximum => 5 }, { maximum => 5 }, { maximum => 5 } ], items => { maximum => 0 }, } )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '', keywordLocation => '/prefixItems', annotation => true, }, # no error nor annotation from items ], }, 'prefixItems + schema-based items', ); }; done_testing; JSON-Schema-Modern-0.609/t/find-identifiers.t000640 000766 000024 00000054561 15001302761 021051 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use utf8; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use List::Util 'unpairs'; use builtin::compat 'refaddr'; use lib 't/lib'; use Helper; # spec version -> vocab classes my %vocabularies = unpairs(JSON::Schema::Modern->new->__all_metaschema_vocabulary_classes); subtest '$id sets canonical uri' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( 1, my $schema = { '$defs' => { foo => my $foo_definition = { '$id' => 'http://localhost:4242/my_foo', const => 'foo value', }, }, '$ref' => 'http://localhost:4242/my_foo', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/const', absoluteKeywordLocation => 'http://localhost:4242/my_foo#/const', error => 'value does not match', }, ], }, '$id was recognized - $ref was successfully traversed', ); cmp_result( { $js->_resource_index }, { '' => { path => '', canonical_uri => str(''), document => ignore, specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'http://localhost:4242/my_foo' => { path => '/$defs/foo', canonical_uri => str('http://localhost:4242/my_foo'), document => methods(canonical_uri => str('')), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'resources indexed; document canonical_uri is still unset', ); my $doc1 = $js->{_resource_index}{''}{document}; my $doc2 = $js->{_resource_index}{'http://localhost:4242/my_foo'}{document}; is(refaddr($doc1), refaddr($doc2), 'the same document object is indexed under both URIs'); sub _find_all_values ($data) { if (ref $data eq 'ARRAY') { return map __SUB__->($_), @$data; } elsif (ref $data eq 'HASH') { return map __SUB__->($_), values %$data; } return $data; } my @blessed_values = grep ref($_), _find_all_values($doc1->schema); ok(!@blessed_values, 'the schema contains no blessed leaf nodes') or diag 'found blessed values: ', explain [ map ref, @blessed_values ]; }; subtest 'anchors' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( 1, my $schema = { '$defs' => { foo => my $foo_definition = { '$anchor' => 'my_foo', const => 'foo value', }, bar => my $bar_definition = { '$anchor' => 'my_bar', not => true, }, }, '$id' => 'http://localhost:4242', allOf => [ { '$ref' => '#my_foo' }, { '$ref' => '#my_bar' }, { not => my $not_definition = { '$anchor' => 'my_not', not => false, }, }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/$ref/const', absoluteKeywordLocation => 'http://localhost:4242#/$defs/foo/const', error => 'value does not match', }, { instanceLocation => '', keywordLocation => '/allOf/1/$ref/not', absoluteKeywordLocation => 'http://localhost:4242#/$defs/bar/not', error => 'subschema is true', }, { instanceLocation => '', keywordLocation => '/allOf/2/not', absoluteKeywordLocation => 'http://localhost:4242#/allOf/2/not', error => 'subschema is valid', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'http://localhost:4242#/allOf', error => 'subschemas 0, 1, 2 are not valid', }, ], }, '$id was recognized - absolute locations use json paths, not anchors', ); cmp_result( { $js->_resource_index }, { 'http://localhost:4242' => { path => '', canonical_uri => str('http://localhost:4242'), document => methods(canonical_uri => str('http://localhost:4242')), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, anchors => { my_foo => { path => '/$defs/foo', canonical_uri => str('http://localhost:4242#/$defs/foo'), }, my_bar => { path => '/$defs/bar', canonical_uri => str('http://localhost:4242#/$defs/bar'), }, my_not => { path => '/allOf/2/not', canonical_uri => str('http://localhost:4242#/allOf/2/not'), }, }, }, }, 'internal resource index is correct', ); }; subtest '$anchor at root without $id' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( 1, { '$anchor' => 'root', '$defs' => { foo => { '$anchor' => 'my_foo', const => 'foo value', }, }, '$ref' => '#my_foo', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/const', absoluteKeywordLocation => '#/$defs/foo/const', error => 'value does not match', }, ], }, '$id without anchor was recognized - absolute locations use json paths, not anchors', ); cmp_result( { $js->_resource_index }, { '' => { path => '', canonical_uri => str(''), document => ignore, specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, anchors => { root => { path => '', canonical_uri => str(''), }, my_foo => { path => '/$defs/foo', canonical_uri => str('#/$defs/foo'), }, }, }, }, 'internal resource index is correct', ); }; subtest '$ids and $anchors in subschemas after $id changes' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( 1, { '$id' => 'https://foo.com/a/alpha', properties => { b => { '$id' => 'beta', properties => { d => { '$anchor' => 'my_d', }, }, }, f => { '$id' => 'zeta', properties => { h => { '$anchor' => 'my_h', }, }, }, }, }, )->TO_JSON, { valid => true, }, '$anchor is legal in a subschema', ); cmp_result( { $js->_resource_index }, { 'https://foo.com/a/alpha' => { path => '', canonical_uri => str('https://foo.com/a/alpha'), document => ignore, specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'https://foo.com/a/beta' => { path => '/properties/b', canonical_uri => str('https://foo.com/a/beta'), document => ignore, specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, anchors => { my_d => { path => '/properties/b/properties/d', canonical_uri => str('https://foo.com/a/beta#/properties/d'), }, }, }, 'https://foo.com/a/zeta' => { path => '/properties/f', canonical_uri => str('https://foo.com/a/zeta'), document => ignore, specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, anchors => { my_h => { path => '/properties/f/properties/h', canonical_uri => str('https://foo.com/a/zeta#/properties/h'), }, }, }, }, 'internal resource index is correct', ); }; subtest 'invalid $id and $anchor' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( 1, { '$id' => 'foo.json', '$defs' => { a_bad_id => { '$id' => 'foo.json#/foo/bar', }, anchor_a => { '$anchor' => 'a_my$foo', }, anchor_b => { '$anchor' => 'b_hello123৪২', }, anchor_c => { '$anchor' => 'c_helloðworld', }, }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$defs/a_bad_id/$id', absoluteKeywordLocation => 'foo.json#/$defs/a_bad_id/$id', error => '$id value "foo.json#/foo/bar" cannot have a non-empty fragment', }, map +{ instanceLocation => '', keywordLocation => '/$defs/anchor_'.substr($_,0,1).'/$anchor', absoluteKeywordLocation => 'foo.json#/$defs/anchor_'.substr($_,0,1).'/$anchor', error => '$anchor value "'.$_.'" does not match required syntax', }, qw(a_my$foo b_hello123৪২ c_helloðworld), ], }, 'bad $id and $anchor are detected, even if bad definitions are not traversed', ); cmp_result( $js->evaluate( 1, { '$id' => 'foo.json', '$defs' => { const_not_id => { const => { '$id' => 'not_a_real_id', }, }, const_not_anchor => { enum => [ '$anchor' => 'not_a_real_anchor', ], }, }, anyOf => [ { '$ref' => '#/$defs/const_not_id' }, { '$ref' => '#/$defs/const_not_anchor' }, true, ], }, )->TO_JSON, { valid => true, }, '"bad" $ids and $anchors that are not actually keywords are not reported as errors', ); }; subtest 'nested $ids' => sub { my $js = JSON::Schema::Modern->new(short_circuit => 0); my $schema = { '$id' => '/foo/bar/baz.json', '$ref' => '/foo/bar/baz.json#/properties/alpha', # not the canonical URI for that location properties => { alpha => my $alpha = { '$id' => 'alpha.json', additionalProperties => false, properties => { beta => my $beta = { '$id' => '/beta/hello.json', properties => { gamma => my $gamma = { '$id' => 'gamma.json', const => 'hello', }, }, }, }, }, }, }; cmp_result( $js->evaluate( { alpha => { beta => { gamma => 'not hello', }, }, }, $schema, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/alpha', keywordLocation => '/$ref/additionalProperties', absoluteKeywordLocation => '/foo/bar/alpha.json#/additionalProperties', error => 'additional property not permitted', }, { instanceLocation => '', keywordLocation => '/$ref/additionalProperties', absoluteKeywordLocation => '/foo/bar/alpha.json#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '/alpha/beta/gamma', keywordLocation => '/properties/alpha/properties/beta/properties/gamma/const', absoluteKeywordLocation => '/beta/gamma.json#/const', error => 'value does not match', }, { instanceLocation => '/alpha/beta', keywordLocation => '/properties/alpha/properties/beta/properties', absoluteKeywordLocation => '/beta/hello.json#/properties', error => 'not all properties are valid', }, { instanceLocation => '/alpha', keywordLocation => '/properties/alpha/properties', absoluteKeywordLocation => '/foo/bar/alpha.json#/properties', error => 'not all properties are valid', }, { instanceLocation => '', keywordLocation => '/properties', absoluteKeywordLocation => '/foo/bar/baz.json#/properties', error => 'not all properties are valid', }, ], }, 'errors have the correct location', ); cmp_result( { $js->_resource_index }, { '/foo/bar/baz.json' => { path => '', canonical_uri => str('/foo/bar/baz.json'), document => methods(canonical_uri => str('/foo/bar/baz.json')), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, '/foo/bar/alpha.json' => { path => '/properties/alpha', canonical_uri => str('/foo/bar/alpha.json'), document => shallow($js->_get_resource('/foo/bar/baz.json')->{document}), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, '/beta/hello.json' => { path => '/properties/alpha/properties/beta', canonical_uri => str('/beta/hello.json'), document => shallow($js->_get_resource('/foo/bar/baz.json')->{document}), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, '/beta/gamma.json' => { path => '/properties/alpha/properties/beta/properties/gamma', canonical_uri => str('/beta/gamma.json'), document => shallow($js->_get_resource('/foo/bar/baz.json')->{document}), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'properly resolved all the nested $ids', ); }; subtest 'multiple documents, each using canonical_uri = ""' => sub { my $js = JSON::Schema::Modern->new; my $schema1 = { allOf => [ { '$id' => 'subschema1.json', type => 'string' }, { '$id' => 'subschema2.json', type => 'number' }, ], }; my $schema2 = { anyOf => [ { '$id' => 'subschema3.json', type => 'string' }, { '$id' => 'subschema4.json', type => 'number' }, ], }; cmp_result( $js->evaluate(1, $schema1)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/type', absoluteKeywordLocation => 'subschema1.json#/type', error => 'got integer, not string', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschema 0 is not valid', }, ], }, 'evaluation of schema1', ); my $resource_index1 = +{ $js->_resource_index }; my $document1 = $resource_index1->{''}{document}; cmp_result( $resource_index1, { '' => { path => '', canonical_uri => str(''), document => shallow($document1), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema1.json' => { path => '/allOf/0', canonical_uri => str('subschema1.json'), document => shallow($document1), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema2.json' => { path => '/allOf/1', canonical_uri => str('subschema2.json'), document => shallow($document1), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'resources in initial schema are indexed', ); cmp_result( $js->evaluate(1, $schema2)->TO_JSON, { valid => true, }, 'successful evaluation of schema2', ); my $resource_index2 = +{ $js->_resource_index }; my $document2 = $resource_index2->{'subschema3.json'}{document}; cmp_result( $resource_index2, { '' => { path => '', canonical_uri => str(''), document => shallow($document2), # same uri as earlier, but now points to document2 specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema1.json' => { path => '/allOf/0', canonical_uri => str('subschema1.json'), document => shallow($document1), # still here! there is no reason to forget about it specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema2.json' => { path => '/allOf/1', canonical_uri => str('subschema2.json'), document => shallow($document1), # still here! there is no reason to forget about it specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema3.json' => { path => '/anyOf/0', canonical_uri => str('subschema3.json'), document => shallow($document2), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema4.json' => { path => '/anyOf/1', canonical_uri => str('subschema4.json'), document => shallow($document2), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'resources in second schema are indexed; all resources from first schema are preserved except uri=""', ); }; subtest 'multiple documents, each using canonical_uri = "", collisions in other resources' => sub { my $js = JSON::Schema::Modern->new; my $schema1 = { allOf => [ { '$id' => 'subschema1.json', type => 'string' }, { '$id' => 'subschema2.json', type => 'number' }, ], }; my $schema2 = { anyOf => [ { '$id' => 'subschema1.json', type => 'string' }, { '$id' => 'subschema3.json', type => 'number' }, ], }; cmp_result( $js->evaluate(1, $schema1)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/type', absoluteKeywordLocation => 'subschema1.json#/type', error => 'got integer, not string', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschema 0 is not valid', }, ], }, 'evaluation of schema1', ); my $resource_index1 = +{ $js->_resource_index }; my $document1 = $resource_index1->{''}{document}; cmp_result( $resource_index1, { '' => { path => '', canonical_uri => str(''), document => shallow($document1), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema1.json' => { path => '/allOf/0', canonical_uri => str('subschema1.json'), document => shallow($document1), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'subschema2.json' => { path => '/allOf/1', canonical_uri => str('subschema2.json'), document => shallow($document1), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'resources in initial schema are indexed', ); cmp_result( $js->evaluate(1, $schema2)->TO_JSON, { valid => false, errors => [ { error => re(qr/^EXCEPTION: uri "subschema1.json" conflicts with an existing schema resource/), instanceLocation => '', keywordLocation => '', }, ], }, 'schema2 cannot be evaluated - an internal $id collides with an existing resource', ); }; subtest 'resource collisions in canonical uris' => sub { my $js = JSON::Schema::Modern->new; $js->add_schema({ '$id' => 'https://foo.com/x/y/z' }); cmp_result( $js->evaluate(1, { '$id' => 'https://foo.com', anyOf => [ { '$id' => '/x/y/z' } ] })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => re(qr{^EXCEPTION: uri "https://foo.com/x/y/z" conflicts with an existing schema resource}), } ], }, 'detected collision between a document\'s initial uri and a document\'s subschema\'s uri', ); $js = JSON::Schema::Modern->new; $js->add_schema({ '$id' => 'https://foo.com', anyOf => [ { '$id' => '/x/y/z' } ], }); cmp_result( $js->evaluate(1, { allOf => [ { '$id' => 'https://foo.com/x/y/z' } ] })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => re(qr{^EXCEPTION: uri "https://foo.com/x/y/z" conflicts with an existing schema resource}), } ], }, 'detected collision between two document subschema uris', ); }; subtest 'relative uri in $id' => sub { cmp_result( JSON::Schema::Modern->new->evaluate( 1, { '$id' => 'foo/bar/baz.json', type => 'object', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/type', absoluteKeywordLocation => 'foo/bar/baz.json#/type', error => 'got integer, not object', }, ], }, 'root schema location is correctly identified', ); cmp_result( JSON::Schema::Modern->new->evaluate( [ 1, [ 2, 3 ] ], { '$id' => 'foo/bar/baz.json', type => [ 'integer', 'array' ], items => { '$ref' => '#' }, }, )->TO_JSON, { valid => true, }, 'properly able to traverse a recursive schema using a relative $id', ); }; done_testing; JSON-Schema-Modern-0.609/t/evaluate_json_string.t000640 000766 000024 00000002535 15001302761 022045 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use if "$]" >= 5.022, experimental => 're_strict'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Fatal; use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new; like(ref($js->_json_decoder), qr/^(?:Cpanel::JSON::XS|JSON::PP)$/, 'we have a JSON decoder'); is( exception { ok($js->evaluate_json_string('true', {})->valid, 'json data "true" is evaluated successfully'); }, undef, 'no exceptions in evaluate_json_string on good json', ); is( exception { cmp_result( $js->evaluate_json_string('blargh', {})->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => re(qr/malformed JSON string/), }, ], }, 'evaluating bad json data returns false, with error', ); }, undef, 'no exceptions in evaluate_json_string on bad json', ); done_testing; JSON-Schema-Modern-0.609/t/serialization.t000640 000766 000024 00000011563 15001302761 020476 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Needs qw(Sereal::Encoder Sereal::Decoder); use Test::Warnings qw(:no_end_test had_no_warnings); use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new( collect_annotations => 1, scalarref_booleans => 1, stringy_numbers => 1, strict => 0, max_traversal_depth => 42, specification_version => 'draft2019-09', ); my $metaschema = { '$id' => 'https://my_metaschema', '$schema' => 'https://json-schema.org/draft/2020-12/schema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/format-annotation' => true, }, }; my $schema = { '$id' => 'https://my_schema', '$schema' => 'https://my_metaschema', type => 'number', format => 'ipv4', unknown => 1, properties => { hello => false }, contentMediaType => 'application/json', contentSchema => {}, }; $js->add_schema($metaschema); $js->add_schema($schema); cmp_result( $js->evaluate($schema, {})->TO_JSON, { valid => true }, 'evaluated against an empty schema', ); cmp_result( $js->evaluate(1, 'https://my_schema')->TO_JSON, my $result = { valid => true, annotations => [ map +{ instanceLocation => '', keywordLocation => '/'.$_, absoluteKeywordLocation => 'https://my_schema#/'.$_, annotation => $schema->{$_}, }, 'format', sort qw(type unknown properties contentMediaType contentSchema), ], }, 'evaluate data against schema with custom dialect; format and unknown keywords are collected as annotations', ); cmp_result( $js->evaluate('foo', 'https://my_schema')->TO_JSON, $result, 'evaluate data against schema with custom dialect; format-annotation is used', ); my @serialized_attributes = sort qw( specification_version output_format short_circuit max_traversal_depth validate_formats validate_content_schemas collect_annotations scalarref_booleans stringy_numbers strict _resource_index _vocabulary_classes _metaschema_vocabulary_classes ); my $frozen = $js->FREEZE(undef); cmp_deeply( [ sort keys %$frozen ], [ sort @serialized_attributes ], 'frozen object contains all the right keys', ); my $thawed = JSON::Schema::Modern->THAW(undef, $frozen); cmp_deeply( [ sort keys %$thawed ], [ sort @serialized_attributes ], 'thawed object contains all the right keys', ); $frozen = Sereal::Encoder->new({ freeze_callbacks => 1 })->encode($js); Sereal::Decoder->new->decode($frozen, $thawed); cmp_result( $thawed->evaluate($schema, {})->TO_JSON, { valid => true }, 'evaluate again against an empty schema', ); cmp_result( $thawed->evaluate('hi', 'https://my_schema')->TO_JSON, { valid => true, annotations => [ map +{ instanceLocation => '', keywordLocation => '/'.$_, absoluteKeywordLocation => 'https://my_schema#/'.$_, annotation => $schema->{$_}, }, 'format', sort qw(type unknown properties contentMediaType contentSchema), ], }, 'in thawed object, evaluate data against schema with custom dialect; format and unknown keywords are collected as annotations', ); $frozen = Sereal::Encoder->new({ freeze_callbacks => 1 })->encode($js); $thawed = Sereal::Decoder->new->decode($frozen); cmp_result( $thawed->evaluate($schema, {})->TO_JSON, { valid => true }, 'evaluate again against an empty schema', ); ok($thawed->_get_vocabulary_class('https://json-schema.org/draft/2020-12/vocab/core'), 'core vocabulary_class for a different spec version works in a thawed object'); ok($thawed->_get_vocabulary_class('https://json-schema.org/draft/2020-12/vocab/format-assertion'), 'format-assertion vocabulary_class works in a thawed object'); ok($thawed->_get_metaschema_vocabulary_classes('https://json-schema.org/draft/2020-12/schema'), 'metaschema_vocabulary_classes works in a thawed object'); ok($thawed->get_media_type('application/json'), 'media_type works in a thawed object'); ok($thawed->get_encoding('base64'), 'encoding works in a thawed object'); # now try to thaw the file in a new process and run some more tests if ("$]" >= '5.022' or $^O ne 'MSWin32') { open my $child_in, '|-:raw', $^X, (-d 'blib' ? '-Mblib' : '-Ilib'), 't/read_serialized_file'; print $child_in $frozen; close $child_in; my $hub = Test2::API::test2_stack->top; $hub->set_count($hub->count + ($ENV{AUTHOR_TESTING} ? 2 : 1)); is($? >> 8, 0, 'child process finished successfully'); } had_no_warnings() if $ENV{AUTHOR_TESTING}; done_testing; JSON-Schema-Modern-0.609/t/checksums.t000640 000766 000024 00000004511 15001302761 017601 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::More 0.96; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Digest::MD5 'md5_hex'; use Path::Tiny; foreach my $line () { chomp $line; my ($filename, $checksum) = split / /, $line, 2; is(md5_hex(path($filename)->slurp_raw), $checksum, 'checksum for '.$filename.' is correct') or diag $filename.' is not what was shipped in the distribution!'; } done_testing; __DATA__ share/LICENSE 82b044426b4e3998d7eb085d98a5f916 share/draft2019-09/meta/applicator.json fd08fc5b4c3bd23ae19c919c62b86551 share/draft2019-09/meta/content.json 50fe3f49e909fb38f2bf35022139d174 share/draft2019-09/meta/core.json b4c0e7eac5bd74641d464ebb6377e8cf share/draft2019-09/meta/format.json 067f8aa16b1e4b8f6b3c352e42ce7a04 share/draft2019-09/meta/meta-data.json f1d30b664cc43acf7d14cce61629cec8 share/draft2019-09/meta/validation.json 9d955e385dbc9cd1d2cb609725c22836 share/draft2019-09/output/schema.json bf63c8c7e3b4aa8786c6f7c27c28121f share/draft2019-09/schema.json 235e1fd47201b751194d9e8e90969ce4 share/draft2020-12/meta/applicator.json 835180064e52815df939c2118814d80d share/draft2020-12/meta/content.json 43fd532b134825d343a9be5aa5610234 share/draft2020-12/meta/core.json 8e6829848b79f6d6952e888b4291a639 share/draft2020-12/meta/format-annotation.json 12e1bd6b2af5bdd67240bcb5efc473ae share/draft2020-12/meta/format-assertion.json c78476a32441e52b48f13027a37fcfa6 share/draft2020-12/meta/meta-data.json c416e310b2648f291e51992b7dc012ab share/draft2020-12/meta/unevaluated.json 9e4dddcbb0581b939b686048713a1b8e share/draft2020-12/meta/validation.json a5a6bc93fa352985fc455e6325237f9c share/draft2020-12/output/schema.json 6efc8e121569c98060dc754e88c52113 share/draft2020-12/schema.json e32920982620b2b43803dc82d4640b50 share/draft4/schema.json c6be0c4792c7455a526f0e0cc9eb7d25 share/draft6/schema.json 0376a64fd48e524336d37410c50f7b74 share/draft7/schema.json d6f6ffd262250e16b20f687b94a08bdc JSON-Schema-Modern-0.609/t/errors.t000640 000766 000024 00000131501 15001302761 017130 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new(short_circuit => 0); my $js_short = JSON::Schema::Modern->new(short_circuit => 1); subtest 'multiple types' => sub { my $result = $js->evaluate(true, { type => ['string','number'] }); ok(!$result->valid, 'type returned false'); is($result->error_count, 1, 'got error count'); cmp_result( [ $result->errors ], [ all( isa('JSON::Schema::Modern::Error'), methods( instance_location => '', keyword_location => '/type', absolute_keyword_location => undef, error => 'got boolean, not one of string, number', ), ), ], 'correct error generated from type', ); cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/type', error => 'got boolean, not one of string, number', }, ], }, 'result object serializes correctly', ); }; subtest 'multipleOf' => sub { cmp_result( $js->evaluate(3, { multipleOf => 2 })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/multipleOf', error => 'value is not a multiple of 2', }, ], }, 'correct error generated from multipleOf', ); }; subtest 'uniqueItems' => sub { cmp_result( $js->evaluate([qw(a b c d c)], { uniqueItems => true })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/uniqueItems', error => 'items at indices 2 and 4 are not unique', }, ], }, 'correct error generated from uniqueItems', ); }; subtest 'allOf, not, and false schema' => sub { cmp_result( $js->evaluate( my $data = 1, my $schema = { allOf => [ true, false, { not => { not => false } } ] }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/1', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf/2/not', error => 'subschema is valid', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschemas 1, 2 are not valid', }, ], }, 'correct errors with locations; did not collect errors inside "not"', ); cmp_result( $js_short->evaluate($data, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/1', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschema 1 is not valid', }, ], }, 'short-circuited results contain fewer errors', ); }; subtest 'anyOf keeps all errors for false paths when invalid, discards errors for false paths when valid' => sub { cmp_result( $js->evaluate( my $data = 1, my $schema = { anyOf => [ false, false ] }, )->TO_JSON, my $result = { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/anyOf/0', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/anyOf/1', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/anyOf', error => 'no subschemas are valid', }, ], }, 'correct errors with locations; did not collect errors inside "not"', ); cmp_result( $js_short->evaluate($data, $schema)->TO_JSON, $result, 'short-circuited results contain the same errors (short-circuiting not possible)', ); cmp_result( $result = $js->evaluate(1, { anyOf => [ false, true ], not => true })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/not', error => 'subschema is true', }, ], }, 'did not collect errors from failure paths from successful anyOf', ); cmp_result( $js->evaluate(1, { anyOf => [ false, true ] })->TO_JSON, { valid => true }, 'no errors collected for true validation', ); }; subtest 'applicators with non-boolean subschemas, discarding intermediary errors - items' => sub { my $result = $js->evaluate( my $data = [ 1, 2 ], my $schema = { items => { anyOf => [ { minimum => 2 }, { allOf => [ { maximum => -1 }, { maximum => 0 } ] }, ] }, }, ); # - evaluate /items on instance '' # - evaluate /items on instance /0 # - evaluate /items/anyOf on instance /0 # - evaluate /items/anyOf/0 on instance /0 # - evaluate /items/anyOf/0/minimum on instance /0 FAIL # /items/anyOf/0 FAILS # - evaluate /items/anyOf/1 on instance /0 # - evaluate /items/anyOf/1/allOf on instance /0 # - evaluate /items/anyOf/1/allOf/0 on instance /0 # - evaluate /items/anyOf/1/allOf/0/maximum on instance /0 FAIL # - evaluate /items/anyOf/1/allOf/1 on instance /0 # - evaluate /items/anyOf/1/allOf/1/maximum on instance /0 FAIL # /items/anyOf/1/allOf FAILS # /items/anyOf/1 FAILS (no message) # /items/anyOf FAILS # /items FAILS on instance /0 (no message) # - evaluate /items on instance /1 # - evaluate /items/anyOf on instance /1 # - evaluate /items/anyOf/0 on instance /1 # - evaluate /items/anyOf/0/minimum on instance /1 PASS # /items/anyOf/0 PASSES # - evaluate /items/anyOf/1 on instance /1 # - evaluate /items/anyOf/1/allOf on instance /1 # - evaluate /items/anyOf/1/allOf/0 on instance /1 # - evaluate /items/anyOf/1/allOf/0/maximum on instance /1 FAIL # - evaluate /items/anyOf/1/allOf/1 on instance /1 # - evaluate /items/anyOf/1/allOf/1/maximum on instance /1 FAIL # /items/anyOf/1/allOf FAILS # /items/anyOf/1 FAILS (no message) # /items/anyOf PASSES -- all failures above are discarded # /items PASSES on instance /1 # /items FAILS (across all instances) # entire schema FAILS cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/items/anyOf/0/minimum', error => 'value is less than 2', }, { instanceLocation => '/0', keywordLocation => '/items/anyOf/1/allOf/0/maximum', error => 'value is greater than -1', }, { instanceLocation => '/0', keywordLocation => '/items/anyOf/1/allOf/1/maximum', error => 'value is greater than 0', }, { instanceLocation => '/0', keywordLocation => '/items/anyOf/1/allOf', error => 'subschemas 0, 1 are not valid', }, { instanceLocation => '/0', keywordLocation => '/items/anyOf', error => 'no subschemas are valid', }, # these errors are discarded because /items/anyOf passes on instance /1 #{ # instanceLocation => '/1', # keywordLocation => '/items/anyOf/1/allOf/0/maximum', # error => 'value is greater than -1', #}, #{ # instanceLocation => '/1', # keywordLocation => '/items/anyOf/1/allOf/1/maximum', # error => 'value is greater than 0', #}, #{ # instanceLocation => '/1', # keywordLocation => '/items/anyOf/1/allOf', # error => 'subschemas 0, 1 are not valid', #}, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'collected all errors from subschemas for failing branches only (passing branches discard errors)', ); cmp_result( $js_short->evaluate($data, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/items/anyOf/0/minimum', error => 'value is less than 2' }, { instanceLocation => '/0', keywordLocation => '/items/anyOf/1/allOf/0/maximum', error => 'value is greater than -1', }, { instanceLocation => '/0', keywordLocation => '/items/anyOf/1/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '/0', keywordLocation => '/items/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'short-circuited results contain fewer errors', ); }; subtest 'applicators with non-boolean subschemas, discarding intermediary errors - contains' => sub { my $result = $js->evaluate( my $data = [ { foo => 1 }, { bar => 2 }, ], my $schema = { not => true, contains => { properties => { foo => false, # if 'foo' is present, then we fail }, }, }, ); # - evaluate /not on instance '' # - evaluate subschema "true" - PASS # /not FAILS. # - evaluate /contains on instance '' # - evaluate /contains on instance /0 # - evaluate /contains/properties on instance /0 # - evaluate /contains/properties/foo on instance /0/foo # schema is FALSE. # /contains/properties FAILS # /contains does not match on instance /0 # - evaluate /contains on instance /1 # - evaluate /contains/properties on instance /1 # - evaluate /contains/properties/foo on instance /1/foo - does not exist. # /contains/properties/foo PASSES # /contains/properties PASSES # /contains matches on instance /1 # /contains has at least 1 match; it PASSES # entire schema FAILS cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/not', error => 'subschema is true', }, # these errors are discarded because /contains passes on instance /1 #{ # instanceLocation => '/0/foo', # keywordLocation => '/contains/properties/foo', # error => 'subschema is false', #}, #{ # instanceLocation => '/0', # keywordLocation => '/contains/properties', # error => 'not all properties are valid', #}, ], }, 'collected all errors from subschemas for failing branches only (passing branches discard errors)', ); cmp_result( $js_short->evaluate($data, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/not', error => 'subschema is true', }, ], }, 'short-circuited results contain the same errors', ); }; subtest 'errors with $refs' => sub { my $result = $js->evaluate( [ { x => 1 }, { x => 2 }, { x => 3 } ], { '$defs' => { mydef => { type => 'integer', minimum => 5, '$ref' => '#/$defs/myint', }, myint => { multipleOf => 5, }, }, items => { properties => { x => { '$ref' => '#/$defs/mydef', maximum => 2, }, }, } }, ); # evaluation order: # /items/properties/x/$ref (mydef) /$ref (myint) /multipleOf # /items/properties/x/$ref (mydef) /type # /items/properties/x/$ref (mydef) /minimum # /items/properties/x/maximum cmp_result( $result->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0/x', keywordLocation => '/items/properties/x/$ref/$ref/multipleOf', absoluteKeywordLocation => '#/$defs/myint/multipleOf', error => 'value is not a multiple of 5', }, { instanceLocation => '/0/x', keywordLocation => '/items/properties/x/$ref/minimum', absoluteKeywordLocation => '#/$defs/mydef/minimum', error => 'value is less than 5', }, { instanceLocation => '/0', keywordLocation => '/items/properties', error => 'not all properties are valid', }, { instanceLocation => '/1/x', keywordLocation => '/items/properties/x/$ref/$ref/multipleOf', absoluteKeywordLocation => '#/$defs/myint/multipleOf', error => 'value is not a multiple of 5', }, { instanceLocation => '/1/x', keywordLocation => '/items/properties/x/$ref/minimum', absoluteKeywordLocation => '#/$defs/mydef/minimum', error => 'value is less than 5', }, { instanceLocation => '/1', keywordLocation => '/items/properties', error => 'not all properties are valid', }, { instanceLocation => '/2/x', keywordLocation => '/items/properties/x/$ref/$ref/multipleOf', absoluteKeywordLocation => '#/$defs/myint/multipleOf', error => 'value is not a multiple of 5', }, { instanceLocation => '/2/x', keywordLocation => '/items/properties/x/$ref/minimum', absoluteKeywordLocation => '#/$defs/mydef/minimum', error => 'value is less than 5', }, { instanceLocation => '/2/x', keywordLocation => '/items/properties/x/maximum', error => 'value is greater than 2', }, { instanceLocation => '/2', keywordLocation => '/items/properties', error => 'not all properties are valid', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'errors have correct absolute keyword location via $ref', ); }; subtest 'const and enum' => sub { cmp_result( $js->evaluate( { foo => { a => { b => { c => { d => 1 } } } } }, { properties => { foo => { allOf => [ { const => { a => { b => { c => { d => 2 } } } } }, { enum => [ 0, 'whargarbl', { a => { b => { c => { d => 2 } } } } ] }, ], } }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo', keywordLocation => '/properties/foo/allOf/0/const', error => 'value does not match (at \'/a/b/c/d\': integers not equal)', }, { instanceLocation => '/foo', keywordLocation => '/properties/foo/allOf/1/enum', error => 'value does not match (from enum 0 at \'\': wrong type: object vs integer; from enum 1 at \'\': wrong type: object vs string; from enum 2 at \'/a/b/c/d\': integers not equal)', }, { instanceLocation => '/foo', keywordLocation => '/properties/foo/allOf', error => 'subschemas 0, 1 are not valid', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'got details about object differences in errors from const and enum', ); }; subtest 'exceptions' => sub { cmp_result( (my $result = $js->evaluate_json_string('[ 1, 2, 3, whargarbl ]', true))->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => re(qr/malformed JSON string/), }, ], }, 'attempting to evaluate a json string returns the exception as an error', ); ok($result->exception, 'exception flag is true on the result'); cmp_result( ($result = $js->evaluate( { x => 'hello' }, { allOf => [ { properties => { x => 1 } }, { properties => { x => 'hi' } }, ], } ))->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/properties/x', error => 'invalid schema type: integer', }, { instanceLocation => '', keywordLocation => '/allOf/1/properties/x', error => 'invalid schema type: string', }, ], }, 'a subschema of an invalid type returns an error at the right position, and evaluation continues', ); ok($result->exception, 'exception flag is true on the result'); cmp_result( ($result = $js->evaluate( 1, { allOf => [ { type => 'whargarbl' }, { type => 'whoops' }, ], } ))->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/type', error => 'unrecognized type "whargarbl"', }, { instanceLocation => '', keywordLocation => '/allOf/1/type', error => 'unrecognized type "whoops"', }, ], }, 'invalid argument to "type" returns an error at the right position, and evaluation continues', ); ok($result->exception, 'exception flag is true on the result'); }; subtest 'errors after crossing multiple $refs using $id and $anchor' => sub { cmp_result( $js->evaluate( 1, { '$id' => 'base.json', '$defs' => { def1 => { '$comment' => 'canonical uri: "def1.json"', '$id' => 'def1.json', '$ref' => 'base.json#/$defs/myint', type => 'integer', maximum => -1, minimum => 5, }, myint => { '$comment' => 'canonical uri: "def2.json"', '$id' => 'def2.json', '$ref' => 'base.json#my_not', multipleOf => 5, exclusiveMaximum => 1, }, mynot => { '$comment' => 'canonical uri: "base.json#/$defs/mynot"', '$anchor' => 'my_not', '$ref' => 'http://localhost:4242/object.json', not => true, }, myobject => { '$id' => 'http://localhost:4242/object.json', type => 'object', anyOf => [ false ], }, }, '$ref' => '#/$defs/def1', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/$ref/$ref/$ref/type', absoluteKeywordLocation => 'http://localhost:4242/object.json#/type', error => 'got integer, not object', }, { instanceLocation => '', keywordLocation => '/$ref/$ref/$ref/$ref/anyOf/0', absoluteKeywordLocation => 'http://localhost:4242/object.json#/anyOf/0', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/$ref/$ref/$ref/$ref/anyOf', absoluteKeywordLocation => 'http://localhost:4242/object.json#/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/$ref/$ref/$ref/not', absoluteKeywordLocation => 'base.json#/$defs/mynot/not', error => 'subschema is true', }, { instanceLocation => '', keywordLocation => '/$ref/$ref/multipleOf', absoluteKeywordLocation => 'def2.json#/multipleOf', error => 'value is not a multiple of 5', }, { instanceLocation => '', keywordLocation => '/$ref/$ref/exclusiveMaximum', absoluteKeywordLocation => 'def2.json#/exclusiveMaximum', error => 'value is greater than or equal to 1', }, { instanceLocation => '', keywordLocation => '/$ref/maximum', absoluteKeywordLocation => 'def1.json#/maximum', error => 'value is greater than -1', }, { instanceLocation => '', keywordLocation => '/$ref/minimum', absoluteKeywordLocation => 'def1.json#/minimum', error => 'value is less than 5', }, ], }, 'errors have correct absolute keyword location via $ref', ); cmp_result( $js->evaluate( 1, { '$id' => 'http://localhost:1234/hello', '$defs' => { foo => { '$id' => 'http://localhost:1234/a/b.json', '$defs' => { bar => { '$anchor' => 'my_anchor', '$defs' => { baz => { '$anchor' => 'another_anchor', not => true }, }, }, }, }, }, '$ref' => 'http://localhost:1234/hello#/$defs/foo/$defs/bar/$defs/baz', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/not', absoluteKeywordLocation => 'http://localhost:1234/a/b.json#/$defs/bar/$defs/baz/not', error => 'subschema is true', }, ], }, 'absolute keyword location is correct, even when not used in the $ref', ); }; subtest 'unresolvable $ref to a local resource' => sub { cmp_result( (my $result = $js->evaluate( 1, { '$ref' => '#/$defs/myint', '$defs' => { myint => { '$ref' => '#/$defs/does-not-exist', }, }, anyOf => [ false ], }, ))->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/$ref', absoluteKeywordLocation => '#/$defs/myint/$ref', error => 'EXCEPTION: unable to find resource "#/$defs/does-not-exist"', }, ], }, 'error for a bad $ref reports the correct absolute location that was referred to', ); ok($result->exception, 'exception flag is true on the result'); }; subtest 'unresolvable $ref to a remote resource' => sub { # new evaluator, with no resources remembered my $js = JSON::Schema::Modern->new; cmp_result( (my $result = $js->evaluate( 1, { '$id' => 'http://localhost:4242/foo/bar/top_id.json', '$ref' => '/baz/myint.json', '$defs' => { myint => { '$id' => '/baz/myint.json', '$ref' => 'does-not-exist.json', }, }, anyOf => [ false ], }, ))->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/$ref', absoluteKeywordLocation => 'http://localhost:4242/baz/myint.json#/$ref', error => 'EXCEPTION: unable to find resource "http://localhost:4242/baz/does-not-exist.json"', }, ], }, 'error for a bad $ref reports the correct absolute location that was referred to', ); ok($result->exception, 'exception flag is true on the result'); }; subtest 'unresolvable $ref to plain-name fragment' => sub { cmp_result( (my $result = $js->evaluate(1, { '$ref' => '#nowhere' }))->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref', error => 'EXCEPTION: unable to find resource "#nowhere"', }, ], }, 'properly handled a bad $ref to an anchor', ); ok($result->exception, 'exception flag is true on the result'); }; subtest 'abort due to a schema error' => sub { cmp_result( $js->evaluate( 1, { oneOf => [ { type => 'number' }, { type => 'string' }, { type => 'whargarbl' }, ], } )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/oneOf/2/type', error => 'unrecognized type "whargarbl"', }, ], }, 'exception inside a oneOf (where errors are localized) are still included in the result', ); }; subtest 'sorted property names' => sub { cmp_result( $js->evaluate( { foo => 1, bar => 1, baz => 1, hello => 1 }, { properties => { foo => false, bar => false, }, additionalProperties => false, } )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/bar', keywordLocation => '/properties/bar', error => 'property not permitted', }, { instanceLocation => '/foo', keywordLocation => '/properties/foo', error => 'property not permitted', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, { instanceLocation => '/baz', keywordLocation => '/additionalProperties', error => 'additional property not permitted', }, { instanceLocation => '/hello', keywordLocation => '/additionalProperties', error => 'additional property not permitted', }, { instanceLocation => '', keywordLocation => '/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'property names are considered in sorted order', ); }; subtest 'bad regex in schema' => sub { cmp_result( $js->evaluate( { my_pattern => 'foo', my_patternProperties => { foo => 1 }, }, my $schema = { type => 'object', properties => { my_pattern => { type => 'string', pattern => '(', }, my_patternProperties => { type => 'object', patternProperties => { '(' => true }, additionalProperties => false, }, my_runtime_pattern => { type => 'string', pattern => '\p{main::IsFoo}', # qr/$pattern/ will not find this error, but m/$pattern/ will }, }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/properties/my_pattern/pattern', error => re(qr/^Unmatched \( in regex/), }, { instanceLocation => '', keywordLocation => '/properties/my_patternProperties/patternProperties/(', error => re(qr/^Unmatched \( in regex/), }, # Note no error for missing IsFoo ], }, 'bad "pattern" and "patternProperties" regexes are properly noted in error', ); cmp_result( $js->evaluate( { my_runtime_pattern => 'foo' }, $schema = { $schema->%{type}, properties => +{ $schema->{properties}->%{my_runtime_pattern} }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/my_runtime_pattern', keywordLocation => '/properties/my_runtime_pattern/pattern', # in 5.28 and earlier: Can't find Unicode property definition "IsFoo" # in 5.30 and later: Unknown user-defined property name \p{main::IsFoo} error => re(qr/^EXCEPTION: .*property.*IsFoo/), }, ], }, 'bad "pattern" regex is properly noted in error', ); no warnings 'once'; *IsFoo = sub { "0066\n006F\n" }; # accepts 'f', 'o' cmp_result( $js->evaluate( { my_runtime_pattern => 'foo' }, $schema, )->TO_JSON, { valid => true, }, '"pattern" regex is now valid, due to the Unicode property becoming defined', ); }; subtest 'JSON pointer escaping' => sub { cmp_result( $js->evaluate( { '{}' => { 'my~tilde/slash-property' => 1 } }, my $schema = { '$defs' => { mydef => { properties => { '{}' => { properties => { 'my~tilde/slash-property' => false, }, patternProperties => { '/' => { minimum => 6 }, '[~/]' => { minimum => 7 }, '~' => { minimum => 5 }, '~.*/' => false, }, }, }, }, }, '$ref' => '#/$defs/mydef', }, )->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '/{}/my~0tilde~1slash-property', keywordLocation => '/$ref/properties/{}/properties/my~0tilde~1slash-property', absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/properties/my~0tilde~1slash-property', error => 'property not permitted', }, { instanceLocation => '/{}', keywordLocation => '/$ref/properties/{}/properties', absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/properties', error => 'not all properties are valid', }, { instanceLocation => '/{}/my~0tilde~1slash-property', keywordLocation => '/$ref/properties/{}/patternProperties/~1/minimum', absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/~1/minimum', # / error => 'value is less than 6', }, { instanceLocation => '/{}/my~0tilde~1slash-property', keywordLocation => '/$ref/properties/{}/patternProperties/[~0~1]/minimum', absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/%5B~0~1%5D/minimum', # [~/] error => 'value is less than 7', }, { instanceLocation => '/{}/my~0tilde~1slash-property', keywordLocation => '/$ref/properties/{}/patternProperties/~0/minimum', absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/~0/minimum', # ~ error => 'value is less than 5', }, { instanceLocation => '/{}/my~0tilde~1slash-property', keywordLocation => '/$ref/properties/{}/patternProperties/~0.*~1', absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties/~0.*~1', # ~.*/ error => 'property not permitted', }, { instanceLocation => '/{}', keywordLocation => '/$ref/properties/{}/patternProperties', absoluteKeywordLocation => '#/$defs/mydef/properties/%7B%7D/patternProperties', error => 'not all properties are valid', }, { instanceLocation => '', keywordLocation => '/$ref/properties', absoluteKeywordLocation => '#/$defs/mydef/properties', error => 'not all properties are valid', }, ], }, 'JSON pointers are properly escaped; URIs doubly so', ); cmp_result( $js->evaluate( { '{}' => { 'my~tilde/slash-property' => 1 } }, $schema->{'$defs'}{mydef}, )->TO_JSON, { valid => false, errors => [ map +{ error => $_->{error}, instanceLocation => $_->{instanceLocation}, keywordLocation => $_->{keywordLocation} =~ s{^/\$ref}{}r, }, @$errors ], }, 'absoluteKeywordLocation is omitted when paths are the same, not counting uri encoding', ); cmp_result( $js->evaluate( { '{}' => { 'my~tilde/slash-property' => 1 } }, { '$defs' => { mydef => { properties => { '{}' => { patternProperties => { 'a{' => { minimum => 2 }, # this is a broken regex }, }, }, }, }, '$ref' => '#/$defs/mydef', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$defs/mydef/properties/{}/patternProperties/a{', error => re(qr/^Unescaped left brace in regex is (deprecated|illegal|passed through)/), }, ], }, # all the other _schema_path_suffix cases are tested in the earlier test case 'use of _schema_path_suffix in a fatal error', ) if "$]" >= 5.022; }; subtest 'absoluteKeywordLocation' => sub { cmp_result( JSON::Schema::Modern->new(max_traversal_depth => 1)->evaluate( [ [ 1 ] ], { items => { '$ref' => '#' } }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/items/$ref', absoluteKeywordLocation => '', error => 'EXCEPTION: maximum evaluation depth (1) exceeded', }, ], }, 'absoluteKeywordLocation is included when different from instanceLocation, even when empty', ); cmp_result( $js->evaluate(1, { '$ref' => '#does_not_exist' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref', error => 'EXCEPTION: unable to find resource "#does_not_exist"', }, ], }, 'absoluteKeywordLocation is not included when the path equals keywordLocation, even if a $ref is present', ); $js->add_schema(false); cmp_result( $js->evaluate(1, '#')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'subschema is false', }, ], }, 'absoluteKeywordLocation is never "#"', ); cmp_result( $js->evaluate( 1, my $schema = { '$id' => 'https://localhost:1234/bloop', allOf => [ { '$id' => 'foo.json', type => 'object', }, { '$id' => 'bar/', allOf => [ { '$id' => 'alpha', type => 'object', }, ], }, { type => 'object' }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/type', absoluteKeywordLocation => 'https://localhost:1234/foo.json#/type', error => 'got integer, not object', }, { instanceLocation => '', keywordLocation => '/allOf/1/allOf/0/type', absoluteKeywordLocation => 'https://localhost:1234/bar/alpha#/type', error => 'got integer, not object', }, { instanceLocation => '', keywordLocation => '/allOf/1/allOf', absoluteKeywordLocation => 'https://localhost:1234/bar/#/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '', keywordLocation => '/allOf/2/type', absoluteKeywordLocation => 'https://localhost:1234/bloop#/allOf/2/type', error => 'got integer, not object', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://localhost:1234/bloop#/allOf', error => 'subschemas 0, 1, 2 are not valid', }, ], }, 'absoluteKeywordLocation reflects the canonical schema uri as it changes when passing through $id', ); $schema->{'$id'} = 'https://example.com'; $schema->{allOf}[2]{'$id'} = '#my_anchor2'; cmp_result( JSON::Schema::Modern->new(specification_version => 'draft7')->evaluate(1, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/type', absoluteKeywordLocation => 'https://example.com/foo.json#/type', error => 'got integer, not object', }, { instanceLocation => '', keywordLocation => '/allOf/1/allOf/0/type', absoluteKeywordLocation => 'https://example.com/bar/alpha#/type', error => 'got integer, not object', }, { instanceLocation => '', keywordLocation => '/allOf/1/allOf', absoluteKeywordLocation => 'https://example.com/bar/#/allOf', error => 'subschema 0 is not valid', }, { instanceLocation => '', keywordLocation => '/allOf/2/type', absoluteKeywordLocation => 'https://example.com#/allOf/2/type', error => 'got integer, not object', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://example.com#/allOf', error => 'subschemas 0, 1, 2 are not valid', }, ], }, 'plain-name fragment in $id does not change canonical schema uri', ); }; subtest dependentRequired => sub { cmp_result( $js->evaluate(1, { dependentRequired => { foo => [ 1 ] } })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/dependentRequired/foo/0', error => 'element #0 is not a string', }, ], }, 'dependentRequired traversal error', ); }; subtest 'numbers in output' => sub { cmp_result( $js->evaluate( 5, { multipleOf => 1.23456789, maximum => 4.23456789, minimum => 6.23456789, exclusiveMaximum => 4.23456789, exclusiveMinimum => 6.23456789, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/multipleOf', error => 'value is not a multiple of 1.23456789', }, { instanceLocation => '', keywordLocation => '/maximum', error => 'value is greater than 4.23456789', }, { instanceLocation => '', keywordLocation => '/exclusiveMaximum', error => 'value is greater than or equal to 4.23456789', }, { instanceLocation => '', keywordLocation => '/minimum', error => 'value is less than 6.23456789', }, { instanceLocation => '', keywordLocation => '/exclusiveMinimum', error => 'value is less than or equal to 6.23456789', }, ], }, 'numbers in errors do not lose any digits of precision', ); }; subtest 'effective_base_uri and overriding starting locations' => sub { cmp_result( $js->evaluate( 5, { '$id' => 'foo', '$defs' => { bar => false }, '$ref' => '#/$defs/bar', not => true, }, { effective_base_uri => 'https://example.com', }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref', absoluteKeywordLocation => 'https://example.com/foo#/$defs/bar', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/not', absoluteKeywordLocation => 'https://example.com/foo#/not', error => 'subschema is true', }, ], }, 'error locations are relative to the effective_base_uri, but $ref usage is not restricted', ); # evaluating this document from its root would do nothing, as it is only definitions $js->add_schema('/api', { '$defs' => { alpha => { items => { '$ref' => '#/$defs/beta', }, }, beta => { not => true, }, }, }); cmp_result( $js->evaluate( [ 5 ], '/api#/$defs/alpha', { data_path => '/html/body/div/div/h1/div/p', # reported data location traversed_schema_path => '/some/other/document/$ref', # reported keywords passed through before we start effective_base_uri => 'https://example.com', # base uri to use for document locations }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/html/body/div/div/h1/div/p/0', keywordLocation => '/some/other/document/$ref/items/$ref/not', absoluteKeywordLocation => 'https://example.com/api#/$defs/beta/not', error => 'subschema is true', }, { instanceLocation => '/html/body/div/div/h1/div/p', keywordLocation => '/some/other/document/$ref/items', absoluteKeywordLocation => 'https://example.com/api#/$defs/alpha/items', error => 'subschema is not valid against all items', }, ], }, 'can alter locations with data_path, traversed_schema_path, effective_base_uri', ); }; subtest 'recommended_response' => sub { cmp_result( JSON::Schema::Modern::Result->new(valid => 1)->recommended_response, undef, 'recommended_response is not defined when there are no errors', ); my $result = $js->evaluate( { foo => 3 }, { type => 'object', properties => { foo => { type => 'integer', minimum => 5, }, }, }, ); cmp_result( $result->recommended_response, [ 400, q{'/foo': value is less than 5} ], 'recommended_response uses the first error in the result', ); my $result2 = $js->evaluate(1, { '$ref' => '#/$defs/does_not_exist' }); cmp_result( $result2->recommended_response, [ 500, 'Internal Server Error' ], 'recommended_response indicates an exception occurred', ); my $result3 = JSON::Schema::Modern::Result->new( valid => 0, errors => [ $result->errors, # TODO: I haven't implemented authentication in OpenAPI::Modern yet, so I'm not sure how # exactly these errors are going to look JSON::Schema::Modern::Error->new( depth => 0, mode => 'evaluate', keyword => 'authentication', instance_location => '/request/headers/Authentication', keyword_location => '/paths/foo/get/security', error => 'security check failed', recommended_response => [ 401, 'Unauthorized' ], ), ], ); cmp_result( $result3->recommended_response, [ 401, 'Unauthorized' ], 'recommended_response uses the one from the error that is explicitly set', ); }; subtest 'exclusiveMaximum, exclusiveMinimum across drafts' => sub { cmp_result( $js->evaluate(4, { maximum => 4, exclusiveMaximum => 4 })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/exclusiveMaximum', error => 'value is greater than or equal to 4', }, ], }, 'later drafts; errors are produced separately from the keywords', ); cmp_result( $js->evaluate(5, { maximum => 4, exclusiveMaximum => 4 })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/maximum', error => 'value is greater than 4', }, { instanceLocation => '', keywordLocation => '/exclusiveMaximum', error => 'value is greater than or equal to 4', }, ], }, 'later drafts; two errors can result', ); my $js = JSON::Schema::Modern->new(specification_version => 'draft4'); cmp_result( $js->evaluate(4, { maximum => 4, exclusiveMaximum => true })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/maximum', error => 'value is greater than or equal to 4', }, ], }, 'draft4: one error comes from maximum, but includes the exclusiveMaximum check', ); cmp_result( $js->evaluate(5, { maximum => 4, exclusiveMaximum => true })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/maximum', error => 'value is greater than or equal to 4', }, ], }, 'draft4: maximum + exclusiveMaximum checks are combined', ); cmp_result( $js->evaluate(4, { maximum => 4, exclusiveMaximum => false })->TO_JSON, { valid => true }, 'draft4: exclusive check uses the right boundary', ); cmp_result( $js->evaluate(5, { maximum => 4, exclusiveMaximum => false })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/maximum', error => 'value is greater than 4', }, ], }, 'draft4: maximum check is correct', ); }; done_testing; JSON-Schema-Modern-0.609/t/specification_version.t000640 000766 000024 00000033712 15001302761 022206 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Warnings qw(warnings :no_end_test had_no_warnings); use Test::Fatal; use lib 't/lib'; use Helper; { like( exception { ()= JSON::Schema::Modern->new(specification_version => 'ohhai')->evaluate(true, true) }, qr/^Value "ohhai" did not pass type constraint/, 'unrecognized $SPECIFICATION_VERSION', ); } subtest 'specification aliases' => sub { is( JSON::Schema::Modern->new(specification_version => '2020-12')->specification_version, 'draft2020-12', '2020-12 is an alias for draft2020-12', ); is( JSON::Schema::Modern->new(specification_version => '2019-09')->specification_version, 'draft2019-09', '2019-09 is an alias for draft2019-09', ); is( JSON::Schema::Modern->new(specification_version => '7')->specification_version, 'draft7', '7 is an alias for draft7', ); }; subtest '$ref and older specification versions' => sub { cmp_result( JSON::Schema::Modern->new->evaluate( true, { '$schema' => 'http://json-schema.org/draft-07/schema#', '$ref' => '#/definitions/foo', } )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema', error => '$schema and $ref cannot be used together in older drafts', }, ], }, '$schema and $ref cannot be used together, when $schema is too old', ); }; subtest '<= draft7: $ref in combination with any other keyword causes the other keywords to be ignored' => sub { cmp_result( JSON::Schema::Modern->new( specification_version => 'draft7', )->evaluate( 1, { allOf => [ true, { '$ref' => '#/allOf/0', maximum => 0, }, ], } )->TO_JSON, { valid => true }, 'keywords adjacent to $ref are not evaluated', ); }; subtest '$ref adjacent to a path used in a $ref' => sub { cmp_result( JSON::Schema::Modern->new(specification_version => 'draft7')->evaluate( true, { allOf => [ true, { anyOf => [ false, true ], '$ref' => '#/allOf/0', }, { # a reference that cannot be resolved '$ref' => '#/allOf/1/anyOf/1', }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/2/$ref', error => 'EXCEPTION: bad reference to "#/allOf/1/anyOf/1": not a schema', }, ], }, 'the presence of $ref also blocks the use of other $refs to adjacent locations', ); }; subtest '$defs support' => sub { cmp_result( JSON::Schema::Modern->new(specification_version => 'draft7')->evaluate( 1, my $schema = { '$defs' => 1, allOf => [ { '$ref' => '#/$defs/foo' } ], } )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/$ref', error => 'EXCEPTION: unable to find resource "#/$defs/foo"', }, ], }, '$defs is not recognized in <= draft7', ); cmp_result( JSON::Schema::Modern->new(specification_version => 'draft2019-09')->evaluate(1, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$defs', error => '$defs value is not an object', }, ], }, '$defs is supported in > draft7', ); }; subtest 'definitions support' => sub { my $schema; my @warnings = warnings { cmp_result( JSON::Schema::Modern->new(specification_version => 'draft2019-09')->evaluate( 1, $schema = { definitions => 1, allOf => [ { '$ref' => '#/definitions/foo' } ], } )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/$ref', error => 'EXCEPTION: unable to find resource "#/definitions/foo"', }, ], }, 'definitions is not recognized in >= draft2019-09', ); }; cmp_result( \@warnings, [ re(qr/^no-longer-supported "definitions" keyword present/) ], 'warned when using no-longer-supported keyword', ); cmp_result( JSON::Schema::Modern->new(specification_version => 'draft7')->evaluate(1, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/definitions', error => 'definitions value is not an object', }, ], }, 'definitions is supported in <= draft7', ); }; subtest 'dependencies, dependentRequired, dependentSchemas' => sub { my $js = JSON::Schema::Modern->new(specification_version => 'draft2019-09'); my $dependencies_schema; my @warnings = warnings { cmp_result( $js->evaluate( { alpha => 1, beta => 2 }, $dependencies_schema = { dependencies => { alpha => [ qw(a b c) ], beta => false, }, } )->TO_JSON, { valid => true }, 'dependencies is not recognized in >= draft2019-09', ); }; cmp_result( \@warnings, [ re(qr/^no-longer-supported "dependencies" keyword present/) ], 'warned when using no-longer-supported keyword', ); cmp_result( $js->evaluate( { alpha => 1, beta => 2 }, my $dependentRequired_schema = { dependentRequired => { alpha => [ qw(a b c) ], }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/dependentRequired/alpha', error => 'object is missing properties: a, b, c', }, { instanceLocation => '', keywordLocation => '/dependentRequired', error => 'not all dependencies are satisfied', }, ], }, 'dependentRequired is supported in >= draft2019-09', ); cmp_result( $js->evaluate( { alpha => 1, beta => 2 }, my $dependentSchemas_schema = { dependentSchemas => { beta => false, }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/dependentSchemas/beta', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/dependentSchemas', error => 'not all dependencies are satisfied', }, ], }, 'dependentSchemas is supported in >= draft2019-09', ); $js = JSON::Schema::Modern->new(specification_version => 'draft7'); cmp_result( $js->evaluate( { alpha => 1, beta => 2 }, $dependencies_schema, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/dependencies/alpha', error => 'object is missing properties: a, b, c', }, { instanceLocation => '', keywordLocation => '/dependencies/beta', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/dependencies', error => 'not all dependencies are satisfied', }, ], }, 'dependencies is supported in <= draft7', ); cmp_result( $js->evaluate( { alpha => 1, beta => 2 }, $dependentRequired_schema, )->TO_JSON, { valid => true }, 'dependentRequired is not recognized in <= draft7', ); cmp_result( $js->evaluate( { alpha => 1, beta => 2 }, $dependentSchemas_schema, )->TO_JSON, { valid => true }, 'dependentSchemas is not recognized in <= draft7', ); }; subtest 'prefixItems, items and additionalItems' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( [ 1, 2 ], { prefixItems => [ { maximum => 0 } ], items => { maximum => 1 }, } )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/prefixItems/0/maximum', error => 'value is greater than 0', }, { instanceLocation => '', keywordLocation => '/prefixItems', error => 'not all items are valid', }, { instanceLocation => '/1', keywordLocation => '/items/maximum', error => 'value is greater than 1', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'prefixitems+items works when specification_version >= draft2020-12', ); cmp_result( $js->evaluate( [ 1 ], { items => [ { maximum => 0 } ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/items', error => 'array form of "items" not supported in draft2020-12', }, ], }, 'array form of items not supported when specification_version >= draft2020-12', ); my @warnings = warnings { cmp_result( $js->evaluate( [ 1 ], { additionalItems => false }, )->TO_JSON, { valid => true }, 'additionalitems not recognized when specification_version >= draft2020-12', ); }; cmp_result( \@warnings, [ re(qr/^no-longer-supported "additionalItems" keyword present/) ], 'warned when using no-longer-supported keyword', ); cmp_result( JSON::Schema::Modern->new(specification_version => 'draft2019-09')->evaluate( [ 1 ], { prefixItems => [ { maximum => 0 } ] } )->TO_JSON, { valid => true }, 'prefixitems not supported when specification_version specifies other than draft2020-12', ); cmp_result( $js->evaluate( [ 1, 2, 3 ], { prefixItems => [ { maximum => 0 } ], items => [ { maximum => 1 } ], }, )->TO_JSON, { valid => false, errors => [ { error => "array form of \"items\" not supported in draft2020-12", instanceLocation => '', keywordLocation => '/items', }, ], }, 'array-based items in >= draft2020-12', ); @warnings = warnings { cmp_result( $js->evaluate( [ 1, 2, 3 ], { prefixItems => [ { maximum => 0 } ], additionalItems => { maximum => 1 }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/prefixItems/0/maximum', error => 'value is greater than 0', }, { instanceLocation => '', keywordLocation => '/prefixItems', error => 'not all items are valid', }, ], }, 'prefixItems + additionalItems', ); }; cmp_result( \@warnings, [ re(qr/^no-longer-supported "additionalItems" keyword present/) ], 'warned when using no-longer-supported keyword', ); cmp_result( $js->evaluate( [ 1, 2, 3 ], { prefixItems => [ { maximum => 0 } ], items => { maximum => 1 }, } )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/0', keywordLocation => '/prefixItems/0/maximum', error => 'value is greater than 0', }, { instanceLocation => '', keywordLocation => '/prefixItems', error => 'not all items are valid', }, { instanceLocation => '/1', keywordLocation => '/items/maximum', error => 'value is greater than 1', }, { instanceLocation => '/2', keywordLocation => '/items/maximum', error => 'value is greater than 1', }, { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'prefixItems + schema-based items', ); @warnings = warnings { cmp_result( $js->evaluate( [ 1, 2, 3 ], { items => { maximum => 0 }, additionalItems => { maximum => 1 }, } )->TO_JSON, { valid => false, errors => [ (map +{ instanceLocation => '/'.$_, keywordLocation => '/items/maximum', error => 'value is greater than 0', }, (0..2)), { instanceLocation => '', keywordLocation => '/items', error => 'subschema is not valid against all items', }, ], }, 'schema-based items + additionalItems, failure case', ); }; cmp_result( \@warnings, [ re(qr/^no-longer-supported "additionalItems" keyword present/) ], 'warned when using no-longer-supported keyword', ); @warnings = warnings { cmp_result( $js->evaluate( [ 1, 2, 3 ], { items => { maximum => 5 }, additionalItems => { maximum => 0 }, } )->TO_JSON, { valid => true }, 'schema-based items + additionalItems, passing case', ); }; cmp_result( \@warnings, [ re(qr/^no-longer-supported "additionalItems" keyword present/) ], 'warned when using no-longer-supported keyword', ); }; had_no_warnings() if $ENV{AUTHOR_TESTING}; done_testing; JSON-Schema-Modern-0.609/t/pattern.t000640 000766 000024 00000004050 15001302761 017267 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use utf8; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new; my $tests = sub ($char, $test_substr) { cmp_result( $js->evaluate($char, { pattern => '[a-z]' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/pattern', error => 'pattern does not match', }, ], }, $test_substr.' LATIN SMALL LETTER E WITH ACUTE does not match the ascii range [a-z]', ); cmp_result( $js->evaluate($char, { pattern => '\w' })->TO_JSON, { valid => true, }, $test_substr.' LATIN SMALL LETTER E WITH ACUTE does match the "word" character class, because unicode semantics are used for matching', ); }; my $letter = "é"; $tests->($letter, 'unchanged'); utf8::upgrade($letter); $tests->($letter, 'upgraded'); utf8::downgrade($letter); $tests->($letter, 'downgraded'); subtest 'empty pattern' => sub { # create a "last successful match" in a containing scope my $str = "furble" =~ s/fur/meow/r; cmp_result( $js->evaluate('hello', { pattern => '' })->TO_JSON, { valid => true }, 'empty pattern in "pattern" will correctly match', ); # create a new "last successful match" $str = "furble" =~ s/fur/meow/r; cmp_result( $js->evaluate( { alpha => 'hello' }, { patternProperties => { '' => true }, additionalProperties => false, unevaluatedProperties => false, }, )->TO_JSON, { valid => true }, 'empty pattern in "patternProperties" will correctly match', ); }; done_testing; JSON-Schema-Modern-0.609/t/additional-tests-draft7.t000640 000766 000024 00000003410 15001302761 022246 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Warnings 'warnings', ':no_end_test'; use lib 't/lib'; use Helper; use Acceptance; my $version = 'draft7'; my @warnings = warnings { acceptance_tests( acceptance => { specification => $version, test_dir => 't/additional-tests-'.$version, }, evaluator => { specification_version => $version, validate_formats => 1, collect_annotations => 0, }, output_file => $version.'-additional-tests.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ # these all depend on optional prereqs !eval { require Time::Moment; 1 } ? map "optional/format-$_.json", qw(date-time date time) : (), !eval { require DateTime::Format::RFC3339; 1 } ? 'optional/format-date-time.json' : (), ] }, ] ), }, ); }; my $test_sub = $ENV{AUTHOR_TESTING} ? sub { bag(@_) } : sub { superbagof(@_) }; cmp_result( \@warnings, $test_sub->( # these are all in unknownKeyword.json map +( ( re(qr/^no-longer-supported "$_" keyword present/) ) x (4 * ($ENV{NO_SHORT_CIRCUIT} ? 1 : 2)), ), qw(id), ), 'got unsupported keyword warnings'.($ENV{AUTHOR_TESTING} ? '; no unexpected warnings' : ''), ); done_testing; __END__ see t/results/draft7-additional-tests.txt for test results JSON-Schema-Modern-0.609/t/zzz-acceptance-draft7.t000640 000766 000024 00000007164 15001302761 021731 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use List::Util 1.50 'head'; use Config; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } my $version = 'draft7'; my $orig_warn_handler = $SIG{__WARN__}; $SIG{__WARN__} = sub { return if $_[0] =~ /^no-longer-supported "id" keyword present \(at location "[^"]+"\): this should be rewritten as "\$id"/; goto &$orig_warn_handler if $orig_warn_handler; }; acceptance_tests( acceptance => { specification => $version, skip_dir => 'optional/format', }, evaluator => { specification_version => $version, validate_formats => 0, # not the default for the Format vocabulary for this draft collect_annotations => 0, }, output_file => $version.'-acceptance.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ # various edge cases that are difficult to accomodate $Config{ivsize} < 8 ? { file => 'multipleOf.json', group_description => 'small multiple of large integer', test_description => 'any integer is a multiple of 1e-8' } : (), { file => 'optional/ecmascript-regex.json', group_description => '\w in patterns matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in pattern matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => '\w in patternProperties matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in patternProperties matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => [ 'ECMA 262 \d matches ascii digits only', 'ECMA 262 \D matches everything but ascii digits', 'ECMA 262 \w matches ascii letters only', 'ECMA 262 \W matches everything but ascii letters' ] }, # TODO, see test suite PR#505 { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \s matches whitespace', test_description => 'zero-width whitespace matches' }, { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \S matches everything but whitespace', test_description => 'zero-width whitespace does not match' }, ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft7-acceptance.txt for test results JSON-Schema-Modern-0.609/t/results/000770 000766 000024 00000000000 15001302761 017130 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/cached-metaschemas.t000640 000766 000024 00000004327 15001302761 021320 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use List::Util 'unpairs'; use constant METASCHEMA => 'https://json-schema.org/draft/2019-09/schema'; use lib 't/lib'; use Helper; # spec version -> vocab classes my %vocabularies = unpairs(JSON::Schema::Modern->new->__all_metaschema_vocabulary_classes); subtest 'load cached metaschema' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->_get_resource(METASCHEMA), undef, 'this resource is not yet known', ); cmp_result( $js->_get_or_load_resource(METASCHEMA), my $resource = +{ canonical_uri => str(METASCHEMA), path => '', specification_version => 'draft2019-09', vocabularies => $vocabularies{'draft2019-09'}, document => all( isa('JSON::Schema::Modern::Document'), methods( schema => superhashof({ '$schema' => str(METASCHEMA), '$id' => METASCHEMA, }), canonical_uri => str(METASCHEMA), resource_index => ignore, ), ), configs => {}, }, 'loaded metaschema from sharedir cache', ); cmp_result( $js->_get_resource(METASCHEMA), $resource, 'this resource is now in the resource index', ); }; subtest 'resource collision with cached metaschema' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate(1, { '$id' => METASCHEMA })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => re(qr{^EXCEPTION: \Quri "https://json-schema.org/draft/2019-09/schema" conflicts with an existing meta-schema resource\E}), }, ], }, 'cannot introduce another schema whose id collides with a cached schema, even if it isn\'t loaded yet', ); }; done_testing; JSON-Schema-Modern-0.609/t/zzz-acceptance-draft2020-12-format.t000640 000766 000024 00000007757 15001302761 023664 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Needs; use List::Util 1.50 'head'; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } if ($ENV{EXTENDED_TESTING}) { test_needs { 'Time::Moment' => 0, 'DateTime::Format::RFC3339' => 0, 'Email::Address::XS' => '1.04', 'Data::Validate::Domain' => 0.13, 'Net::IDN::Encode' => 0, }; } if ($ENV{AUTHOR_TESTING}) { eval { require Time::Moment; 1 } or fail $@; eval { require DateTime::Format::RFC3339; 1 } or fail $@; eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } or fail $@; eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } or fail $@; eval { require Net::IDN::Encode; 1 } or fail $@; } my $version = 'draft2020-12'; acceptance_tests( acceptance => { specification => $version, test_subdir => 'optional/format', }, evaluator => { specification_version => $version, validate_formats => 1, # not the default for the Format vocabulary for this draft collect_annotations => 0, }, output_file => $version.'-acceptance-format.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ 'iri-reference.json', # all strings are considered valid 'uri-template.json', # not yet implemented # these all depend on optional prereqs !$ENV{AUTHOR_TESTING} && !eval { require Time::Moment; 1 } ? qw(date-time.json date.json) : (), !$ENV{AUTHOR_TESTING} && !eval { require DateTime::Format::RFC3339; 1 } ? 'date-time.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } ? qw(email.json idn-email.json) : (), !$ENV{AUTHOR_TESTING} && !eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } ? 'hostname.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Net::IDN::Encode; 1 } ? 'idn-hostname.json' : (), ] }, # various edge cases that are difficult to accomodate { file => 'email.json', group_description => 'validation of e-mail addresses', test_description => [ 'an invalid domain', 'an invalid IPv4-address-literal' ] }, { file => 'iri.json', group_description => 'validation of IRIs', # see test suite issue 395 test_description => 'an invalid IRI based on IPv6' }, { file => 'idn-hostname.json', group_description => 'validation of internationalized host names' }, # IDN decoder, Data::Validate::Domain both have issues { file => 'uri.json', test_description => 'validation of URIs', test_description => 'an invalid URI with comma in scheme' }, # Mojo::URL does not fully validate # note this test was added in TJSA 1.027 { file => 'ecmascript-regex.json', group_description => '\a is not an ECMA 262 control escape', test_description => 'when used as a pattern' }, ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft2020-12-acceptance-format.txt for test results JSON-Schema-Modern-0.609/t/document.t000640 000766 000024 00000074661 15001302761 017447 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Deep::UnorderedPairs; use Test::Fatal; use Test::Memory::Cycle; use List::Util 'unpairs'; use lib 't/lib'; use Helper; # spec version -> vocab classes my %vocabularies = unpairs(JSON::Schema::Modern->new->__all_metaschema_vocabulary_classes); my %configs = ( specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, ); subtest 'boolean document' => sub { cmp_deeply( JSON::Schema::Modern::Document->new(schema => false), listmethods( resource_index => [ '' => { path => '', canonical_uri => str(''), %configs, }, ], original_uri => [ str('') ], canonical_uri => [ str('') ], _entities => [ { '' => 0 } ], ), 'boolean schema with no canonical_uri', ); like( exception { JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com#/x/y/z'), schema => false, ) }, qr/Reference .*did not pass type constraint/, 'boolean schema with invalid canonical_uri (fragment)', ); cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com'), schema => false, ), listmethods( resource_index => [ 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), %configs, }, ], canonical_uri => [ str('https://foo.com') ], _entities => [ { '' => 0 } ], ), 'boolean schema with valid canonical_uri', ); }; subtest 'object document' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( defined $_ ? ( canonical_uri => $_ ) : (), schema => {}, ), listmethods( resource_index => [ str($_//'') => { path => '', canonical_uri => str($_//''), %configs, }, ], original_uri => [ str($_//'') ], canonical_uri => [ str($_//'') ], _entities => [ { '' => 0 } ], ), 'object schema with originally provided uri = \''.($_//'').'\' and no root $id', ) foreach (undef, '', '0', Mojo::URL->new(''), Mojo::URL->new('0')); cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com'), schema => {}, ), listmethods( resource_index => [ # note: no '' entry! 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), %configs, }, ], original_uri => [ str('https://foo.com') ], canonical_uri => [ str('https://foo.com') ], _entities => [ { '' => 0 } ], ), 'object schema with valid canonical_uri, no root $id', ); cmp_deeply( JSON::Schema::Modern::Document->new( defined $_ ? ( canonical_uri => $_ ) : (), schema => { '$id' => 'https://foo.com' }, ), listmethods( resource_index => [ # note: no '' entry 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), %configs, }, ], original_uri => [ str($_//'') ], canonical_uri => [ str('https://foo.com') ], # note canonical_uri has been overwritten _entities => [ { '' => 0 } ], ), 'object schema with originally provided uri = \''.($_//'').'\' and absolute root $id', ) foreach (undef, '', Mojo::URL->new); cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => $_, schema => { '$id' => 'https://bar.com' }, ), listmethods( resource_index => [ # note: no '' entry 'https://bar.com' => { path => '', canonical_uri => str('https://bar.com'), %configs, }, ], original_uri => [ str($_) ], canonical_uri => [ str('https://bar.com') ], # note canonical_uri has been overwritten _entities => [ { '' => 0 } ], ), 'originally provided uri is not indexed when overridden by an absolute root $id', ) foreach ('0', Mojo::URL->new('0'), 'https://foo.com'); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$defs' => { foo => {} }, }, canonical_uri => 'https://example.com', ), listmethods( resource_index => [ 'https://example.com' => { path => '', canonical_uri => str('https://example.com'), %configs, }, ], ), 'when canonical_uri provided, the empty uri is not added as a referenceable uri', ); cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com'), schema => { '$id' => 'https://foo.com' }, ), listmethods( resource_index => [ 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), %configs, }, ], original_uri => [ str('https://foo.com') ], canonical_uri => [ str('https://foo.com') ], _entities => [ { '' => 0 } ], ), 'object schema with originally provided uri equal to root $id', ); cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com'), schema => { '$id' => 'https://bar.com', allOf => [ { '$anchor' => 'my_anchor' }, { '$id' => 'x/y/z.json' }, ], }, ), listmethods( resource_index => unordered_pairs( 'https://bar.com' => { path => '', canonical_uri => str('https://bar.com'), %configs, anchors => { my_anchor => { path => '/allOf/0', canonical_uri => str('https://bar.com#/allOf/0'), }, }, }, 'https://bar.com/x/y/z.json' => { path => '/allOf/1', canonical_uri => str('https://bar.com/x/y/z.json'), %configs, }, ), original_uri => [ str('https://foo.com') ], canonical_uri => [ str('https://bar.com') ], _entities => [ { map +($_ => 0), '', '/allOf/0', '/allOf/1' } ], ), 'object schema with canonical_uri and root $id, and additional resource schemas as well', ); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$id' => 'relative', }, canonical_uri => 'https://my-base.com', ), listmethods( resource_index => [ 'https://my-base.com/relative' => { path => '', canonical_uri => str('https://my-base.com/relative'), %configs, }, ], original_uri => [ str('https://my-base.com') ], canonical_uri => [ str('https://my-base.com/relative') ], _entities => [ { '' => 0 } ], ), 'relative $id at root is resolved against provided canonical_id', ); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$defs' => { foo => { '$id' => 'my_foo', const => 'foo value', }, }, '$ref' => 'my_foo', }, ), listmethods( resource_index => unordered_pairs( '' => { path => '', canonical_uri => str(''), %configs, }, 'my_foo' => { path => '/$defs/foo', canonical_uri => str('my_foo'), %configs, }, ), original_uri => [ str('') ], canonical_uri => [ str('') ], _entities => [ { map +($_ => 0), '', '/$defs/foo' } ], ), 'relative uri for inner $id', ); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$defs' => { foo => { '$id' => 'http://localhost:4242/my_foo', const => 'foo value', }, }, }, ), listmethods( resource_index => unordered_pairs( '' => { path => '', canonical_uri => str(''), %configs, }, 'http://localhost:4242/my_foo' => { path => '/$defs/foo', canonical_uri => str('http://localhost:4242/my_foo'), %configs, }, ), original_uri => [ str('') ], canonical_uri => [ str('') ], _entities => [ { map +($_ => 0), '', '/$defs/foo' } ], ), 'no root $id; absolute uri with path in subschema resource', ); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$anchor' => 'my_anchor', }, ), listmethods( resource_index => [ '' => { path => '', canonical_uri => str(''), %configs, anchors => { my_anchor => { path => '', canonical_uri => str(''), }, }, }, ], original_uri => [ str('') ], canonical_uri => [ str('') ], ), 'no root $id or canonical_uri provided; anchor is indexed at the root', ); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$anchor' => 'my_anchor', }, canonical_uri => 'https://example.com', ), listmethods( resource_index => [ 'https://example.com' => { path => '', canonical_uri => str('https://example.com'), %configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://example.com'), }, }, }, ], original_uri => [ str('https://example.com') ], canonical_uri => [ str('https://example.com') ], ), 'canonical_uri provided; empty uri not added as a referenceable uri when an anchor exists', ); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$id' => 'https://my-base.com', '$anchor' => 'my_anchor', }, ), listmethods( resource_index => [ 'https://my-base.com' => { path => '', canonical_uri => str('https://my-base.com'), %configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://my-base.com'), }, }, }, ], original_uri => [ str('') ], canonical_uri => [ str('https://my-base.com') ], ), 'absolute uri provided at root; adjacent anchor has the same canonical uri', ); cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$id' => 'https://my-base.com', '$defs' => { foo => { '$anchor' => 'my_anchor', }, }, }, ), listmethods( resource_index => [ 'https://my-base.com' => { path => '', canonical_uri => str('https://my-base.com'), %configs, anchors => { my_anchor => { path => '/$defs/foo', canonical_uri => str('https://my-base.com#/$defs/foo'), }, }, }, ], original_uri => [ str('') ], canonical_uri => [ str('https://my-base.com') ], ), 'absolute uri provided at root; anchor lower down has its own canonical uri', ); }; subtest '$id and $anchor as properties' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( schema => { type => 'object', properties => { '$id' => { type => 'string' }, '$anchor' => { type => 'string' }, }, }, ), listmethods( resource_index => [ '' => { path => '', canonical_uri => str(''), %configs, }, ], _entities => [ { map +($_ => 0), '', '/properties/$id', '/properties/$anchor' } ], ), 'did not index the $id and $anchor properties as if they were identifier keywords', ); }; subtest '$id with an empty fragment' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$defs' => { foo => { '$id' => 'http://localhost:4242/my_foo#', type => 'string', }, }, }, ), listmethods( resource_index => unordered_pairs( '' => { path => '', canonical_uri => str(''), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'http://localhost:4242/my_foo' => { path => '/$defs/foo', canonical_uri => str('http://localhost:4242/my_foo'), %configs, }, ), _entities => [ { map +($_ => 0), '', '/$defs/foo' } ], ), '$id is stored with the empty fragment stripped', ); }; subtest '$id with a non-empty fragment' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$id' => 'http://main.com', '$defs' => { foo => { '$id' => 'http://secondary.com', properties => { bar => { '$id' => 'http://localhost:4242/my_foo#hello', }, }, }, }, }, ), all( methods(canonical_uri => str('http://main.com')), listmethods( resource_index => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/$defs/foo/properties/bar/$id', absoluteKeywordLocation => 'http://secondary.com#/properties/bar/$id', error => '$id value "http://localhost:4242/my_foo#hello" cannot have a non-empty fragment', }), ], ), ), 'did not index the $id with a non-empty fragment, nor use it as the base for other identifiers', ); }; subtest '$anchor not conforming to syntax' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$defs' => { foo => { '$anchor' => 'my_#bad_anchor', }, }, }, ), listmethods( resource_index => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/$defs/foo/$anchor', error => '$anchor value "my_#bad_anchor" does not match required syntax', }), ], ), 'did not index an $anchor with invalid characters', ); cmp_deeply( JSON::Schema::Modern::Document->new( specification_version => 'draft7', schema => { definitions => { foo => { '$id' => '#my_$bad_anchor', }, qux => { '$id' => 'https://foo.com#my_bad_id', }, }, }, ), listmethods( resource_index => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/definitions/foo/$id', error => '$id value "#my_$bad_anchor" does not match required syntax', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/definitions/qux/$id', error => '$id cannot change the base uri at the same time as declaring an anchor', }), ], ), 'did not index a draft7 fragment-only $id with invalid characters, or non-fragment-only $id', ); cmp_deeply( JSON::Schema::Modern::Document->new( specification_version => 'draft4', schema => { definitions => { foo => { id => '#my_$bad_anchor', }, qux => { id => 'https://foo.com#my_bad_id', }, }, }, ), listmethods( resource_index => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/definitions/foo/id', error => 'id value "#my_$bad_anchor" does not match required syntax', }), methods(TO_JSON => { instanceLocation => '', keywordLocation => '/definitions/qux/id', error => 'id cannot change the base uri at the same time as declaring an anchor', }), ], ), 'did not index a draft4 fragment-only $id with invalid characters, or non-fragment-only id', ); }; subtest '$schema not conforming to syntax' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( schema => { '$schema' => 'foo' }, ), listmethods( resource_index => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/$schema', error => '"foo" is not a valid URI', }), ], ), 'invalid $schema is detected', ); }; subtest '$anchor and $id below an $id that is not at the document root' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com'), schema => { allOf => [ { '$id' => 'https://bar.com', '$anchor' => 'my_anchor', not => { '$anchor' => 'my_not', not => { '$id' => 'inner_id' }, }, }, ], }, ), listmethods( resource_index => unordered_pairs( 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'https://bar.com' => { path => '/allOf/0', canonical_uri => str('https://bar.com'), %configs, anchors => { my_anchor => { path => '/allOf/0', canonical_uri => str('https://bar.com'), }, my_not => { path => '/allOf/0/not', canonical_uri => str('https://bar.com#/not'), }, }, }, 'https://bar.com/inner_id' => { path => '/allOf/0/not/not', canonical_uri => str('https://bar.com/inner_id'), %configs, }, ), _entities => [ { map +($_ => 0), '', '/allOf/0', '/allOf/0/not', '/allOf/0/not/not' } ], ), 'canonical_uri uses the path from the innermost $id, not document root $id', ); }; subtest 'JSON pointer and URI escaping' => sub { cmp_deeply( my $doc = JSON::Schema::Modern::Document->new( schema => { '$defs' => { foo => { patternProperties => { '~' => { '$id' => 'http://localhost:4242/~username', properties => { '~/' => { '$anchor' => 'tilde', }, }, }, '/' => { '$id' => 'http://localhost:4242/my_slash', properties => { '~/' => { '$anchor' => 'slash', }, }, }, '[~/]' => { '$id' => 'http://localhost:4242/~username/my_slash', properties => { '~/' => { '$anchor' => 'tildeslash', }, }, }, }, }, }, }, ), listmethods( resource_index => unordered_pairs( '' => { path => '', canonical_uri => str(''), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, 'http://localhost:4242/~username' => { path => '/$defs/foo/patternProperties/~0', canonical_uri => str('http://localhost:4242/~username'), %configs, anchors => { tilde => { path => '/$defs/foo/patternProperties/~0/properties/~0~1', canonical_uri => str('http://localhost:4242/~username#/properties/~0~1'), }, }, }, 'http://localhost:4242/my_slash' => { path => '/$defs/foo/patternProperties/~1', canonical_uri => str('http://localhost:4242/my_slash'), %configs, anchors => { slash => { path => '/$defs/foo/patternProperties/~1/properties/~0~1', canonical_uri => str('http://localhost:4242/my_slash#/properties/~0~1'), }, }, }, 'http://localhost:4242/~username/my_slash' => { path => '/$defs/foo/patternProperties/[~0~1]', canonical_uri => str('http://localhost:4242/~username/my_slash'), %configs, anchors => { tildeslash => { path => '/$defs/foo/patternProperties/[~0~1]/properties/~0~1', canonical_uri => str('http://localhost:4242/~username/my_slash#/properties/~0~1'), }, }, }, ), _entities => [ { map +($_ => 0), my @locations = ( '', '/$defs/foo', '/$defs/foo/patternProperties/~0', '/$defs/foo/patternProperties/~0/properties/~0~1', '/$defs/foo/patternProperties/~1', '/$defs/foo/patternProperties/~1/properties/~0~1', '/$defs/foo/patternProperties/[~0~1]', '/$defs/foo/patternProperties/[~0~1]/properties/~0~1', ) }], ), 'properly escaped special characters in JSON pointers and URIs', ); is($doc->get_entity_at_location('/$defs/foo/patternProperties/~0'), 'schema', 'schema locations are tracked'); is($doc->get_entity_at_location('/$defs/foo/patternProperties'), '', 'non-schema locations are also tracked'); cmp_deeply( [ $doc->get_entity_locations('schema') ], bag(@locations), 'schema locations can be queried', ); }; subtest 'resource collisions' => sub { is( exception { JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com/x/y/z'), schema => { '$id' => '/x/y/z' }, ); }, undef, 'no collision when adding an identical resource (after resolving with base uri)', ); like( exception { JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com/x/y/z'), schema => { allOf => [ { '$id' => '/x/y/z' }, { '$id' => '/a/b/c' }, ], }, ); }, qr{^\Quri "https://foo.com/x/y/z" conflicts with an existing schema resource\E}, 'detected collision between document\'s initial uri and a subschema\'s uri', ); cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com'), schema => { allOf => [ { '$id' => '/x/y/z' }, { '$id' => '/x/y/z' }, ], }, ), all( listmethods( resource_index => [], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/allOf/1/$id', absoluteKeywordLocation => 'https://foo.com#/allOf/1/$id', error => 'duplicate canonical uri "https://foo.com/x/y/z" found (original at path "/allOf/0")', }), ], ), ), 'detected collision between two subschema uris in a document', ); my $doc1 = JSON::Schema::Modern::Document->new(schema => { '$id' => 'a/b' }); my $doc2 = JSON::Schema::Modern::Document->new(schema => { '$id' => 'b' }); my $js = JSON::Schema::Modern->new; is( # id resolves to https://foo.com/a/b exception { $js->add_document('https://foo.com' => $doc1) }, undef, 'add first document, resolving resources to a base uri', ); like( # id resolves to https://foo.com/a/b exception { $js->add_document('https://foo.com/a/' => $doc2) }, qr{^uri "https://foo.com/a/b" conflicts with an existing schema resource}, 'the resource in the second document resolves to the same uri as from the first document', ); is( exception { JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com/x/y/z'), schema => { examples => [ { '$id' => '/x/y/z' }, { '$id' => 'https://foo.com/x/y/z' }, ], default => { allOf => [ { '$id' => '/x/y/z' }, { '$id' => 'https://foo.com/x/y/z' }, ], }, }, ); }, undef, 'ignored "duplicate" uris embedded in non-schemas', ); cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => Mojo::URL->new('https://foo.com/x/y/z'), schema => { '$id' => 'https://bar.com', '$anchor' => 'hello', }, )->path_to_resource(''), superhashof({ canonical_uri => str('https://bar.com') }), 'the correct canonical uri is indexed in the inverted index', ); }; subtest 'create document with explicit canonical_uri set to the same as root $id' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => 'https://foo.com/x/y/z', schema => { '$id' => 'https://foo.com/x/y/z' }, ), listmethods( resource_index => [ 'https://foo.com/x/y/z' => { path => '', canonical_uri => str('https://foo.com/x/y/z'), %configs, }, ], canonical_uri => [ str('https://foo.com/x/y/z') ], ), 'there is one single uri indexed to the document', ); }; subtest 'canonical_uri identification from a document with errors' => sub { cmp_deeply( JSON::Schema::Modern::Document->new( canonical_uri => 'https://foo.com/x/y/z', schema => { '$id' => 'https://bar.com', allOf => [ { '$id' => 'https://baz.com', oneOf => [ { '$id' => 'https://quux.com' }, [ 'not a subschema' ], ], }, ], }, ), listmethods( canonical_uri => [ str('https://bar.com') ], errors => [ methods(TO_JSON => { instanceLocation => '', keywordLocation => '/allOf/0/oneOf/1', absoluteKeywordLocation => 'https://baz.com#/oneOf/1', error => 'invalid schema type: array', }), ], ), 'error lower down in document does not result in an inner identifier being used as canonical_uri', ); }; subtest 'custom metaschema_uri' => sub { my $js = JSON::Schema::Modern->new; $js->add_schema({ '$id' => 'https://my/first/metaschema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/applicator' => true, 'https://json-schema.org/draft/2020-12/vocab/core' => true, # note: no validation! }, }); my $doc = $js->add_document(JSON::Schema::Modern::Document->new( schema => { '$id' => my $id = 'https://my/first/schema/with/custom/metaschema', # note: no $schema keyword! allOf => [ { minimum => 'not even an integer' } ], }, metaschema_uri => 'https://my/first/metaschema', evaluator => $js, # needed in order to find the metaschema )); cmp_result( $js->{_resource_index}{$id}, { canonical_uri => str($id), path => '', specification_version => 'draft2020-12', document => $doc, vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Applicator), ], configs => {}, }, 'determined vocabularies to use for this schema', ); cmp_result( $js->evaluate(1, $id)->TO_JSON, { valid => true }, 'validation succeeds because "minimum" never gets run', ); cmp_result( $js->evaluate(1, Mojo::URL->new($id)->fragment('/allOf/0'))->TO_JSON, { valid => true }, 'can evaluate at a subschema as well, with the same vocabularies', ); cmp_result( $doc->validate->TO_JSON, { valid => true }, 'schema validates against its metaschema, and "minimum" is ignored', ); memory_cycle_ok($js, 'no leaks in the evaluator object'); }; subtest 'multiple uris used for resolution and identification, and original_uri' => sub { my $js = JSON::Schema::Modern->new; my $doc = $js->add_document( 'https://example.com/api/' => JSON::Schema::Modern::Document->new( canonical_uri => 'staging/', schema => { '$id' => 'alpha.json', properties => { foo => { '$id' => 'beta' }, }, }, evaluator => $js, ) ); cmp_deeply( $doc, listmethods( original_uri => [ str('staging/') ], canonical_uri => [ str('staging/alpha.json') ], resource_index => unordered_pairs( 'staging/alpha.json' => { path => '', canonical_uri => str('staging/alpha.json'), %configs, }, 'staging/beta' => { canonical_uri => str('staging/beta'), path => '/properties/foo', %configs, }, ), ), 'document has correct resources, resolved against the provided base uri', ); cmp_deeply( $js->{_resource_index}, my $resource_index = { 'https://example.com/api/' => { path => '', canonical_uri => str('https://example.com/api/staging/alpha.json'), document => $doc, %configs, }, 'https://example.com/api/staging/alpha.json' => { path => '', canonical_uri => str('https://example.com/api/staging/alpha.json'), document => $doc, %configs, }, 'https://example.com/api/staging/beta' => { path => '/properties/foo', canonical_uri => str('https://example.com/api/staging/beta'), document => $doc, %configs, }, }, 'evaluator has correct resources, resolved against the provided base uri', ); my $doc2 = $js->add_document('file:///usr/local/share/api.json' => $doc); is($doc2, $doc, 'same document is added a second time'); cmp_deeply( $js->{_resource_index}, { %$resource_index, # original entries 'file:///usr/local/share/api.json' => { path => '', canonical_uri => str('file:///usr/local/share/staging/alpha.json'), document => $doc, %configs, }, 'file:///usr/local/share/staging/alpha.json' => { path => '', canonical_uri => str('file:///usr/local/share/staging/alpha.json'), document => $doc, %configs, }, 'file:///usr/local/share/staging/beta' => { path => '/properties/foo', canonical_uri => str('file:///usr/local/share/staging/beta'), document => $doc, %configs, }, }, 'document resources are added using new base, which appears in their canonical_uri values', ); }; done_testing; JSON-Schema-Modern-0.609/t/strict.t000640 000766 000024 00000011624 15001302761 017127 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new; ok(!$js->strict, 'strict defaults to false'); my $schema = { '$id' => 'my_loose_schema', type => 'object', properties => { foo => { title => 'bloop', # produces an annotation for 'title' with value 'bloop' bloop => 'hi', # unknown keyword barf => 'no', # unknown keyword }, }, }; my $document = $js->add_schema($schema); cmp_result( $js->evaluate({ foo => 1 }, 'my_loose_schema')->TO_JSON, { valid => true }, 'by default, unknown keywords are allowed in evaluate()', ); cmp_result( $js->evaluate({ foo => 1 }, 'my_loose_schema', { strict => 1 })->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo', keywordLocation => '/properties/foo', absoluteKeywordLocation => 'my_loose_schema#/properties/foo', error => 'unknown keywords found: barf, bloop', }, ], }, 'strict mode disallows unknown keywords during evaluation via a config override', ); cmp_result( $js->validate_schema($schema)->TO_JSON, { valid => true }, 'by default, unknown keywords are allowed in validate_schema()', ); cmp_result( $js->validate_schema($schema, { strict => 1 })->TO_JSON, my $schema_result = { valid => false, errors => [ { instanceLocation => '/properties/foo/barf', keywordLocation => '', absoluteKeywordLocation => 'https://json-schema.org/draft/2020-12/schema', error => 'unknown keyword found in schema: barf', }, { instanceLocation => '/properties/foo/bloop', keywordLocation => '', absoluteKeywordLocation => 'https://json-schema.org/draft/2020-12/schema', error => 'unknown keyword found in schema: bloop', }, ], }, 'strict mode disallows unknown keywords in validate_schema() via a config override', ); $js = JSON::Schema::Modern->new(strict => 1); $js->add_document($document); cmp_result( $js->evaluate({ foo => 1 }, $document->canonical_uri)->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo', keywordLocation => '/properties/foo', absoluteKeywordLocation => 'my_loose_schema#/properties/foo', error => 'unknown keywords found: barf, bloop', }, ], }, 'strict mode disallows unknown keywords during evaluation, even if the document was already traversed', ); cmp_result( $js->validate_schema($schema)->TO_JSON, $schema_result, 'strict mode disallows unknown keywords in the schema data passed to validate_schema()', ); delete $schema->{'$id'}; cmp_result( $js->evaluate({ foo => 1 }, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', # note no instance location - indicating evaluation has not started keywordLocation => '/properties/foo', error => 'unknown keywords found: barf, bloop', }, ], }, 'strict mode disallows unknown keywords during traverse', ); my $lax_metaschema = { '$id' => 'my_lax_metaschema', '$schema' => 'https://json-schema.org/draft/2020-12/schema', '$dynamicAnchor' => 'meta', '$ref' => 'https://json-schema.org/draft/2020-12/schema', properties => { bloop => true, # bloop is now a recognized property }, }; $js->add_schema($lax_metaschema); $schema->{'$schema'} = 'my_lax_metaschema'; cmp_result( $js->validate_schema($schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '/properties/foo/barf', keywordLocation => '', absoluteKeywordLocation => 'my_lax_metaschema', error => 'unknown keyword found in schema: barf', }, ], }, 'strict mode only detected one property this time - bloop is evaluated', ); $schema->{'$schema'} = 'http://json-schema.org/draft-07/schema#'; cmp_result( $js->validate_schema($schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '/properties/foo/barf', keywordLocation => '', absoluteKeywordLocation => 'http://json-schema.org/draft-07/schema', error => 'unknown keyword found in schema: barf', }, { instanceLocation => '/properties/foo/bloop', keywordLocation => '', absoluteKeywordLocation => 'http://json-schema.org/draft-07/schema', error => 'unknown keyword found in schema: bloop', }, ], }, 'strict mode detects unknown keywords using draft7', ); done_testing; JSON-Schema-Modern-0.609/t/content-encoding.t000640 000766 000024 00000035647 15001302761 021070 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use utf8; use Test::Fatal; use lib 't/lib'; use Helper; subtest 'unrecognized encoding formats do not result in errors, when not asserting' => sub { my $js = JSON::Schema::Modern->new(collect_annotations => 1); cmp_result( my $result = $js->evaluate( 'hello', { contentEncoding => 'base64', contentMediaType => 'image/png', contentSchema => false, }, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '', keywordLocation => '/contentEncoding', annotation => 'base64', }, { instanceLocation => '', keywordLocation => '/contentMediaType', annotation => 'image/png', }, { instanceLocation => '', keywordLocation => '/contentSchema', annotation => false, }, ], }, 'in evaluate(), annotations are collected and no validation is performed', ); }; subtest 'media_type and encoding handlers' => sub { my $js = JSON::Schema::Modern->new; like( exception { $js->add_media_type('FOO/BAR' => sub { \1 }) }, qr!Value "FOO/BAR" did not pass type constraint !, 'upper-cased names are not accepted', ); cmp_result( $js->get_media_type('application/json')->(\'{"alpha": "a string"}'), \ { alpha => 'a string' }, 'application/json media_type decoder', ); cmp_result($js->get_media_type('*/*'), undef, '*/* has no default match'); cmp_result($js->get_media_type('text/plain')->(\'foo'), \'foo', 'default text/plain media_type decoder'); cmp_result($js->get_media_type('tExt/PLaIN')->(\'foo'), \'foo', 'getter uses the casefolded name'); $js->add_media_type('furble/*' => sub { \1 }); cmp_result($js->get_media_type('furble/bloop')->(\''), \'1', 'getter matches to wildcard entries'); $js->add_media_type('text/*' => sub { \'wildcard' }); cmp_result($js->get_media_type('TExT/plain')->(\'foo'), \'wildcard', 'getter uses new override entry for wildcard'); $js->add_media_type('text/plain' => sub { \'plain' }); cmp_result($js->get_media_type('TExT/plain')->(\'foo'), \'plain', 'getter prefers case-insensitive matches to wildcard entries'); cmp_result($js->get_media_type('TExT/blop')->(\'foo'), \'wildcard', 'getter matches to wildcard entries'); cmp_result($js->get_media_type('TExT/*')->(\'foo'), \'wildcard', 'text/* matches itself'); $js->add_media_type('*/*' => sub { \'wildercard' }); cmp_result($js->get_media_type('TExT/plain')->(\'foo'), \'plain', 'getter still prefers case-insensitive matches to wildcard entries'); cmp_result($js->get_media_type('TExT/blop')->(\'foo'), \'wildcard', 'text/* is preferred to */*'); cmp_result($js->get_media_type('*/*')->(\'foo'), \'wildercard', '*/* matches */*, once defined'); cmp_result($js->get_media_type('fOO/bar')->(\'foo'), \'wildercard', '*/* is returned as a last resort'); cmp_result( $js->get_media_type('application/x-www-form-urlencoded')->(\qq!\x{c3}\x{a9}clair=\x{e0}\x{b2}\x{a0}\x{5f}\x{e0}\x{b2}\x{a0}!), \ { 'éclair' => 'ಠ_ಠ' }, 'application/x-www-form-urlencoded happy path with unicode', ); cmp_result( $js->get_media_type('application/x-ndjson')->(\qq!{"foo":1,"bar":2}\n["a","b",3]\r\n"\x{e0}\x{b2}\x{a0}\x{5f}\x{e0}\x{b2}\x{a0}"!), \ [ { foo => 1, bar => 2 }, [ 'a', 'b', 3 ], 'ಠ_ಠ' ], 'application/x-ndjson happy path with unicode', ); like( exception { $js->get_media_type('application/x-ndjson')->(\qq!{"foo":1,"bar":2}\n["a","b",]!) }, qr/^parse error at line 2: malformed JSON string/, 'application/x-ndjson dies with line number of the bad data', ); $js = JSON::Schema::Modern->new; # MIME::Base64::decode("eyJmb28iOiAiYmFyIn0K") -> {"foo": "bar"} # JSON::MaybeXS->new(allow_nonref => 1, utf8 => 0)->decode(q!{"foo": "bar"}!) -> { foo => 'bar' } cmp_result( $js->get_media_type('application/json')->($js->get_encoding('base64')->(\'eyJmb28iOiAiYmFyIn0K')), \ { foo => 'bar' }, 'base64 encoding decoder + application/json media_type decoder', ); cmp_result( $js->get_media_type('application/json')->($js->get_encoding('base64url')->(\'eyJmb28iOiJiYXIifQ')), \ { foo => 'bar' }, 'base64url encoding decoder + application/json media_type decoder', ); }; subtest 'draft2020-12 assertions' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( my $data = { encoded_object => 'eyJmb28iOiAiYmFyIn0K' }, my $schema = { type => 'object', properties => { encoded_object => { contentEncoding => 'base64', contentMediaType => 'application/json', contentSchema => { type => 'object', additionalProperties => { const => 'ಠ_ಠ', }, }, }, }, }, )->TO_JSON, { valid => true }, 'under the current spec version, content* keywords are not assertions', ); cmp_result( my $result = $js->evaluate( { encoded_object => 'blur^p=' }, # invalid base64 $schema, { validate_content_schemas => 1 }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentEncoding', error => 'could not decode base64 string: invalid characters', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'contentEncoding first decodes the string, erroring if it can\'t', ); cmp_result( $result = $js->evaluate( { encoded_object => 'bm90IGpzb24=' }, # base64-encoded "not json" $schema, { validate_content_schemas => 1 }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentMediaType', error => re(qr!could not decode application/json string: \'null\' expected, at character offset 0!), }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'then contentMediaType parses the decoded string, erroring if it can\'t, and does not continue with the schema', ); cmp_result( $result = $js->evaluate( { encoded_object => 'eyJoaSI6MX0=' }, # base64-encoded, json-encoded { hi => 1 } $schema, { validate_content_schemas => 1 }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/encoded_object/hi', keywordLocation => '/properties/encoded_object/contentSchema/additionalProperties/const', error => 'value does not match', }, { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentSchema/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentSchema', error => 'subschema is not valid', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'contentSchema evaluates the decoded data', ); cmp_result( $result = $js->evaluate( { encoded_object => 'bnVsbA==' }, # base64-encoded, json-encoded undef $schema, { validate_content_schemas => 1 }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentSchema/type', error => 'got null, not object', }, { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentSchema', error => 'subschema is not valid', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'null data is handled properly', ); cmp_result( $result = $js->evaluate( { encoded_object => 'eyJoaSI6IuCyoF/gsqAifQ==' }, # base64-encoded, json-encoded { hi => "ಠ_ಠ" } $schema, { validate_content_schemas => 1 }, )->TO_JSON, { valid => true }, 'contentSchema successfully evaluates the decoded data', ); }; subtest 'draft7 assertions' => sub { my $js = JSON::Schema::Modern->new(specification_version => 'draft7'); cmp_result( my $result = $js->evaluate( { encoded_object => 'blur^p=' }, # invalid base64 my $schema = { type => 'object', properties => { encoded_object => { contentEncoding => 'base64', contentMediaType => 'application/json', contentSchema => { type => 'object', additionalProperties => { const => 'ಠ_ಠ', }, }, }, }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentEncoding', error => 'could not decode base64 string: invalid characters', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'in draft7, assertion behaviour is the default', ); cmp_result( $result = $js->evaluate( { encoded_object => 'bm90IGpzb24=' }, # base64-encoded "not json" $schema, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/encoded_object', keywordLocation => '/properties/encoded_object/contentMediaType', error => re(qr!could not decode application/json string: \'null\' expected, at character offset 0!), }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, 'in draft7, then contentMediaType parses the decoded string, erroring if it can\'t, and does not continue with the schema', ); cmp_result( $result = $js->evaluate( { encoded_object => 'eyJoaSI6MX0=' }, # base64-encoded, json-encoded { hi => 1 } $schema, )->TO_JSON, { valid => true }, 'under draft7, content* are assertions by default, but contentSchema does not exist', ); }; subtest 'more assertions' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate( 'a string', { contentEncoding => 'whargarbl', contentMediaType => 'whargarbl', contentSchema => false, }, { validate_content_schemas => 1, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/contentEncoding', error => 'cannot find decoder for contentEncoding "whargarbl"', }, ], }, 'evaluation aborts with an unrecognized contentEncoding', ); cmp_result( $js->evaluate( 'a string', { contentMediaType => 'whargarbl', contentSchema => false, }, { validate_content_schemas => 1, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/contentMediaType', error => 'cannot find decoder for contentMediaType "whargarbl"', }, ], }, 'evaluation aborts with an unrecognized contentMediaType', ); }; subtest 'use of an absolute URI and different dialect within contentSchema' => sub { my $js = JSON::Schema::Modern->new( validate_content_schemas => 1, collect_annotations => 1, ); $js->add_schema({ '$id' => 'https://my_metaschema', '$schema' => 'https://json-schema.org/draft/2020-12/schema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/validation' => true, }, }); my $subschema; cmp_result( $js->evaluate( { foo => '{"bar":1}' }, { '$id' => 'https://example.com', additionalProperties => { contentMediaType => 'application/json', contentSchema => $subschema = { '$id' => 'https://foo.com', '$schema' => 'https://my_metaschema', '$defs' => { my_def => { type => 'object', blah => 1 }, }, '$ref' => '#/$defs/my_def', bloop => 2, properties => { bar => false }, # this keyword should only annotate }, }, }, )->TO_JSON, { valid => true, annotations => [ { instanceLocation => '/foo', keywordLocation => '/additionalProperties/contentMediaType', absoluteKeywordLocation => 'https://example.com#/additionalProperties/contentMediaType', annotation => 'application/json', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/contentSchema/$ref/blah', absoluteKeywordLocation => 'https://foo.com#/$defs/my_def/blah', annotation => 1, }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/contentSchema/bloop', absoluteKeywordLocation => 'https://foo.com#/bloop', annotation => 2, }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/contentSchema/properties', absoluteKeywordLocation => 'https://foo.com#/properties', annotation => { bar => false }, }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/contentSchema', absoluteKeywordLocation => 'https://example.com#/additionalProperties/contentSchema', annotation => $subschema, }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://example.com#/additionalProperties', annotation => [ 'foo' ], }, ], }, 'evaluation of the subschema correctly uses the new $id and $schema', ); }; done_testing; JSON-Schema-Modern-0.609/t/unsupported-keywords.t000640 000766 000024 00000005057 15001302761 022057 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Warnings qw(warnings :no_end_test had_no_warnings); use lib 't/lib'; use Helper; my %strings = ( id => qr/^no-longer-supported "id" keyword present \(at location ""\): this should be rewritten as "\$id" at /, definitions => qr/^no-longer-supported "definitions" keyword present \(at location ""\): this should be rewritten as "\$defs" at /, dependencies => qr/^no-longer-supported "dependencies" keyword present \(at location ""\): this should be rewritten as "dependentSchemas" or "dependentRequired" at /, ); my %schemas = ( id => 'https://localhost:1234', definitions => {}, dependencies => {}, ); my @warnings = ( [ draft6 => [ qw(id) ] ], [ draft7 => [ qw(id) ] ], [ 'draft2019-09' => [ qw(id definitions dependencies) ] ], ); foreach my $index (0 .. $#warnings) { my ($spec_version, $removed_keywords) = $warnings[$index]->@*; note "\n", $spec_version; my $js = JSON::Schema::Modern->new(specification_version => $spec_version); foreach my $keyword (@$removed_keywords) { cmp_result( [ warnings { cmp_result( $js->evaluate(true, { $keyword => $schemas{$keyword} })->TO_JSON, { valid => true }, 'schema with "'.$keyword.'" still validates in '.$spec_version, ) } ], superbagof(re($strings{$keyword})), 'warned for "'.$keyword.'" in '.$spec_version, ); } next if $index == $#warnings; my ($next_spec_version, $removed_next_keywords) = $warnings[$index+1]->@*; foreach my $keyword (@$removed_next_keywords) { next if grep $keyword eq $_, @$removed_keywords; local $SIG{__WARN__} = sub { warn @_ if $_[0] =~ /^no-longer-supported "$keyword" keyword present/; }; cmp_result( [ warnings { cmp_result( $js->evaluate(true, { $keyword => $schemas{$keyword} })->TO_JSON, { valid => true }, 'schema with "'.$keyword.'" validates in '.$spec_version, ) } ], [], 'did not warn for "'.$keyword.'" in '.$spec_version, ); } } had_no_warnings if $ENV{AUTHOR_TESTING}; done_testing; JSON-Schema-Modern-0.609/t/00-report-prereqs.t000644 000766 000024 00000014175 15001302761 021036 0ustar00etherstaff000000 000000 #!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.029 use Test::More tests => 1; use Module::Metadata; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( Encode File::Temp JSON::PP Module::Runtime Sub::Name YAML autodie JSON::PP Cpanel::JSON::XS JSON::XS Mojolicious Sereal::Encoder Sereal::Decoder Math::BigInt Math::BigFloat builtin builtin::Backport ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if grep { $_ eq $mod } @exclude; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; if ($mod eq 'perl') { push @reports, ['perl', $want, $]]; next; } my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; if ($prefix) { my $have = Module::Metadata->new_from_file( File::Spec->catfile($prefix, $file) )->version; $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass('Reported prereqs'); # vim: ts=4 sts=4 sw=4 et: JSON-Schema-Modern-0.609/t/add-schema.t000640 000766 000024 00000101046 15001302761 017603 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Fatal; use Test::Warnings qw(warnings :no_end_test had_no_warnings); use List::Util 'unpairs'; use lib 't/lib'; use Helper; use constant METASCHEMA => 'https://json-schema.org/draft/2019-09/schema'; # spec version -> vocab classes my %vocabularies = unpairs(JSON::Schema::Modern->new->__all_metaschema_vocabulary_classes); subtest 'evaluate a document' => sub { my $document = JSON::Schema::Modern::Document->new( schema => { '$id' => 'https://foo.com', allOf => [ false, true ], }); my $js = JSON::Schema::Modern->new; $js->add_document($document); cmp_result( $js->evaluate(1, $document->canonical_uri)->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '', keywordLocation => '/allOf/0', absoluteKeywordLocation => 'https://foo.com#/allOf/0', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://foo.com#/allOf', error => 'subschema 0 is not valid', }, ], }, 'evaluate a Document object', ); cmp_result( { $js->_resource_index }, { 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), document => shallow($document), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'resource index from the document is copied to the main object', ); cmp_result( $js->evaluate(1, $document->canonical_uri)->TO_JSON, { valid => false, errors => $errors, }, 'evaluate a Document object again without error', ); }; subtest 'evaluate a uri' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate({ '$schema' => 1 }, METASCHEMA)->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '/$schema', keywordLocation => '/allOf/0/$ref/properties/$schema/type', absoluteKeywordLocation => 'https://json-schema.org/draft/2019-09/meta/core#/properties/$schema/type', error => 'got integer, not string', }, { instanceLocation => '', keywordLocation => '/allOf/0/$ref/properties', absoluteKeywordLocation => 'https://json-schema.org/draft/2019-09/meta/core#/properties', error => 'not all properties are valid', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => METASCHEMA.'#/allOf', error => 'subschema 0 is not valid', }, ], }, 'evaluate with a uri that is not yet loaded', ); cmp_result( { $js->_resource_index }, { map +( $_ => { path => '', canonical_uri => str($_), document => isa('JSON::Schema::Modern::Document'), specification_version => 'draft2019-09', vocabularies => $vocabularies{'draft2019-09'}, configs => {}, } ), METASCHEMA, map 'https://json-schema.org/draft/2019-09/meta/'.$_, qw(core applicator validation meta-data format content) }, 'the metaschema is now loaded and its resources are indexed', ); # and again, we can use the same resource without reloading it cmp_result( $js->evaluate({ '$schema' => 1 }, METASCHEMA)->TO_JSON, { valid => false, errors => $errors, }, 'evaluate against the metaschema again', ); # now use a subschema at that url to evaluate with. # multiple things are being tested here: # - we can load a schema resource, or find an existing one, with a fragment # - the json path we used is saved in the state, for correct errors cmp_result( $js->evaluate( 1, 'https://json-schema.org/draft/2019-09/meta/core#/properties/$schema', )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/type', absoluteKeywordLocation => 'https://json-schema.org/draft/2019-09/meta/core#/properties/$schema/type', error => 'got integer, not string', }, ], }, 'evaluate against the a subschema of the metaschema', ); cmp_result( $js->evaluate( 1, METASCHEMA.'#/does/not/exist', )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'EXCEPTION: unable to find resource "'.METASCHEMA.'#/does/not/exist"', }, ], }, 'evaluate against the a fragment of the metaschema that does not exist', ); cmp_result( $js->evaluate( 1, METASCHEMA.'#does_not_exist', )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'EXCEPTION: unable to find resource "'.METASCHEMA.'#does_not_exist"', }, ], }, 'evaluate against the a plain-name fragment of the metaschema that does not exist', ); }; subtest 'add a uri resource' => sub { my $js = JSON::Schema::Modern->new; cmp_result( my $get_metaschema = scalar $js->get(METASCHEMA), my $orig_metaschema = $js->_get_resource(METASCHEMA)->{document}->schema, '->get in scalar context on a URI to the head of a document', ); ok($get_metaschema != $orig_metaschema, 'get() did not return a reference to the original data'); cmp_result( [ $js->get(METASCHEMA) ], [ $js->_get_resource(METASCHEMA)->{document}->schema, all(isa('Mojo::URL'), str(METASCHEMA)) ], '->get in list context on a URI to the head of a document', ); cmp_result( scalar $js->get(METASCHEMA.'#/properties/definitions/type'), 'object', # $document->schema->{properties}{definitions}{type} '->get in scalar context on a URI to inside of a document', ); cmp_result( [ $js->get(METASCHEMA.'#/properties/definitions/type') ], [ 'object', all(isa('Mojo::URL'), str(METASCHEMA.'#/properties/definitions/type')) ], '->get in list context on a URI to inside of a document', ); }; subtest 'add a schema associated with a uri' => sub { my $js = JSON::Schema::Modern->new; like( exception { $js->add_schema('https://foo.com#/x/y/z', {}) }, qr/^cannot add a schema with a uri with a fragment/, 'cannot use a uri with a fragment', ); cmp_deeply( my $document = $js->add_schema( 'https://foo.com', { '$id' => 'https://bar.com', allOf => [ false, true ] }, ), all( isa('JSON::Schema::Modern::Document'), listmethods( resource_index => [ 'https://bar.com' => { path => '', canonical_uri => str('https://bar.com'), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, ], canonical_uri => [ str('https://bar.com') ], ), ), 'added the schema data with an associated uri; the document does not see the overridden uri', ); cmp_result( $js->evaluate(1, 'https://bar.com#/allOf/0')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', absoluteKeywordLocation => 'https://bar.com#/allOf/0', error => 'subschema is false', }, ], }, 'can now evaluate using a uri to a subschema of a resource we loaded earlier', ); cmp_result( $js->evaluate(1, 'https://foo.com')->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '', keywordLocation => '/allOf/0', absoluteKeywordLocation => 'https://bar.com#/allOf/0', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/allOf', absoluteKeywordLocation => 'https://bar.com#/allOf', error => 'subschema 0 is not valid', }, ], }, 'can also evaluate using a non-canonical uri', ); cmp_result( $js->add_document('https://bloop.com', $document), shallow($document), 'can add the same document and associate it with another schema', ); my @warnings = warnings { cmp_result( $js->add_schema('https://bloop.com', $document), shallow($document), 'can add the same document twice, using deprecated interface', ); }; cmp_result( \@warnings, [ re(qr/use of deprecated form of add_schema with document/) ], 'warned when using deprecated form of add_schema', ); # this actually does nothing, via the duplicate check in _add_resource cmp_result( $js->add_document($document), shallow($document), 'can add the same document again with the proper interface', ); cmp_result( { $js->_resource_index }, { map +( $_ => { path => '', canonical_uri => str('https://bar.com'), document => shallow($document), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, } ), qw(https://foo.com https://bar.com https://bloop.com) }, 'now the document is available as all three uris, with the same canonical_uri', ); }; subtest 'multiple anonymous schemas' => sub { my $js = JSON::Schema::Modern->new; cmp_result( $js->evaluate(1, { minimum => 1 })->TO_JSON, { valid => true }, 'evaluate an anonymous schema', ); cmp_deeply([ keys $js->{_resource_index}->%* ], [ '' ], 'one resource is indexed'); cmp_result( $js->evaluate(2, { minimum => 2 })->TO_JSON, { valid => true }, 'evaluate another anonymous schema', ); cmp_deeply([ keys $js->{_resource_index}->%* ], [ '' ], 'still only one resource is indexed'); }; subtest 'add a document without associating it with a uri' => sub { my $js = JSON::Schema::Modern->new; cmp_deeply( $js->add_document( my $document = JSON::Schema::Modern::Document->new( schema => { '$id' => 'https://bar.com', allOf => [ false, true ] }, )), all( isa('JSON::Schema::Modern::Document'), listmethods( resource_index => [ 'https://bar.com' => { path => '', canonical_uri => str('https://bar.com'), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, ], canonical_uri => [ str('https://bar.com') ], ), ), 'added the document without an associated uri', ); cmp_result( { $js->_resource_index }, { 'https://bar.com' => { path => '', canonical_uri => str('https://bar.com'), document => shallow($document), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'document only added under its canonical uri', ); }; subtest 'add a schema without a uri' => sub { my $js = JSON::Schema::Modern->new; cmp_deeply( my $document = $js->add_schema( { '$id' => 'https://bar.com', allOf => [ false, true ] }, ), all( isa('JSON::Schema::Modern::Document'), listmethods( resource_index => [ 'https://bar.com' => { path => '', canonical_uri => str('https://bar.com'), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, ], canonical_uri => [ str('https://bar.com') ], ), ), 'added the schema data without an associated uri', ); cmp_result( { $js->_resource_index }, { 'https://bar.com' => { path => '', canonical_uri => str('https://bar.com'), document => shallow($document), specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, }, }, 'document only added under its canonical uri', ); }; subtest '$ref to non-canonical uri' => sub { my $schema = { '$id' => 'http://localhost:4242/my_document', # the canonical_uri properties => { alpha => false, beta => { '$id' => 'beta', properties => { gamma => { minimum => 2, }, }, }, delta => { '$ref' => 'http://otherhost:4242/another_uri#/properties/alpha', }, }, }; my $js = JSON::Schema::Modern->new; $js->add_schema('http://otherhost:4242/another_uri', $schema); cmp_result( $js->evaluate({ alpha => 1 }, 'http://otherhost:4242/another_uri')->TO_JSON, { valid => false, errors => [ { instanceLocation => '/alpha', keywordLocation => '/properties/alpha', absoluteKeywordLocation => 'http://localhost:4242/my_document#/properties/alpha', error => 'property not permitted', }, { instanceLocation => '', keywordLocation => '/properties', absoluteKeywordLocation => 'http://localhost:4242/my_document#/properties', error => 'not all properties are valid', }, ], }, 'errors use the canonical uri, not the uri used to evaluate against', ); cmp_result( $js->evaluate({ gamma => 1 }, 'http://otherhost:4242/beta')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'EXCEPTION: unable to find resource "http://otherhost:4242/beta"', }, ], }, 'non-canonical uri is not used to resolve inner $id keywords', ); cmp_result( $js->evaluate({ gamma => 1 }, 'http://localhost:4242/beta')->TO_JSON, { valid => false, errors => [ { instanceLocation => '/gamma', keywordLocation => '/properties/gamma/minimum', absoluteKeywordLocation => 'http://localhost:4242/beta#/properties/gamma/minimum', error => 'value is less than 2', }, { instanceLocation => '', keywordLocation => '/properties', absoluteKeywordLocation => 'http://localhost:4242/beta#/properties', error => 'not all properties are valid', }, ], }, 'the canonical uri is updated when use the canonical uri, not the uri used to evaluate against', ); cmp_result( $js->evaluate({ delta => 1 }, 'http://otherhost:4242/another_uri')->TO_JSON, { valid => false, errors => [ { instanceLocation => '/delta', keywordLocation => '/properties/delta/$ref', absoluteKeywordLocation => 'http://localhost:4242/my_document#/properties/alpha', error => 'subschema is false', }, { instanceLocation => '', keywordLocation => '/properties', absoluteKeywordLocation => 'http://localhost:4242/my_document#/properties', error => 'not all properties are valid', }, ], }, 'canonical_uri is not always what was in the $ref, even when no local $id is present', ); cmp_result( $js->evaluate(1, 'http://otherhost:4242/another_uri#/properties/alpha')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', absoluteKeywordLocation => 'http://localhost:4242/my_document#/properties/alpha', error => 'subschema is false', }, ], }, 'canonical_uri fragment also needs to be adjusted', ); delete $schema->{properties}{beta}{'$id'}; cmp_result( $js->evaluate({ gamma => 1 }, 'http://otherhost:4242/another_uri#/properties/beta')->TO_JSON, { valid => false, errors => [ { instanceLocation => '/gamma', keywordLocation => '/properties/gamma/minimum', absoluteKeywordLocation => 'http://localhost:4242/beta#/properties/gamma/minimum', error => 'value is less than 2', }, { instanceLocation => '', keywordLocation => '/properties', absoluteKeywordLocation => 'http://localhost:4242/beta#/properties', error => 'not all properties are valid', }, ], }, 'canonical_uri starts out containing a fragment and can be appended to during traversal', ); }; subtest 'register a document against multiple uris, with absolute root uri' => sub { my $js = JSON::Schema::Modern->new; my $document = JSON::Schema::Modern::Document->new( schema => { '$id' => 'https://foo.com', '$anchor' => 'my_anchor', maximum => 1, '$defs' => { foo => { '$id' => 'my_dir', allOf => [ true ], }, }, }); my %more_configs; cmp_result( { $document->resource_index }, my $doc_resource_index = { 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), do { %more_configs = ( specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, ) }, anchors => { my_anchor => { path => '', canonical_uri => str('https://foo.com'), }, }, }, 'https://foo.com/my_dir' => { path => '/$defs/foo', canonical_uri => str('https://foo.com/my_dir'), %more_configs, }, }, 'identifiers stored for the document', ); $js->add_document($document); cmp_result( { $js->_resource_index }, my $main_resource_index = { 'https://foo.com' => { path => '', canonical_uri => str('https://foo.com'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://foo.com'), }, }, }, 'https://foo.com/my_dir' => { path => '/$defs/foo', canonical_uri => str('https://foo.com/my_dir'), document => shallow($document), %more_configs, }, }, 'resource index from the document is copied to the main object', ); $js->add_document('https://uri2.com', $document); cmp_result( { $js->_resource_index }, $main_resource_index = { %$main_resource_index, 'https://uri2.com' => { path => '', canonical_uri => str('https://foo.com'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://foo.com'), }, }, }, }, 'add a secondary uri for the same document', ); cmp_result( { $document->resource_index }, $doc_resource_index, 'secondary uri not also added to the document', ); like( exception { $js->add_schema('https://uri2.com', { x => 1 }) }, qr!^\Quri "https://uri2.com" conflicts with an existing schema resource\E!, 'cannot call add_schema with the same URI as for another schema', ); like( exception { $js->add_schema('https://uri3.com', { '$id' => 'https://foo.com', x => 1 }) }, qr!^\Quri "https://foo.com" conflicts with an existing schema resource\E!, 'cannot reuse the same $id in another document', ); cmp_result( { $js->_resource_index }, $main_resource_index, 'resource index remains unchanged after erroneous add_schema calls', ); $js->add_schema('https://uri4.com', +{ $document->schema->%* }); cmp_result( { $js->_resource_index }, { %$main_resource_index, 'https://uri4.com' => { path => '', canonical_uri => str('https://foo.com'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://foo.com'), }, }, }, }, 'adding the same schema content again is permitted', ); is( scalar $js->get('https://foo.com#i_do_not_exist'), undef, '->get in scalar context for a nonexistent resource returns undef', ); cmp_result( [ $js->get('https://foo.com#i_do_not_exist') ], [], '->get in list context for a nonexistent resource returns empty list', ); }; subtest 'register a document against multiple uris, with relative root uri' => sub { my $js = JSON::Schema::Modern->new; my $document = JSON::Schema::Modern::Document->new( schema => { '$id' => 'my_dir/', '$anchor' => 'my_anchor', maximum => 1, '$defs' => { foo => { '$id' => 'my_dir2', allOf => [ true ], }, }, }); my %more_configs; cmp_result( { $document->resource_index }, my $doc_resource_index = { 'my_dir/' => { path => '', canonical_uri => str('my_dir/'), do { %more_configs = ( specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, ) }, anchors => { my_anchor => { path => '', canonical_uri => str('my_dir/'), }, }, }, 'my_dir/my_dir2' => { path => '/$defs/foo', canonical_uri => str('my_dir/my_dir2'), %more_configs, }, }, 'identifiers stored for the document', ); $js->add_document($document); cmp_result( { $js->_resource_index }, my $main_resource_index = { 'my_dir/' => { path => '', canonical_uri => str('my_dir/'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('my_dir/'), }, }, }, 'my_dir/my_dir2' => { path => '/$defs/foo', canonical_uri => str('my_dir/my_dir2'), document => shallow($document), %more_configs, }, }, 'resource index from the document is copied to the main object', ); $js->add_document('https://uri2.com', $document); cmp_result( { $js->_resource_index }, $main_resource_index = { %$main_resource_index, 'https://uri2.com' => { path => '', canonical_uri => str('https://uri2.com/my_dir/'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://uri2.com/my_dir/'), }, }, }, 'https://uri2.com/my_dir/' => { path => '', canonical_uri => str('https://uri2.com/my_dir/'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://uri2.com/my_dir/'), }, }, }, 'https://uri2.com/my_dir/my_dir2' => { path => '/$defs/foo', canonical_uri => str('https://uri2.com/my_dir/my_dir2'), document => shallow($document), %more_configs, }, }, 'add a secondary (absolute) uri for the same document', ); cmp_result( { $document->resource_index }, $doc_resource_index, 'secondary uri not also added to the document', ); like( exception { $js->add_schema('https://uri2.com', { x => 1 }) }, qr!^\Quri "https://uri2.com" conflicts with an existing schema resource\E!, 'cannot call add_schema with the same URI as for another schema', ); like( exception { $js->add_schema('https://uri3.com', { '$id' => 'https://uri2.com', x => 1 }) }, qr!^\Quri "https://uri2.com" conflicts with an existing schema resource\E!, 'cannot reuse the same $id in another document', ); cmp_result( { $js->_resource_index }, $main_resource_index, 'resource index remains unchanged after erroneous add_schema calls', ); $js->add_schema('https://uri4.com', +{ $document->schema->%* }); cmp_result( { $js->_resource_index }, { %$main_resource_index, 'https://uri4.com' => { path => '', canonical_uri => str('https://uri4.com/my_dir/'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://uri4.com/my_dir/'), }, }, }, 'https://uri4.com/my_dir/' => { path => '', canonical_uri => str('https://uri4.com/my_dir/'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://uri4.com/my_dir/'), }, }, }, 'https://uri4.com/my_dir/my_dir2' => { path => '/$defs/foo', canonical_uri => str('https://uri4.com/my_dir/my_dir2'), document => shallow($document), %more_configs, }, }, 'adding the same schema content again is permitted', ); }; subtest 'register a document against multiple uris, with no root uri' => sub { my $js = JSON::Schema::Modern->new; my $document = JSON::Schema::Modern::Document->new( schema => { '$anchor' => 'my_anchor', maximum => 1, '$defs' => { foo => { '$id' => 'my_dir', allOf => [ true ], }, }, }); my %more_configs; cmp_result( { $document->resource_index }, my $doc_resource_index = { '' => { path => '', canonical_uri => str(''), do { %more_configs = ( specification_version => 'draft2020-12', vocabularies => $vocabularies{'draft2020-12'}, configs => {}, ) }, anchors => { my_anchor => { path => '', canonical_uri => str(''), }, }, }, 'my_dir' => { path => '/$defs/foo', canonical_uri => str('my_dir'), %more_configs, }, }, 'identifiers stored for the document', ); $js->add_document($document); cmp_result( { $js->_resource_index }, my $main_resource_index = { '' => { path => '', canonical_uri => str(''), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str(''), }, }, }, 'my_dir' => { path => '/$defs/foo', canonical_uri => str('my_dir'), document => shallow($document), %more_configs, }, }, 'resource index from the document is copied to the main object', ); $js->add_document('https://uri2.com', $document); cmp_result( { $js->_resource_index }, $main_resource_index = { %$main_resource_index, 'https://uri2.com' => { path => '', canonical_uri => str('https://uri2.com'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://uri2.com'), }, }, }, 'https://uri2.com/my_dir' => { path => '/$defs/foo', canonical_uri => str('https://uri2.com/my_dir'), document => shallow($document), %more_configs, }, }, 'add a secondary (absolute) uri for the same document', ); cmp_result( { $document->resource_index }, $doc_resource_index, 'secondary uri not also added to the document', ); like( exception { $js->add_schema('https://uri2.com', { x => 1 }) }, qr!^\Quri "https://uri2.com" conflicts with an existing schema resource\E!, 'cannot call add_schema with the same URI as for another schema', ); like( exception { $js->add_schema('https://uri3.com', { '$id' => 'https://uri2.com', x => 1 }) }, qr!^\Quri "https://uri2.com" conflicts with an existing schema resource\E!, 'cannot reuse the same $id in another document', ); cmp_result( { $js->_resource_index }, $main_resource_index, 'resource index remains unchanged after erroneous add_schema calls', ); $js->add_schema('https://uri4.com', +{ $document->schema->%* }); cmp_result( { $js->_resource_index }, { %$main_resource_index, 'https://uri4.com' => { path => '', canonical_uri => str('https://uri4.com'), document => shallow($document), %more_configs, anchors => { my_anchor => { path => '', canonical_uri => str('https://uri4.com'), }, }, }, 'https://uri4.com/my_dir' => { path => '/$defs/foo', canonical_uri => str('https://uri4.com/my_dir'), document => shallow($document), %more_configs, }, }, 'adding the same schema content again is permitted', ); }; subtest 'external resource with externally-supplied uri; main resource with multiple uris' => sub { my $js = JSON::Schema::Modern->new; $js->add_schema('http://localhost:1234/integer.json', { type => 'integer' }); $js->add_schema( 'https://secondary.com', my $schema = { '$id' => 'https://main.com', '$ref' => 'http://localhost:1234/integer.json', type => 'object', }, ); cmp_result( my $result = $js->evaluate('string', 'https://secondary.com')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$ref/type', absoluteKeywordLocation => 'http://localhost:1234/integer.json#/type', error => 'got string, not integer', }, { instanceLocation => '', keywordLocation => '/type', absoluteKeywordLocation => 'https://main.com#/type', error => 'got string, not object', }, ], }, 'all uris in result are correct, using secondary uri as the target', ); cmp_result( $js->evaluate('string', 'https://main.com')->TO_JSON, $result, 'all uris in result are correct, using main uri as the target', ); }; subtest 'document with no canonical URI, but assigned a URI through add_schema' => sub { my $js = JSON::Schema::Modern->new; # the document itself doesn't know about this URI, but the evaluator does $js->add_schema( 'https://localhost:1234/mydef.json', my $def_schema = { '$defs' => { integer => { type => 'integer' } } }, ); cmp_result( $js->evaluate( { foo => 'string' }, my $schema = { # no $id here! type => 'object', additionalProperties => { '$ref' => 'https://localhost:1234/mydef.json#/$defs/integer', }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo', keywordLocation => '/additionalProperties/$ref/type', # the canonical URI is what the evaluator knows it as, even if the document doesn't know absoluteKeywordLocation => 'https://localhost:1234/mydef.json#/$defs/integer/type', error => 'got string, not integer', }, { instanceLocation => '', keywordLocation => '/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'evaluate a schema referencing a document given an ad-hoc uri', ); # start over with a new evaluator... $js = JSON::Schema::Modern->new; $js->add_document( 'https://localhost:1234/mydef.json', JSON::Schema::Modern::Document->new(schema => { '$id' => 'https://otherhost.com/mydef.json', %$def_schema, }), ); cmp_result( $js->evaluate( { foo => 'string' }, $schema, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo', keywordLocation => '/additionalProperties/$ref/type', absoluteKeywordLocation => 'https://otherhost.com/mydef.json#/$defs/integer/type', error => 'got string, not integer', }, { instanceLocation => '', keywordLocation => '/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'adding a uri to an existing document does not change its canonical uri', ); }; had_no_warnings if $ENV{AUTHOR_TESTING}; done_testing; JSON-Schema-Modern-0.609/t/lib/000770 000766 000024 00000000000 15001302761 016175 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/zzz-acceptance-draft4.t000640 000766 000024 00000010215 15001302761 021715 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use List::Util 1.50 'head'; use Config; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } my $version = 'draft4'; my $orig_warn_handler = $SIG{__WARN__}; $SIG{__WARN__} = sub { return if $_[0] =~ /^no-longer-supported "id" keyword present \(at location "[^"]+"\): this should be rewritten as "\$id"/; goto &$orig_warn_handler if $orig_warn_handler; }; acceptance_tests( acceptance => { specification => $version, skip_dir => 'optional/format', }, evaluator => { specification_version => $version, validate_formats => 0, # not the default for the Format vocabulary for this draft collect_annotations => 0, }, output_file => $version.'-acceptance.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ # various edge cases that are difficult to accomodate $Config{ivsize} < 8 ? { file => 'multipleOf.json', group_description => 'small multiple of large integer', test_description => 'any integer is a multiple of 1e-8' } : (), Test::JSON::Schema::Acceptance->VERSION < '1.027' ? { file => 'refRemote.json', group_description => 'Location-independent identifier in remote ref', test_description => 'integer is valid' } : (), { file => 'optional/ecmascript-regex.json', group_description => '\w in patterns matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in pattern matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => '\w in patternProperties matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in patternProperties matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => [ 'ECMA 262 \d matches ascii digits only', 'ECMA 262 \D matches everything but ascii digits', 'ECMA 262 \w matches ascii letters only', 'ECMA 262 \W matches everything but ascii letters' ] }, # TODO, see test suite PR#505 { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \s matches whitespace', test_description => 'zero-width whitespace matches' }, { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \S matches everything but whitespace', test_description => 'zero-width whitespace does not match' }, # bignums are parsed into Math::BigFloat objects, which lose the distinction between 1.0 and 1 { file => 'optional/zeroTerminatedFloats.json', group_description => 'some languages do not distinguish between different types of numeric value', test_description => 'a float is not an integer even without fractional part' }, ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft4-acceptance.txt for test results JSON-Schema-Modern-0.609/t/traverse.t000640 000766 000024 00000047124 15001302761 017456 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use JSON::Schema::Modern::Utilities 'canonical_uri'; use lib 't/lib'; use Helper; subtest 'traversal with callbacks' => sub { my $schema = { '$id' => 'https://foo.com', '$defs' => { foo => { '$id' => 'recursive_subschema', type => [ 'integer', 'object' ], additionalProperties => { '$ref' => 'recursive_subschema' }, }, bar => { properties => { description => { '$ref' => '#/$defs/foo', const => { '$ref' => 'this is not a real ref' }, }, }, }, }, if => 1, # bad subschema allOf => [ {}, { '$ref' => '#/$defs/foo' }, { '$ref' => '#/$defs/bar' }, ], }; my %refs; my $if_callback_called; my $js = JSON::Schema::Modern->new; my $state = $js->traverse($schema, { callbacks => { '$ref' => sub ($schema, $state) { my $canonical_uri = canonical_uri($state); my $ref_uri = Mojo::URL->new($schema->{'$ref'}); $ref_uri = $ref_uri->to_abs($canonical_uri) if not $ref_uri->is_abs; $refs{$state->{traversed_schema_path}.$state->{schema_path}} = $ref_uri->to_string; }, if => sub { $if_callback_called = 1; }, }}); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/if', absoluteKeywordLocation => 'https://foo.com#/if', error => 'invalid schema type: integer', }, ], 'errors encountered during traversal are returned', ); ok(!$if_callback_called, 'callback for erroneous keyword was not called'); cmp_result( \%refs, { '/$defs/foo/additionalProperties' => 'https://foo.com/recursive_subschema', '/$defs/bar/properties/description' => 'https://foo.com#/$defs/foo', # no entry for 'if' -- callbacks are not called for keywords with errors '/allOf/1' => 'https://foo.com#/$defs/foo', '/allOf/2' => 'https://foo.com#/$defs/bar', }, 'extracted all the real $refs out of the schema, with locations and canonical targets', ); cmp_result( $state->{subschemas}, bag( '', '/$defs/bar', '/$defs/bar/properties/description', '/$defs/foo', '/$defs/foo/additionalProperties', '/allOf/0', '/allOf/1', '/allOf/2', '/if', ), 'identified all subschemas', ); }; subtest 'errors when parsing $schema keyword' => sub { my $js = JSON::Schema::Modern->new; my $state = $js->traverse({ '$schema' => true }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/$schema', error => '$schema value is not a string', }, ], '$schema is not a string', ); $state = $js->traverse({ '$schema' => 'whargarbl' }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/$schema', error => '"whargarbl" is not a valid URI', }, ], '$schema is not a URI', ); }; subtest 'default metaschema' => sub { my $js = JSON::Schema::Modern->new; my $state = $js->traverse( { '$defs' => { foo => { properties => 'not an object', }, }, }, ); cmp_result( $state, superhashof({ spec_version => 'draft2020-12', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData Unevaluated), ], }), 'dialect is properly determined', ); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/$defs/foo/properties', error => 'properties value is not an object', }, ], 'error within $defs is found, showing both Core and Applicator vocabularies are used', ); }; subtest 'traversing a dialect with different core keywords' => sub { my $js = JSON::Schema::Modern->new; my $state = $js->traverse( { '$id' => 'http://localhost:1234/root', '$schema' => 'http://json-schema.org/draft-07/schema#', definitions => { alpha => 1, }, }, ); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/definitions/alpha', absoluteKeywordLocation => 'http://localhost:1234/root#/definitions/alpha', error => 'invalid schema type: integer', }, ], 'dialect changes at root, with $id - dialect is switched in time to get a new keyword list for the core vocabulary', ); $state = $js->traverse( { '$id' => '#hello', '$schema' => 'http://json-schema.org/draft-07/schema#', definitions => { bloop => { '$id' => '/bloop', type => 'object', }, }, }, ); cmp_result($state->{errors}, [], 'no errors when parsing this schema'); cmp_result( $state->{identifiers}, { '' => { path => '', canonical_uri => str(''), specification_version => 'draft7', configs => {}, vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData), ], anchors => { hello => { path => '', canonical_uri => str(''), }, }, }, '/bloop' => { path => '/definitions/bloop', canonical_uri => str('/bloop'), specification_version => 'draft7', configs => {}, vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Validation FormatAnnotation Applicator Content MetaData), ], }, }, 'switched dialect in time to extract all identifiers, from root and definition', ); $state = $js->traverse( { '$schema' => 'http://json-schema.org/draft-07/schema#', definitions => { alpha => 1, }, }, ); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/definitions/alpha', error => 'invalid schema type: integer', }, ], 'dialect changes at root, no $id - dialect is switched in time to get a new keyword list for the core vocabulary', ); $state = $js->traverse( { '$defs' => { alpha => { '$id' => 'http://localhost:1234/inner', '$schema' => 'http://json-schema.org/draft-07/schema#', definitions => { alpha => 1, }, }, }, }, ); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/$defs/alpha/definitions/alpha', absoluteKeywordLocation => 'http://localhost:1234/inner#/definitions/alpha', error => 'invalid schema type: integer', }, ], 'dialect changes below root - dialect is switched in time to get a new keyword list for the core vocabulary', ); }; subtest '$schema without an $id, below the root' => sub { my $js = JSON::Schema::Modern->new; my $state = $js->traverse( { '$defs' => { alpha => { '$schema' => 'https://json-schema.org/draft/2019-09/schema', minimum => 2, }, }, }, ); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/$defs/alpha/$schema', error => '$schema can only appear at the schema resource root', }, ], '$schema cannot exist without an $id, or at the root', ); }; subtest 'duplicate identifiers' => sub { my $js = JSON::Schema::Modern->new; my $state = $js->traverse({ '$id' => 'https://base.com', allOf => [ { '$id' => 'https://foo.com' }, { '$id' => 'https://foo.com' }, ], }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/allOf/1/$id', absoluteKeywordLocation => 'https://base.com#/allOf/1/$id', error => 'duplicate canonical uri "https://foo.com" found (original at path "/allOf/0")', }, ], 'detected colliding $ids within a single schema', ); $state = $js->traverse({ '$id' => 'https://base.com', allOf => [ { '$id' => 'dir1', '$anchor' => 'foo' }, { '$id' => 'dir2', '$anchor' => 'foo' }, ], }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [], 'two anchors with different base uris are acceptable', ); $state = $js->traverse({ '$id' => 'https://base.com', allOf => [ { '$anchor' => 'foo' }, { '$anchor' => 'foo' }, ], }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/allOf/1/$anchor', absoluteKeywordLocation => 'https://base.com#/allOf/1/$anchor', error => 'duplicate anchor uri "https://base.com#foo" found (original at path "/allOf/0")', }, ], 'detected colliding $anchors within a single schema', ); }; subtest '$anchor without $id' => sub { my $js = JSON::Schema::Modern->new; my $state = $js->traverse({ '$anchor' => 'root_anchor', }); cmp_result( $state->{identifiers}, { '' => { path => '', canonical_uri => str(''), specification_version => 'draft2020-12', vocabularies => ignore, configs => {}, anchors => { root_anchor => { path => '', canonical_uri => str(''), }, }, }, }, 'found anchor at root, without an $id to pre-populate the identifiers hash', ); $state = $js->traverse({ properties => { foo => { '$anchor' => 'foo_anchor', }, }, }); cmp_result( $state->{identifiers}, { '' => { path => '', canonical_uri => str(''), specification_version => 'draft2020-12', vocabularies => ignore, configs => {}, anchors => { foo_anchor => { path => '/properties/foo', canonical_uri => str('#/properties/foo'), }, }, }, }, 'found anchor within schema, without an $id to pre-populate the identifiers hash', ); }; subtest 'traverse with overridden metaschema_uri' => sub { my $js = JSON::Schema::Modern->new; $js->add_schema({ '$id' => 'https://metaschema/with/wrong/spec', '$vocabulary' => { 'https://unknown' => true, 'https://unknown2' => false, }, }); my $state = $js->traverse(true, { metaschema_uri => 'https://metaschema/with/wrong/spec' }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], my $errors = [ { instanceLocation => '', keywordLocation => '/$vocabulary/https:~1~1unknown', absoluteKeywordLocation => 'https://metaschema/with/wrong/spec#/$vocabulary/https:~1~1unknown', error => '"https://unknown" is not a known vocabulary', }, { instanceLocation => '', keywordLocation => '/$vocabulary', absoluteKeywordLocation => 'https://metaschema/with/wrong/spec#/$vocabulary', error => 'the first vocabulary (by evaluation_order) must be Core', }, { instanceLocation => '', keywordLocation => '', error => '"https://metaschema/with/wrong/spec" is not a valid metaschema', }, ], 'metaschema_uri is overridden with a bad schema: same errors are returned', ); $state = $js->traverse( { '$id' => 'https://my-poor-schema/foo.json' }, { metaschema_uri => 'https://metaschema/with/wrong/spec' }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ $errors->@[0..1], { $errors->[2]->%*, absoluteKeywordLocation => 'https://my-poor-schema/foo.json', }, ], 'metaschema_uri is overridden with a bad schema: errors contain the right locations', ); $state = $js->traverse( true, { metaschema_uri => 'https://metaschema/with/wrong/spec', initial_schema_uri => 'https://my-poor-schema/foo.json#/$my_dialect_is', traversed_schema_path => '/$my_dialect_is', }); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { $errors->[0]->%*, keywordLocation => '/$my_dialect_is'.$errors->[0]{keywordLocation}, }, { $errors->[1]->%*, keywordLocation => '/$my_dialect_is'.$errors->[1]{keywordLocation}, }, { $errors->[2]->%*, keywordLocation => '/$my_dialect_is'.$errors->[2]{keywordLocation}, absoluteKeywordLocation => 'https://my-poor-schema/foo.json#/$my_dialect_is', }, ], 'metaschema_uri is overridden with a bad schema and there is a traversal path: errors contain the right locations', ); $js->add_schema({ '$id' => 'https://my/first/metaschema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/applicator' => true, 'https://json-schema.org/draft/2020-12/vocab/core' => true, # note: no validation! }, }); $state = $js->traverse( { '$id' => my $id = 'https://my/first/schema/with/custom/metaschema', # note: no $schema keyword! allOf => [ { minimum => 'not even an integer' } ], }, { metaschema_uri => 'https://my/first/metaschema' }, ); cmp_result( $state->{identifiers}, { $id => { canonical_uri => str($id), path => '', specification_version => 'draft2020-12', vocabularies => [ map 'JSON::Schema::Modern::Vocabulary::'.$_, qw(Core Applicator), ], configs => {}, } }, 'determined vocabularies to use for this schema', ); }; subtest 'start traversing below the document root' => sub { my $js = JSON::Schema::Modern->new; # Remember: at this point the $document object may exist, but its constructor hasn't finished yet # (until traverse() returns), and we have no idea where in the document we are, and the evaluator # isn't provided the document object yet. # The document data might not even be a JSON Schema. # Let's say the document actually looks like: # { # $self => 'my_document.yaml', # openapi => '3.1.1', # components => { # schemas => { # alpha => { # *** SUBSCHEMA BELOW *** # }, # }, # }, # } my $state = $js->traverse( { properties => { myprop => { allOf => [ { '$id' => 'inner_document', properties => { foo => 'not a valid schema', }, }, ], }, }, type => 'not a valid type', }, { initial_schema_uri => 'dir/my_subdocument#/subid', traversed_schema_path => '/components/alpha/subid', }, ); cmp_result( [ map $_->TO_JSON, $state->{errors}->@* ], [ { instanceLocation => '', keywordLocation => '/components/alpha/subid/type', absoluteKeywordLocation => 'dir/my_subdocument#/subid/type', error => 'unrecognized type "not a valid type"', }, { instanceLocation => '', keywordLocation => '/components/alpha/subid/properties/myprop/allOf/0/properties/foo', absoluteKeywordLocation => 'dir/inner_document#/properties/foo', error => 'invalid schema type: string', }, ], 'identified the overridden location of all errors during traverse', ); $state = $js->traverse( { properties => { myprop => { allOf => [ { '$id' => 'inner_document', # resolves to dir/inner_document properties => { foo => true }, }, ], }, }, }, { initial_schema_uri => 'dir/my_subdocument#/subid', traversed_schema_path => '/components/alpha/subid', }, ); cmp_result( $state->{identifiers}, { 'dir/inner_document' => { canonical_uri => str('dir/inner_document'), path => '/components/alpha/subid/properties/myprop/allOf/0', specification_version => 'draft2020-12', vocabularies => ignore, configs => {}, }, }, 'identifiers are correctly extracted when traversing below the document root', ); $state = $js->traverse( { # the path at this position is /components/alpha/subid properties => { alpha => { '$id' => 'alpha_id', # resolves to dir/alpha_id properties => { alpha_one => { '$id' => 'alpha_one_id', # resolves to dir/alpha_one_id }, alpha_two => { '$anchor' => 'alpha_two_anchor', # resolves to dir/alpha_id#alpha_two_anchor }, alpha_three => { '$anchor' => 'alpha_three_anchor', # resolves to dir/alpha_id#alpha_three_anchor }, }, }, beta => { # produces anchor definition: # base uri is dir/my_subdocument, # canonical uri is dir/my_subdocument#/subid/properties/beta # path is /components/alpha/subid/properties/beta '$anchor' => 'beta_anchor', # resolves to dir/my_subdocument#beta_anchor }, }, }, { # this is used for adjusting canonical_uri in extracted 'identifiers'; and for errors. # we can infer that there is an identifier 'dir/mysubdocument' at path '/components/alpha' initial_schema_uri => 'dir/my_subdocument#/subid', traversed_schema_path => '/components/alpha/subid', }, ); cmp_result( $state->{identifiers}, { 'dir/alpha_id' => { canonical_uri => str('dir/alpha_id'), path => '/components/alpha/subid/properties/alpha', specification_version => 'draft2020-12', vocabularies => ignore, configs => {}, anchors => { alpha_two_anchor => { canonical_uri => str('dir/alpha_id#/properties/alpha_two'), path => '/components/alpha/subid/properties/alpha/properties/alpha_two', }, alpha_three_anchor => { canonical_uri => str('dir/alpha_id#/properties/alpha_three'), path => '/components/alpha/subid/properties/alpha/properties/alpha_three', }, }, }, 'dir/alpha_one_id' => { canonical_uri => str('dir/alpha_one_id'), path => '/components/alpha/subid/properties/alpha/properties/alpha_one', specification_version => 'draft2020-12', vocabularies => ignore, configs => {}, }, 'dir/my_subdocument' => { # this is inferred when we process "$anchor": "beta_anchor" canonical_uri => str('dir/my_subdocument'), path => '/components/alpha', specification_version => 'draft2020-12', vocabularies => ignore, configs => {}, anchors => { beta_anchor => { canonical_uri => str('dir/my_subdocument#/subid/properties/beta'), path => '/components/alpha/subid/properties/beta', }, }, }, }, 'identifiers are correctly extracted when traversing below the document root, with anchor', ); }; done_testing; JSON-Schema-Modern-0.609/t/validate-schema.t000640 000766 000024 00000005027 15001302761 020646 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use if "$]" >= 5.022, experimental => 're_strict'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new; cmp_result( $js->validate_schema({ type => 'bloop' })->TO_JSON, { valid => false, errors => supersetof( { instanceLocation => '/type', keywordLocation => re(qr{/enum$}), absoluteKeywordLocation => 'https://json-schema.org/draft/2020-12/meta/validation#/$defs/simpleTypes/enum', error => 'value does not match', }, ), }, 'validate_schema on simple schema with no $schema keyword', ); cmp_result( $js->validate_schema({ '$schema' => 'https://json-schema.org/draft/2019-09/schema', type => 'bloop', })->TO_JSON, { valid => false, errors => supersetof( { instanceLocation => '/type', keywordLocation => re(qr{/enum$}), absoluteKeywordLocation => 'https://json-schema.org/draft/2019-09/meta/validation#/$defs/simpleTypes/enum', error => 'value does not match', }, ), }, 'validate_schema on schema with metaschema $schema keyword', ); $js->add_schema('http://example.com/myschema', { '$id' => 'http://example.com/myschema', type => 'boolean' }); cmp_result( $js->validate_schema({ '$schema' => 'http://example.com/myschema', type => 'bloop', })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/type', absoluteKeywordLocation => 'http://example.com/myschema#/type', error => 'got object, not boolean', }, ], }, 'validate_schema with custom metaschema', ); cmp_result( $js->validate_schema({ '$id' => '#/$defs/foo', '$schema' => 'http://json-schema.org/draft-07/schema#', })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$id', error => '$id value "#/$defs/foo" does not match required syntax', }, ], }, 'validate_schema with schema that validates against the metaschema, but fails in extra traverse checks', ); done_testing; JSON-Schema-Modern-0.609/t/stringy-numbers.t000640 000766 000024 00000013343 15001302761 020767 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; foreach my $config (0, 1) { note 'stringy_numbers = '.$config; my $js = JSON::Schema::Modern->new(stringy_numbers => $config); cmp_result( $js->evaluate(1, { $_ => '1' })->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/'.$_, error => $_.' value is not a number', } ], }, 'strings cannot be used in place of numbers in schema for '.$_, ) foreach qw(multipleOf maximum exclusiveMaximum minimum exclusiveMinimum); my $schema = { allOf => [ { type => 'string' }, { type => 'number' }, { type => 'integer' }, { type => [ 'object', 'number' ] }, { type => [ 'object', 'integer' ] }, ], }; cmp_result( $js->evaluate('blah', $schema)->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '', keywordLocation => '/allOf/1/type', error => 'got string, not number', }, { instanceLocation => '', keywordLocation => '/allOf/2/type', error => 'got string, not integer', }, { instanceLocation => '', keywordLocation => '/allOf/3/type', error => 'got string, not one of object, number', }, { instanceLocation => '', keywordLocation => '/allOf/4/type', error => 'got string, not one of object, integer', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschemas 1, 2, 3, 4 are not valid', }, ], }, 'strings that do not look like numbers are never valid as numbers', ); cmp_result( $js->evaluate('1.1', $schema)->TO_JSON, { valid => false, errors => $errors, }, 'by default "type": "string" does not accept numbers', ) if not $config; cmp_result( $js->evaluate('1.1', $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/2/type', error => 'got string, not integer', }, { instanceLocation => '', keywordLocation => '/allOf/4/type', error => 'got string, not one of object, integer', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschemas 2, 4 are not valid', }, ], }, 'using stringy numbers, numeric strings are treated as numbers but are still not always integers', ) if $config; $schema = { maximum => 5, exclusiveMaximum => 5, minimum => 15, exclusiveMinimum => 15, allOf => [ { multipleOf => 2 }, { multipleOf => 0.3 }, ], }; $errors = [ { instanceLocation => '', keywordLocation => '/maximum', error => 'value is greater than 5', }, { instanceLocation => '', keywordLocation => '/exclusiveMaximum', error => 'value is greater than or equal to 5', }, { instanceLocation => '', keywordLocation => '/minimum', error => 'value is less than 15', }, { instanceLocation => '', keywordLocation => '/exclusiveMinimum', error => 'value is less than or equal to 15', }, { instanceLocation => '', keywordLocation => '/allOf/0/multipleOf', error => 'value is not a multiple of 2', }, { instanceLocation => '', keywordLocation => '/allOf/1/multipleOf', error => 'value is not a multiple of 0.3', }, { instanceLocation => '', keywordLocation => '/allOf', error => 'subschemas 0, 1 are not valid', }, ]; my $data = 11e0; cmp_result( $js->evaluate($data, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'real numbers are always evaluated', ); $data = '11e0'; cmp_result( $js->evaluate($data, $schema)->TO_JSON, { valid => true }, 'by default, stringy numbers are not evaluated by numeric keywords', ) if $config == 0; cmp_result( $js->evaluate($data, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'with the config enabled, stringy numbers are treated as numbers by numeric keywords', ) if $config == 1; is(JSON::Schema::Modern::Utilities::get_type($data), 'string', 'data was not mutated'); $schema = { enum => [11, 12], const => 11, }; cmp_result( $js->evaluate($data, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/enum', error => 'value does not match', }, { instanceLocation => '', keywordLocation => '/const', error => 'value does not match', }, ], }, 'by default, stringy numbers are not the same as numbers using comparison keywords', ) if $config == 0; cmp_result( $js->evaluate($data, $schema)->TO_JSON, { valid => true }, 'with the config enabled, stringy numbers are the same as numbers using comparison keywords', ) if $config == 1; is(JSON::Schema::Modern::Utilities::get_type($data), 'string', 'data was not mutated'); } done_testing; JSON-Schema-Modern-0.609/t/00-report-prereqs.dd000644 000766 000024 00000034035 15001302761 021157 0ustar00etherstaff000000 000000 do { my $x = { 'configure' => { 'requires' => { 'Module::Build::Tiny' => '0.034', 'perl' => '5.020' } }, 'develop' => { 'recommends' => { 'Dist::Zilla::PluginBundle::Author::ETHER' => '0.166', 'Dist::Zilla::PluginBundle::Git::VersionManager' => '0.007' }, 'requires' => { 'Data::Validate::Domain' => '0.13', 'DateTime::Format::RFC3339' => '0', 'Email::Address::XS' => '1.04', 'Encode' => '0', 'File::Spec' => '0', 'IO::Handle' => '0', 'IPC::Open3' => '0', 'Net::IDN::Encode' => '0', 'Pod::Wordlist' => '0', 'Sereal' => '0', 'Test::CPAN::Changes' => '0.19', 'Test::CPAN::Meta' => '0', 'Test::CleanNamespaces' => '0.15', 'Test::EOL' => '0', 'Test::Kwalitee' => '1.21', 'Test::MinimumVersion' => '0', 'Test::Mojibake' => '0', 'Test::More' => '0.96', 'Test::NoTabs' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage::TrustMe' => '0.002001', 'Test::Portability::Files' => '0', 'Test::Spelling' => '0.17', 'Time::Moment' => '0' } }, 'runtime' => { 'requires' => { 'B' => '0', 'Carp' => '0', 'Cpanel::JSON::XS' => '4.38', 'Digest::MD5' => '0', 'Exporter' => '0', 'Feature::Compat::Try' => '0', 'File::ShareDir' => '0', 'Getopt::Long::Descriptive' => '0', 'JSON::PP' => '4.11', 'List::Util' => '1.55', 'MIME::Base64' => '0', 'Math::BigFloat' => '0', 'Math::BigInt' => '1.999701', 'Mojo::JSON' => '0', 'Mojo::JSON::Pointer' => '0', 'Mojo::Message::Response' => '0', 'Mojo::URL' => '0', 'Mojolicious' => '7.87', 'Moo' => '0', 'Moo::Role' => '0', 'MooX::TypeTiny' => '0.002002', 'Path::Tiny' => '0', 'Ref::Util' => '0.100', 'Safe::Isa' => '1.000008', 'Scalar::Util' => '0', 'Storable' => '0', 'Sub::Install' => '0', 'Types::Common::Numeric' => '0', 'Types::Standard' => '1.016003', 'autovivification' => '0', 'builtin::compat' => '0.003003', 'constant' => '0', 'experimental' => '0.026', 'if' => '0', 'namespace::clean' => '0', 'open' => '0', 'overload' => '0', 'perl' => 'v5.20.0', 'stable' => '0.031', 'strict' => '0', 'strictures' => '2', 'warnings' => '0' }, 'suggests' => { 'Class::XSAccessor' => '0', 'Data::Validate::Domain' => '0.13', 'DateTime::Format::RFC3339' => '0', 'Email::Address::XS' => '1.04', 'Net::IDN::Encode' => '0', 'Ref::Util::XS' => '0', 'Sereal' => '0', 'Time::Moment' => '0', 'Type::Tiny::XS' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'CPAN::Meta::Check' => '0.011', 'CPAN::Meta::Requirements' => '0', 'Data::Dumper' => '0', 'File::Spec' => '0', 'Math::BigInt' => '1.999701', 'Module::Metadata' => '0', 'Term::ANSIColor' => '0', 'Test2::API' => '0', 'Test::Deep' => '0', 'Test::Deep::UnorderedPairs' => '0', 'Test::Fatal' => '0', 'Test::File::ShareDir' => '0', 'Test::JSON::Schema::Acceptance' => '1.021', 'Test::Memory::Cycle' => '0', 'Test::More' => '0.96', 'Test::Needs' => '0', 'Test::Warnings' => '0', 'Test::Without::Module' => '0.19', 'lib' => '0', 'perl' => 'v5.20.0', 'utf8' => '0' } }, 'x_Dist_Zilla' => { 'requires' => { 'Dist::Zilla' => '5', 'Dist::Zilla::Plugin::Authority' => '1.009', 'Dist::Zilla::Plugin::AutoMetaResources' => '0', 'Dist::Zilla::Plugin::AutoPrereqs' => '5.038', 'Dist::Zilla::Plugin::Breaks' => '0', 'Dist::Zilla::Plugin::BumpVersionAfterRelease::Transitional' => '0.004', 'Dist::Zilla::Plugin::CheckIssues' => '0', 'Dist::Zilla::Plugin::CheckMetaResources' => '0', 'Dist::Zilla::Plugin::CheckPrereqsIndexed' => '0.019', 'Dist::Zilla::Plugin::CheckSelfDependency' => '0', 'Dist::Zilla::Plugin::CheckStrictVersion' => '0', 'Dist::Zilla::Plugin::ConfirmRelease' => '0', 'Dist::Zilla::Plugin::CopyFilesFromRelease' => '0', 'Dist::Zilla::Plugin::EnsureLatestPerl' => '0', 'Dist::Zilla::Plugin::ExecDir' => '0', 'Dist::Zilla::Plugin::FileFinder::ByName' => '0', 'Dist::Zilla::Plugin::GenerateFile::FromShareDir' => '0', 'Dist::Zilla::Plugin::Git::Check' => '0', 'Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch' => '0.004', 'Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts' => '0', 'Dist::Zilla::Plugin::Git::Commit' => '2.020', 'Dist::Zilla::Plugin::Git::Contributors' => '0.029', 'Dist::Zilla::Plugin::Git::Describe' => '0.004', 'Dist::Zilla::Plugin::Git::GatherDir' => '2.016', 'Dist::Zilla::Plugin::Git::Push' => '0', 'Dist::Zilla::Plugin::Git::Remote::Check' => '0', 'Dist::Zilla::Plugin::Git::Tag' => '0', 'Dist::Zilla::Plugin::GitHub::Update' => '0.40', 'Dist::Zilla::Plugin::GithubMeta' => '0.54', 'Dist::Zilla::Plugin::InstallGuide' => '1.200005', 'Dist::Zilla::Plugin::Keywords' => '0.004', 'Dist::Zilla::Plugin::License' => '5.038', 'Dist::Zilla::Plugin::Manifest' => '0', 'Dist::Zilla::Plugin::MetaConfig' => '0', 'Dist::Zilla::Plugin::MetaJSON' => '0', 'Dist::Zilla::Plugin::MetaNoIndex' => '0', 'Dist::Zilla::Plugin::MetaProvides::Package' => '1.15000002', 'Dist::Zilla::Plugin::MetaTests' => '0', 'Dist::Zilla::Plugin::MetaYAML' => '0', 'Dist::Zilla::Plugin::MinimumPerl' => '1.006', 'Dist::Zilla::Plugin::ModuleBuildTiny' => '0.012', 'Dist::Zilla::Plugin::MojibakeTests' => '0.8', 'Dist::Zilla::Plugin::NextRelease' => '5.033', 'Dist::Zilla::Plugin::PodSyntaxTests' => '5.040', 'Dist::Zilla::Plugin::PodWeaver' => '4.008', 'Dist::Zilla::Plugin::Prereqs' => '0', 'Dist::Zilla::Plugin::Prereqs::AuthorDeps' => '0.006', 'Dist::Zilla::Plugin::Prereqs::Soften' => '0', 'Dist::Zilla::Plugin::PromptIfStale' => '0', 'Dist::Zilla::Plugin::Readme' => '0', 'Dist::Zilla::Plugin::ReadmeAnyFromPod' => '0.142180', 'Dist::Zilla::Plugin::RewriteVersion::Transitional' => '0.006', 'Dist::Zilla::Plugin::Run::AfterBuild' => '0.041', 'Dist::Zilla::Plugin::Run::AfterRelease' => '0.038', 'Dist::Zilla::Plugin::Run::BeforeRelease' => '0', 'Dist::Zilla::Plugin::RunExtraTests' => '0.024', 'Dist::Zilla::Plugin::ShareDir' => '0', 'Dist::Zilla::Plugin::StaticInstall' => '0.005', 'Dist::Zilla::Plugin::Test::CPAN::Changes' => '0.012', 'Dist::Zilla::Plugin::Test::ChangesHasContent' => '0', 'Dist::Zilla::Plugin::Test::CheckBreaks' => '0', 'Dist::Zilla::Plugin::Test::CleanNamespaces' => '0.006', 'Dist::Zilla::Plugin::Test::Compile' => '2.039', 'Dist::Zilla::Plugin::Test::EOL' => '0.17', 'Dist::Zilla::Plugin::Test::Kwalitee' => '2.10', 'Dist::Zilla::Plugin::Test::MinimumVersion' => '2.000010', 'Dist::Zilla::Plugin::Test::NoTabs' => '0.08', 'Dist::Zilla::Plugin::Test::Pod::Coverage::TrustMe' => '0', 'Dist::Zilla::Plugin::Test::PodSpelling' => '2.006003', 'Dist::Zilla::Plugin::Test::Portability' => '2.000007', 'Dist::Zilla::Plugin::Test::ReportPrereqs' => '0.022', 'Dist::Zilla::Plugin::TestRelease' => '0', 'Dist::Zilla::Plugin::UploadToCPAN' => '0', 'Dist::Zilla::Plugin::UseUnsafeInc' => '0', 'Dist::Zilla::PluginBundle::Author::ETHER' => '0.154', 'Dist::Zilla::PluginBundle::Git::VersionManager' => '0.007', 'Software::License::Perl_5' => '0' } } }; $x; }JSON-Schema-Modern-0.609/t/ref.t000640 000766 000024 00000116122 15001302761 016372 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Fatal; use Storable 'dclone'; use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new; subtest 'local JSON pointer' => sub { cmp_result( $js->evaluate(true, { '$defs' => { true => true }, '$ref' => '#/$defs/true' })->TO_JSON, { valid => true }, 'can follow local $ref to a true schema', ); cmp_result( $js->evaluate(true, { '$defs' => { false => false }, '$ref' => '#/$defs/false' })->TO_JSON, superhashof({ valid => false }), 'can follow local $ref to a false schema', ); is( exception { my $result = $js->evaluate(true, { '$ref' => '#/$defs/nowhere' }); like( (($result->errors)[0])->error, qr{^EXCEPTION: unable to find resource "\#/\$defs/nowhere"}, 'got error for unresolvable ref', ); }, undef, 'no exception', ); }; subtest 'fragment with URI-escaped and JSON Pointer-escaped characters' => sub { cmp_result( $js->evaluate( 1, { '$defs' => { 'foo-bar-tilde~-slash/-braces{}-def' => true }, '$ref' => '#/$defs/foo-bar-tilde~0-slash~1-braces%7B%7D-def', }, )->TO_JSON, { valid => true }, 'can follow $ref with escaped components', ); }; subtest 'local anchor' => sub { cmp_result( $js->evaluate( true, { '$defs' => { true => { '$anchor' => 'true', }, }, '$ref' => '#true', }, )->TO_JSON, { valid => true }, 'can follow local $ref to an $anchor to a true schema', ); cmp_result( $js->evaluate( true, { '$defs' => { false => { '$anchor' => 'false', not => true, }, }, '$ref' => '#false', }, )->TO_JSON, superhashof({ valid => false }), 'can follow local $ref to an $anchor to a false schema', ); is( exception { my $result = $js->evaluate(true, { '$ref' => '#nowhere' }); like( (($result->errors)[0])->error, qr{^EXCEPTION: unable to find resource "\#nowhere"}, 'got error for unresolvable ref', ); }, undef, 'no exception', ); }; subtest '$id with an empty fragment' => sub { my $js = JSON::Schema::Modern->new(max_traversal_depth => 2); cmp_result( $js->evaluate( 1, { '$defs' => { foo => { '$id' => 'http://localhost:4242/my_foo#', type => 'string', }, reference_to_foo => { '$ref' => 'http://localhost:4242/my_foo', }, }, allOf => [ { '$ref' => 'http://localhost:4242/my_foo' }, { '$ref' => '#/$defs/reference_to_foo' }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/$ref/type', absoluteKeywordLocation => 'http://localhost:4242/my_foo#/type', error => 'got integer, not string', }, { absoluteKeywordLocation => 'http://localhost:4242/my_foo', error => 'EXCEPTION: maximum evaluation depth (2) exceeded', instanceLocation => '', keywordLocation => "/allOf/1/\$ref/\$ref", }, ], }, '$id with empty fragment can be found by $ref that did not include it; fragment not included in error either', ); }; $js = JSON::Schema::Modern->new(specification_version => 'draft2019-09'); subtest '$recursiveRef without nesting behaves like $ref' => sub { cmp_result( $js->evaluate( { foo => { bar => 'hello', baz => 1 } }, { '$id' => 'http://localhost:4242', '$recursiveAnchor' => true, anyOf => [ { type => 'string' }, { type => 'object', additionalProperties => { '$recursiveRef' => '#' }, }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/anyOf/0/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/0/type', error => 'got object, not string', }, # /anyOf/1 with '' # /anyOf/1/additionalProperties/$recursiveRef with '/foo' # /anyOf/1/additionalProperties/$recursiveRef/anyOf/0 - wrong type { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$recursiveRef/anyOf/0/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/0/type', error => 'got object, not string', }, # /anyOf/1/additionalProperties/$recursiveRef/anyOf/1 with /foo # additionalProperties: consider /foo/bar # /anyOf/1/additionalProperties/$recursiveRef/anyOf/1/additionalProperties/$recursiveRef # /anyOf/1/additionalProperties/$recursiveRef/anyOf/1/additionalProperties/$recursiveRef/anyOf/0 - is string, so no error # /anyOf/1/additionalProperties/$recursiveRef/anyOf/1/additionalProperties/$recursiveRef/anyOf/1 - is object # additionalProperties: consider /foo/baz # is neither string or object { instanceLocation => '/foo/baz', keywordLocation => '/anyOf/1/additionalProperties/$recursiveRef/anyOf/1/additionalProperties/$recursiveRef/anyOf/0/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/0/type', error => 'got integer, not string', }, { instanceLocation => '/foo/baz', keywordLocation => '/anyOf/1/additionalProperties/$recursiveRef/anyOf/1/additionalProperties/$recursiveRef/anyOf/1/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/1/type', error => 'got integer, not object', }, { instanceLocation => '/foo/baz', keywordLocation => '/anyOf/1/additionalProperties/$recursiveRef/anyOf/1/additionalProperties/$recursiveRef/anyOf', absoluteKeywordLocation => 'http://localhost:4242#/anyOf', error => 'no subschemas are valid', }, # and now we start to unwind. { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$recursiveRef/anyOf/1/additionalProperties', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$recursiveRef/anyOf', absoluteKeywordLocation => 'http://localhost:4242#/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/anyOf/1/additionalProperties', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/anyOf', absoluteKeywordLocation => 'http://localhost:4242#/anyOf', error => 'no subschemas are valid', }, ], }, '$recursiveRef without nested $recursiveAnchor behaves like $ref', ); }; subtest '$recursiveRef without $recursiveAnchor behaves like $ref' => sub { cmp_result( $js->evaluate( { foo => { bar => 1 } }, { properties => { foo => { '$recursiveRef' => '#' } }, additionalProperties => false, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo/bar', keywordLocation => '/properties/foo/$recursiveRef/additionalProperties', absoluteKeywordLocation => '#/additionalProperties', error => 'additional property not permitted', }, { instanceLocation => '/foo', keywordLocation => '/properties/foo/$recursiveRef/additionalProperties', absoluteKeywordLocation => '#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, '$recursiveRef without $recursiveAnchor behaves like $ref', ); }; subtest '$recursiveAnchor must be at a schema resource root' => sub { my $schema = { '$defs' => { myobject => { '$recursiveAnchor' => true, anyOf => [ { type => 'integer' }, { type => 'object', additionalProperties => { '$recursiveRef' => '#' }, }, ], }, }, anyOf => [ { type => 'integer' }, { '$ref' => '#/$defs/myobject' }, ], }; cmp_result( $js->evaluate({ foo => 1 }, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$defs/myobject/$recursiveAnchor', error => '"$recursiveAnchor" keyword used without "$id"', }, ], }, '$recursiveAnchor can only appear at a schema resource root', ); $schema = dclone($schema); $schema->{'$defs'}{myobject}{'$id'} = 'myobject.json'; cmp_result( $js->evaluate({ foo => 1 }, $schema)->TO_JSON, { valid => true, }, 'schema now valid when an $id is added', ); cmp_result( $js->evaluate( { foo => 1 }, { '$defs' => { inner => { '$recursiveAnchor' => true, # this is illegal - canonical uri has a fragment type => [ qw(integer object) ], additionalProperties => { '$recursiveRef' => '#/$defs/inner' }, }, }, type => 'object', additionalProperties => { '$recursiveRef' => '#/$defs/inner' }, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$defs/inner/$recursiveAnchor', error => '"$recursiveAnchor" keyword used without "$id"', }, ], }, '$recursiveAnchor can only appear at a schema resource root', ); cmp_result( $js->evaluate( { foo => 1 }, { allOf => [ { '$recursiveAnchor' => true, type => [ qw(integer object) ], additionalProperties => { '$recursiveRef' => '#/allOf/1' }, }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/allOf/0/$recursiveAnchor', error => '"$recursiveAnchor" keyword used without "$id"', }, ], }, 'properly detecting a bad $recursiveAnchor even before passing through a $ref', ); }; subtest '$recursiveAnchor and $recursiveRef - standard usecases' => sub { my $schema = { '$id' => 'https://base.com', #'$recursiveAnchor' => true, # the presence of this keyword changes everything type => [ 'object', 'integer' ], additionalProperties => { '$id' => 'https://innerbase.com', #'$recursiveAnchor' => true, # the presence of this keyword changes everything type => [ 'object', 'boolean' ], additionalProperties => { '$ref' => '#', # if this was a $recursiveRef and there are $recursiveAnchors, we will go to base. }, }, }; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '/foo/bar', keywordLocation => '/additionalProperties/additionalProperties/$ref/type', absoluteKeywordLocation => 'https://innerbase.com#/type', error => 'got integer, not one of object, boolean', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/additionalProperties', absoluteKeywordLocation => 'https://innerbase.com#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://base.com#/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'validation requires the override that is not in scope', ); # now make the ref a recursiveRef, but still won't recurse to base because no recursiveanchor. $js->{_resource_index} = {}; $schema->{additionalProperties}{additionalProperties}{'$recursiveRef'} = delete $schema->{additionalProperties}{additionalProperties}{'$ref'}; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => [ +{ $errors->[0]->%*, keywordLocation => ($errors->[0]{keywordLocation} =~ s/ref/recursiveRef/r), }, $errors->@[1..2], ], }, '$recursiveRef requires a $recursiveAnchor that does not exist', ); # now we will recurse to the base. $js->{_resource_index} = {}; $schema->{'$recursiveAnchor'} = true; cmp_result( $js->evaluate({ foo => true }, $schema)->TO_JSON, { valid => true, }, '$recursiveRef with both $recursiveAnchors in scope', ); }; subtest '$recursiveRef without $recursiveAnchor' => sub { my $schema = { '$id' => 'strings_only', '$defs' => { allow_ints => { '$id' => 'allow_ints', anyOf => [ { type => 'integer' }, { type => 'object', additionalProperties => { '$ref' => '#' } }, ], }, }, anyOf => [ { type => 'string' }, { type => 'object', additionalProperties => { '$ref' => '#' } }, ], }; cmp_result( $js->evaluate( { foo => 1 }, $schema, )->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '', keywordLocation => '/anyOf/0/type', absoluteKeywordLocation => 'strings_only#/anyOf/0/type', error => 'got object, not string', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$ref/anyOf/0/type', absoluteKeywordLocation => 'strings_only#/anyOf/0/type', error => 'got integer, not string', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$ref/anyOf/1/type', absoluteKeywordLocation => 'strings_only#/anyOf/1/type', error => 'got integer, not object', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$ref/anyOf', absoluteKeywordLocation => 'strings_only#/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/anyOf/1/additionalProperties', absoluteKeywordLocation => 'strings_only#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/anyOf', absoluteKeywordLocation => 'strings_only#/anyOf', error => 'no subschemas are valid', }, ], }, '$ref - one level recursion', ); $js->{_resource_index} = {}; cmp_result( $js->evaluate( { foo => 1 }, $js->_json_decoder->decode($js->_json_decoder->encode($schema) =~ s/\$ref/\$recursiveRef/gr), )->TO_JSON, { valid => false, errors => $js->_json_decoder->decode($js->_json_decoder->encode($errors) =~ s/\$ref/\$recursiveRef/gr), }, '$recursiveRef with no $recursiveAnchor in scope has the same outcome', ); }; subtest '$recursiveAnchor in our dynamic scope, but not in the target schema' => sub { my $schema = { '$id' => 'base', '$recursiveAnchor' => true, anyOf => [ { type => 'boolean' }, { type => 'object', additionalProperties => { '$id' => 'inner', # note: no $recursiveAnchor here! so we do NOT recurse to the base. anyOf => [ { type => 'integer' }, { type => 'object', additionalProperties => { '$recursiveRef' => '#' } }, ], }, }, ], }; cmp_result( $js->evaluate( { foo => { bar => 1 } }, $schema, )->TO_JSON, { valid => true, }, '$recursiveAnchor does not exist in the target schema - local recursion only, so integers match', ); cmp_result( $js->evaluate( { foo => true }, 'base', )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/anyOf/0/type', absoluteKeywordLocation => 'base#/anyOf/0/type', error => 'got object, not boolean', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/anyOf/0/type', absoluteKeywordLocation => 'inner#/anyOf/0/type', error => 'got boolean, not integer', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/anyOf/1/type', absoluteKeywordLocation => 'inner#/anyOf/1/type', error => 'got boolean, not object', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/anyOf', absoluteKeywordLocation => 'inner#/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/anyOf/1/additionalProperties', absoluteKeywordLocation => 'base#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/anyOf', absoluteKeywordLocation => 'base#/anyOf', error => 'no subschemas are valid', }, ], }, '$recursiveAnchor does not exist in the target schema - no recursion', ); cmp_result( $js->evaluate( { foo => { bar => true } }, 'base', )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/anyOf/0/type', absoluteKeywordLocation => 'base#/anyOf/0/type', error => 'got object, not boolean', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/anyOf/0/type', absoluteKeywordLocation => 'inner#/anyOf/0/type', error => 'got object, not integer', }, { instanceLocation => '/foo/bar', keywordLocation => '/anyOf/1/additionalProperties/anyOf/1/additionalProperties/$recursiveRef/anyOf/0/type', absoluteKeywordLocation => 'inner#/anyOf/0/type', error => 'got boolean, not integer', }, { instanceLocation => '/foo/bar', keywordLocation => '/anyOf/1/additionalProperties/anyOf/1/additionalProperties/$recursiveRef/anyOf/1/type', absoluteKeywordLocation => 'inner#/anyOf/1/type', error => 'got boolean, not object', }, { instanceLocation => '/foo/bar', keywordLocation => '/anyOf/1/additionalProperties/anyOf/1/additionalProperties/$recursiveRef/anyOf', absoluteKeywordLocation => 'inner#/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/anyOf/1/additionalProperties', absoluteKeywordLocation => 'inner#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/anyOf', absoluteKeywordLocation => 'inner#/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/anyOf/1/additionalProperties', absoluteKeywordLocation => 'base#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/anyOf', absoluteKeywordLocation => 'base#/anyOf', error => 'no subschemas are valid', }, ], }, '$recursiveAnchor does not exist in the target schema - local recursion only', ); }; $js = JSON::Schema::Modern->new; subtest '$dynamicRef without nesting behaves like $ref' => sub { cmp_result( $js->evaluate( { foo => { bar => 'hello', baz => 1 } }, { '$id' => 'http://localhost:4242', '$dynamicAnchor' => 'hi', anyOf => [ { type => 'string' }, { type => 'object', additionalProperties => { '$dynamicRef' => '#hi' }, }, ], }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/anyOf/0/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/0/type', error => 'got object, not string', }, # /anyOf/1 with '' # /anyOf/1/additionalProperties/$dynamicRef with '/foo' # /anyOf/1/additionalProperties/$dynamicRef/anyOf/0 - wrong type { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$dynamicRef/anyOf/0/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/0/type', error => 'got object, not string', }, # /anyOf/1/additionalProperties/$dynamicRef/anyOf/1 with /foo # additionalProperties: consider /foo/bar # /anyOf/1/additionalProperties/$dynamicRef/anyOf/1/additionalProperties/$dynamicRef # /anyOf/1/additionalProperties/$dynamicRef/anyOf/1/additionalProperties/$dynamicRef/anyOf/0 - is string, so no error # /anyOf/1/additionalProperties/$dynamicRef/anyOf/1/additionalProperties/$dynamicRef/anyOf/1 - is object # additionalProperties: consider /foo/baz # is neither string or object { instanceLocation => '/foo/baz', keywordLocation => '/anyOf/1/additionalProperties/$dynamicRef/anyOf/1/additionalProperties/$dynamicRef/anyOf/0/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/0/type', error => 'got integer, not string', }, { instanceLocation => '/foo/baz', keywordLocation => '/anyOf/1/additionalProperties/$dynamicRef/anyOf/1/additionalProperties/$dynamicRef/anyOf/1/type', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/1/type', error => 'got integer, not object', }, { instanceLocation => '/foo/baz', keywordLocation => '/anyOf/1/additionalProperties/$dynamicRef/anyOf/1/additionalProperties/$dynamicRef/anyOf', absoluteKeywordLocation => 'http://localhost:4242#/anyOf', error => 'no subschemas are valid', }, # and now we start to unwind. { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$dynamicRef/anyOf/1/additionalProperties', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '/foo', keywordLocation => '/anyOf/1/additionalProperties/$dynamicRef/anyOf', absoluteKeywordLocation => 'http://localhost:4242#/anyOf', error => 'no subschemas are valid', }, { instanceLocation => '', keywordLocation => '/anyOf/1/additionalProperties', absoluteKeywordLocation => 'http://localhost:4242#/anyOf/1/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/anyOf', absoluteKeywordLocation => 'http://localhost:4242#/anyOf', error => 'no subschemas are valid', }, ], }, '$dynamicRef without nested $dynamicAnchor behaves like $ref', ); }; subtest '$recursiveRef without $dynamicAnchor behaves like $ref' => sub { cmp_result( $js->evaluate( { foo => { bar => 1 } }, { properties => { foo => { '$dynamicRef' => '#' } }, additionalProperties => false, }, )->TO_JSON, { valid => false, errors => [ { instanceLocation => '/foo/bar', keywordLocation => '/properties/foo/$dynamicRef/additionalProperties', absoluteKeywordLocation => '#/additionalProperties', error => 'additional property not permitted', }, { instanceLocation => '/foo', keywordLocation => '/properties/foo/$dynamicRef/additionalProperties', absoluteKeywordLocation => '#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/properties', error => 'not all properties are valid', }, ], }, '$dynamicRef without $dynamicAnchor behaves like $ref', ); }; subtest '$dynamicAnchor and $dynamicRef - standard usecases' => sub { my $schema = { '$id' => 'https://base.com', #'$dynamicAnchor' => 'thingy', # adding this, and changing the $ref, will make us recurse here. type => [ 'object', 'integer' ], additionalProperties => { '$id' => 'https://innerbase.com', # without $dynamicRef, we will recurse here. #'$dynamicAnchor' => 'thingy', type => [ 'object', 'boolean' ], additionalProperties => { '$ref' => '#', # if this was a $dynamicRef and there are $dynamicAnchors, we will go to base. }, }, }; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '/foo/bar', keywordLocation => '/additionalProperties/additionalProperties/$ref/type', absoluteKeywordLocation => 'https://innerbase.com#/type', error => 'got integer, not one of object, boolean', }, { instanceLocation => '/foo', keywordLocation => '/additionalProperties/additionalProperties', absoluteKeywordLocation => 'https://innerbase.com#/additionalProperties', error => 'not all additional properties are valid', }, { instanceLocation => '', keywordLocation => '/additionalProperties', absoluteKeywordLocation => 'https://base.com#/additionalProperties', error => 'not all additional properties are valid', }, ], }, 'validation requires the override that is not in scope', ); # now make the ref a dynamicRef, but still won't recurse to base because no dynamicanchor. $js->{_resource_index} = {}; $schema->{additionalProperties}{additionalProperties}{'$dynamicRef'} = delete $schema->{additionalProperties}{additionalProperties}{'$ref'}; # '#' $errors->[0]{keywordLocation} =~ s/ref/dynamicRef/; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => $errors, }, '$dynamicRef requires a $dynamicAnchor that does not exist', ); # we still won't recurse to the base because $dynamicRef doesn't use the anchor URI. $js->{_resource_index} = {}; $schema->{additionalProperties}{'$dynamicAnchor'} = 'thingy'; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => $errors, }, '$dynamicRef must use a URI containing the dynamic anchor fragment', ); # use the anchor URI for $dynamicRef, but we still won't recurse to the base because there is no # outer $dynamicAnchor. $js->{_resource_index} = {}; $schema->{additionalProperties}{additionalProperties}{'$dynamicRef'} = '#thingy'; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'there is no outer $dynamicAnchor in scope to recurse to', ); # change $dynamicRef back to $ref, but use the fragment uri. $js->{_resource_index} = {}; $schema->{additionalProperties}{additionalProperties}{'$ref'} = delete $schema->{additionalProperties}{additionalProperties}{'$dynamicRef'}; # '#thingy' $errors->[0]{keywordLocation} =~ s/dynamicRef/ref/; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'we have an outer $dynamicAnchor, and are using the fragment URI, but we used $ref rather than $dynamicRef', ); # now add a $dynamicAnchor to base, but we still won't recurse to the base because $dynamicRef # doesn't use the anchor. $js->{_resource_index} = {}; delete $schema->{additionalProperties}{additionalProperties}{'$ref'}; $schema->{additionalProperties}{additionalProperties}{'$dynamicRef'} = '#'; $schema->{'$dynamicAnchor'} = 'thingy'; $errors->[0]{keywordLocation} =~ s/ref/dynamicRef/; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'there is an outer $dynamicAnchor in scope to recurse to, but $dynamicRef must use a URI containing the dynamic anchor fragment', ); $js->{_resource_index} = {}; $schema->{additionalProperties}{additionalProperties}{'$dynamicRef'} = '#thingy'; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => true, }, 'now everything is in place to recurse to the base', ); $js->{_resource_index} = {}; delete $schema->{additionalProperties}{'$dynamicAnchor'}; $schema->{additionalProperties}{additionalProperties}{'$dynamicRef'} = '#'; cmp_result( $js->evaluate({ foo => { bar => 1 } }, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'there is no $dynamicAnchor at the original target, and no anchor used in the target URI', ); }; subtest '$dynamicRef to $dynamicAnchor not directly in the evaluation path' => sub { $js->{_resource_index} = {}; my $schema = { '$id' => 'base', '$defs' => { override => { # this is in base uri 'base' #'$dynamicAnchor' => 'thingy', type => 'number', }, start => { '$id' => 'start', '$defs' => { main => { # start#thingy '$dynamicAnchor' => 'thingy', type => 'string', }, }, '$dynamicRef' => '#thingy', # -> start#thingy ( -> base#thingy ), when second anchor is in place }, }, '$ref' => 'start', }; cmp_result( $js->evaluate(42, $schema)->TO_JSON, { valid => false, errors => my $errors = [ { instanceLocation => '', keywordLocation => '/$ref/$dynamicRef/type', absoluteKeywordLocation => 'start#/$defs/main/type', error => 'got integer, not string', }, ], }, 'second dynamic anchor is not in the evaluation path, but we found it via dynamic scope - type does not match', ); $js->{_resource_index} = {}; $schema->{'$defs'}{override}{'$anchor'} = 'thingy'; cmp_result( $js->evaluate(42, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'regular $anchor in dynamic scope should not be used by $dynamicRef', ); $js->{_resource_index} = {}; delete $schema->{'$defs'}{override}{'$anchor'}; $schema->{'$defs'}{override}{'$dynamicAnchor'} = 'some_other_thingy'; cmp_result( $js->evaluate(42, $schema)->TO_JSON, { valid => false, errors => $errors, }, 'some other $dynamicAnchor in dynamic scope should not be used by $dynamicRef', ); $js->{_resource_index} = {}; $schema->{'$defs'}{override}{'$dynamicAnchor'} = 'thingy'; cmp_result( $js->evaluate(42, $schema)->TO_JSON, { valid => true, }, 'second dynamic anchor is not in the evaluation path, but we found it via dynamic scope - type matches', ); $js->{_resource_index} = {}; my $canonical_uri = delete $schema->{'$id'}; $js->add_schema($canonical_uri => $schema); cmp_result( $js->evaluate(42, $canonical_uri)->TO_JSON, { valid => true, }, 'the first dynamic scope is set by document uri, not just the $id keyword', ); }; subtest 'after leaving a dynamic scope, it should not be used by a $dynamicRef' => sub { $js->{_resource_index} = {}; my $schema = { '$id' => 'main', if => { '$id' => 'first_scope', '$defs' => { thingy => { # this is first_scope#thingy '$dynamicAnchor' => 'thingy', type => 'number', }, }, }, 'then' => { '$id' => 'second_scope', '$ref' => 'start', '$defs' => { 'thingy' => { # this is second_scope#thingy, the final destination of the $dynamicRef '$dynamicAnchor' => 'thingy', type => 'null', }, }, }, '$defs' => { start => { # this is the landing spot from $ref '$id' => 'start', '$dynamicRef' => 'inner_scope#thingy', }, 'thingy' => { # this is the first stop by the $dynamicRef '$id' => 'inner_scope', '$dynamicAnchor' => 'thingy', type => 'string', } } }; cmp_result( $js->evaluate(undef, $schema)->TO_JSON, { valid => true, }, 'first_scope is no longer in scope, so it is not used by $dynamicRef', ); }; subtest 'anchors do not match' => sub { $js->{_resource_index} = {}; my $schema = { '$defs' => { enhanced => { '$dynamicAnchor' => 'thingy', # change this to $anchor and watch what happens minimum => 2, }, orig => { '$id' => 'orig', '$dynamicAnchor' => 'thingy', minimum => 10, }, }, '$dynamicRef' => 'orig#thingy', }; cmp_result( $js->evaluate(1, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$dynamicRef/minimum', absoluteKeywordLocation => '#/$defs/enhanced/minimum', error => 'value is less than 2', }, ], }, '$dynamicRef goes to enhanced schema', ); $js->{_resource_index} = {}; $schema->{'$defs'}{enhanced}{'$anchor'} = delete $schema->{'$defs'}{enhanced}{'$dynamicAnchor'}; $schema->{'$defs'}{enhanced}{'$dynamicAnchor'} = 'somethingelse'; cmp_result( $js->evaluate(1, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$dynamicRef/minimum', absoluteKeywordLocation => 'orig#/minimum', error => 'value is less than 10', }, ], }, '$dynamicRef -> $dynamicAnchor -> $anchor is a no go: we stay at the original schema', ); }; subtest 'reference to a non-schema location' => sub { $js->{_resource_index} = {}; my $schema = { example => { not_a_schema => true }, '$defs' => { anchor => { '$dynamicAnchor' => 'my_anchor', '$dynamicRef' => '#/example/not_a_schema', }, }, type => 'object', properties => { '$ref' => { '$ref' => '#/example/not_a_schema', }, '$dynamicRef' => { '$dynamicRef' => '#/example/not_a_schema', }, }, }; cmp_result( $js->evaluate({ '$ref' => 1 }, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '/$ref', keywordLocation => '/properties/$ref/$ref', error => 'EXCEPTION: bad reference to "#/example/not_a_schema": not a schema', }, ], }, '$ref to a non-schema is not permitted', ); cmp_result( $js->evaluate({ '$dynamicRef' => 1 }, '')->TO_JSON, { valid => false, errors => [ { instanceLocation => '/$dynamicRef', keywordLocation => '/properties/$dynamicRef/$dynamicRef', error => 'EXCEPTION: bad reference to "#/example/not_a_schema": not a schema', }, ], }, '$dynamicRef to a non-schema is not permitted', ); $js->{_resource_index} = {}; $schema = { '$id' => '/foo', '$schema' => 'https://json-schema.org/draft/2019-09/schema', '$recursiveAnchor' => true, example => { not_a_schema => true }, type => 'object', properties => { '$recursiveRef' => { '$recursiveRef' => '#/example/not_a_schema', }, }, }; cmp_result( $js->evaluate({ '$recursiveRef' => 1 }, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '/$recursiveRef', keywordLocation => '/properties/$recursiveRef/$recursiveRef', absoluteKeywordLocation => '/foo#/properties/$recursiveRef/$recursiveRef', error => 'EXCEPTION: bad reference to "/foo#/example/not_a_schema": not a schema', }, ], }, '$recursiveRef to a non-schema is not permitted', ); package MyDocument { use strict; use warnings; use Moo; extends 'JSON::Schema::Modern::Document'; use experimental 'signatures'; sub traverse ($self, @) { return { initial_schema_uri => $self->canonical_uri, errors => [], spec_version => 'draft2020-12', vocabularies => [], configs => {}, identifiers => {}, subschemas => [], }; } }; $js->{_resource_index} = {}; my $doc = MyDocument->new( schema => [ 'not a json schema' ], canonical_uri => 'https://my_non_schema', ); $js->add_document($doc); $schema = { '$id' => '/foo', '$schema' => 'https://my_non_schema', }; cmp_result( $js->evaluate(1, $schema)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/$schema', # we haven't processed $id yet, so we don't know the absolute location error => 'EXCEPTION: bad reference to $schema "https://my_non_schema": not a schema', }, ], }, '$schema to a non-schema is not permitted', ); }; subtest 'evaluate at a non-schema location' => sub { $js->{_resource_index} = {}; $js->add_schema('http://my_schema', { example => { not_a_schema => true } }); cmp_result( $js->evaluate(1, 'http://my_schema#/example/not_a_schema')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'EXCEPTION: "http://my_schema#/example/not_a_schema" is not a schema', }, ], }, 'evaluating at a non-schema location is not permitted', ); }; done_testing; JSON-Schema-Modern-0.609/t/boolean-schemas.t000640 000766 000024 00000004004 15001302761 020651 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Fatal; use lib 't/lib'; use Helper; my $js = JSON::Schema::Modern->new; my @tests = ( { schema => false, valid => false }, { schema => true, valid => true }, { schema => {}, valid => true }, { schema => 0, valid => false }, { schema => 1, valid => false }, { schema => \0, valid => false }, { schema => \1, valid => false }, ); foreach my $test (@tests) { my $data = 'hello'; is( exception { my $result = $js->evaluate($data, $test->{schema}); ok(!($result->valid xor $test->{valid}), json_sprintf('schema: %s evaluates to: %s', $test->{schema}, $test->{valid})); cmp_result( $result->TO_JSON, { valid => $test->{valid}, $test->{valid} ? () : (errors => supersetof()), }, 'invalid result structure looks correct', ); }, undef, 'no exceptions in evaluate', ); } cmp_result( $js->evaluate('hello', [])->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'invalid schema type: array', }, ], }, 'invalid schema type results in error', ); $js = JSON::Schema::Modern->new(scalarref_booleans => 1); cmp_result( $js->evaluate('hello', \0)->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '', error => 'invalid schema type: reference to SCALAR', }, ], }, 'scalarref for schema results in error, even when scalarref_booleans is true', ); done_testing; JSON-Schema-Modern-0.609/t/zzz-check-breaks.t000644 000766 000024 00000002122 15001302761 020771 0ustar00etherstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::CheckBreaks 0.020 use Test::More tests => 2; use Term::ANSIColor 'colored'; SKIP: { skip 'no conflicts module found to check against', 1; } # this data duplicates x_breaks in META.json my $breaks = { "JSON::Schema::Modern::Document::OpenAPI" => "< 0.079", "JSON::Schema::Modern::Vocabulary::OpenAPI" => "< 0.080", "Mojolicious::Plugin::OpenAPI::Modern" => "< 0.014", "OpenAPI::Modern" => "< 0.077", "Test::Mojo::Role::OpenAPI::Modern" => "< 0.007" }; use CPAN::Meta::Requirements; use CPAN::Meta::Check 0.011; my $reqs = CPAN::Meta::Requirements->new; $reqs->add_string_requirement($_, $breaks->{$_}) foreach keys %$breaks; our $result = CPAN::Meta::Check::check_requirements($reqs, 'conflicts'); if (my @breaks = grep defined $result->{$_}, keys %$result) { diag colored('Breakages found with JSON-Schema-Modern:', 'yellow'); diag colored("$result->{$_}", 'yellow') for sort @breaks; diag "\n", colored('You should now update these modules!', 'yellow'); } pass 'checked x_breaks data'; JSON-Schema-Modern-0.609/t/invalid-schemas.t000640 000766 000024 00000002056 15001302761 020665 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use lib 't/lib'; use Helper; use Acceptance; foreach my $version (qw(draft2019-09 draft2020-12)) { acceptance_tests( acceptance => { specification => $version, test_dir => 't/invalid-schemas', include_optional => 0, test_schemas => 0, }, evaluator => { specification_version => $version, validate_formats => 1, collect_annotations => 0, }, output_file => $version.'-invalid-schemas.txt', ); } done_testing; __END__ see results in t/results/draft2019-09-invalid-schemas.txt t/results/draft2020-12-invalid-schemas.txt JSON-Schema-Modern-0.609/t/zzz-acceptance-draft4-format.t000640 000766 000024 00000005644 15001302761 023215 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::Needs; use List::Util 1.50 'head'; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } if ($ENV{EXTENDED_TESTING}) { test_needs { 'Time::Moment' => 0, 'DateTime::Format::RFC3339' => 0, 'Email::Address::XS' => '1.04', 'Data::Validate::Domain' => 0.13, }; } if ($ENV{AUTHOR_TESTING}) { eval { require Time::Moment; 1 } or fail $@; eval { require DateTime::Format::RFC3339; 1 } or fail $@; eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } or fail $@; eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } or fail $@; } my $version = 'draft4'; acceptance_tests( acceptance => { specification => $version, test_subdir => 'optional/format', }, evaluator => { specification_version => $version, # validate_formats behaviour should default to true for this draft collect_annotations => 0, }, output_file => $version.'-acceptance-format.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ { file => [ # these all depend on optional prereqs !$ENV{AUTHOR_TESTING} && !eval { require Time::Moment; 1 } ? 'date-time.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require DateTime::Format::RFC3339; 1 } ? 'date-time.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Email::Address::XS; Email::Address::XS->VERSION(1.04); 1 } ? 'email.json' : (), !$ENV{AUTHOR_TESTING} && !eval { require Data::Validate::Domain; Data::Validate::Domain->VERSION(0.13); 1 } ? 'hostname.json' : (), ] }, # various edge cases that are difficult to accomodate { file => 'uri.json', test_description => 'validation of URIs', test_description => 'an invalid URI with comma in scheme' }, # Mojo::URL does not fully validate ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft4-acceptance-format.txt for test results JSON-Schema-Modern-0.609/t/zzz-acceptance-draft2019-09.t000640 000766 000024 00000007645 15001302761 022410 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : no autovivification warn => qw(fetch store exists delete); use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use List::Util 1.50 'head'; use Config; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } my $version = 'draft2019-09'; my $orig_warn_handler = $SIG{__WARN__}; $SIG{__WARN__} = sub { return if $_[0] =~ /^no-longer-supported "dependencies" keyword present \(at location ""\): this should be rewritten as "dependentSchemas" or "dependentRequired"/; goto &$orig_warn_handler if $orig_warn_handler; }; acceptance_tests( acceptance => { specification => $version, skip_dir => 'optional/format', }, evaluator => { specification_version => $version, # validate_formats behaviour should default to false for this draft collect_annotations => 0, }, output_file => $version.'-acceptance.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ # I am not interested in back-supporting "dependencies" { file => 'optional/dependencies-compatibility.json' }, # various edge cases that are difficult to accomodate $Config{ivsize} < 8 ? { file => 'multipleOf.json', group_description => 'small multiple of large integer', test_description => 'any integer is a multiple of 1e-8' } : (), { file => 'optional/ecmascript-regex.json', group_description => '\w in patterns matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in pattern matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => '\w in patternProperties matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in patternProperties matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => [ 'ECMA 262 \d matches ascii digits only', 'ECMA 262 \D matches everything but ascii digits', 'ECMA 262 \w matches ascii letters only', 'ECMA 262 \W matches everything but ascii letters' ] }, # TODO, see test suite PR#505 { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \s matches whitespace', test_description => 'zero-width whitespace matches' }, { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \S matches everything but whitespace', test_description => 'zero-width whitespace does not match' }, # things we will never do { file => 'optional/refOfUnknownKeyword.json' }, ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft2019-09-acceptance.txt for test results JSON-Schema-Modern-0.609/t/vocabularies.t000640 000766 000024 00000017305 15001302761 020300 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use builtin::compat 'load_module'; use lib 't/lib'; use Helper; my $DUMP = shift; # regenerate this by running the test file with argument '1' use constant KEYWORDS => { # draft4 -> http://json-schema.org/draft-04/schema# 'draft4' => { Core => [qw( $schema id $ref definitions )], Validation => [qw( type enum multipleOf maximum exclusiveMaximum minimum exclusiveMinimum maxLength minLength pattern maxItems minItems uniqueItems maxProperties minProperties required )], FormatAnnotation => [qw( format )], Applicator => [qw( allOf anyOf oneOf not dependencies items additionalItems properties patternProperties additionalProperties )], MetaData => [qw( title description default )], }, # draft6 -> http://json-schema.org/draft-06/schema# 'draft6' => { Core => [qw( $schema $id $ref definitions )], Validation => [qw( type enum const multipleOf maximum exclusiveMaximum minimum exclusiveMinimum maxLength minLength pattern maxItems minItems uniqueItems maxProperties minProperties required )], FormatAnnotation => [qw( format )], Applicator => [qw( allOf anyOf oneOf not dependencies items additionalItems contains properties patternProperties additionalProperties propertyNames )], MetaData => [qw( title description default examples )], }, # draft7 -> http://json-schema.org/draft-07/schema# 'draft7' => { Core => [qw( $schema $id $ref definitions $comment )], Validation => [qw( type enum const multipleOf maximum exclusiveMaximum minimum exclusiveMinimum maxLength minLength pattern maxItems minItems uniqueItems maxProperties minProperties required )], FormatAnnotation => [qw( format )], Applicator => [qw( allOf anyOf oneOf not if then else dependencies items additionalItems contains properties patternProperties additionalProperties propertyNames )], Content => [qw( contentEncoding contentMediaType )], MetaData => [qw( title description default readOnly writeOnly examples )], }, # draft2019-09 -> https://json-schema.org/draft/2019-09/schema 'draft2019-09' => { Core => [qw( $schema $id $anchor $recursiveAnchor $ref $recursiveRef $vocabulary $defs $comment )], Validation => [qw( type enum const multipleOf maximum exclusiveMaximum minimum exclusiveMinimum maxLength minLength pattern maxItems minItems uniqueItems maxContains minContains maxProperties minProperties required dependentRequired )], FormatAnnotation => [qw( format )], Applicator => [qw( allOf anyOf oneOf not if then else dependentSchemas items additionalItems contains maxContains minContains properties patternProperties additionalProperties propertyNames unevaluatedItems unevaluatedProperties )], Content => [qw( contentEncoding contentMediaType contentSchema )], MetaData => [qw( title description default deprecated readOnly writeOnly examples )], }, # draft2020-12 -> https://json-schema.org/draft/2020-12/schema 'draft2020-12' => { Core => [qw( $schema $id $anchor $dynamicAnchor $ref $dynamicRef $vocabulary $defs $comment )], Validation => [qw( type enum const multipleOf maximum exclusiveMaximum minimum exclusiveMinimum maxLength minLength pattern maxItems minItems uniqueItems maxContains minContains maxProperties minProperties required dependentRequired )], FormatAnnotation => [qw( format )], Applicator => [qw( allOf anyOf oneOf not if then else dependentSchemas prefixItems items contains maxContains minContains properties patternProperties additionalProperties propertyNames )], Content => [qw( contentEncoding contentMediaType contentSchema )], MetaData => [qw( title description default deprecated readOnly writeOnly examples )], Unevaluated => [qw( unevaluatedItems unevaluatedProperties )], }, }; subtest 'valid keywords' => sub { if ($DUMP) { my $js = JSON::Schema::Modern->new; print STDERR "{\n"; foreach my $spec_version (sort { length($a) <=> length($b) || $a cmp $b } $js->SPECIFICATION_VERSIONS_SUPPORTED->@*) { # spec_version -> metaschema uri my $metaschema_uri = $js->METASCHEMA_URIS->{$spec_version}; print STDERR " # $spec_version -> $metaschema_uri\n"; # metaschema uri -> vocab list: [ spec_version, [ vocab classes ] ] foreach my $metaschema_info ($js->_get_metaschema_vocabulary_classes($metaschema_uri)) { print STDERR " '$spec_version' => {\n"; foreach my $class (sort $metaschema_info->[1]->@*) { my ($short_class) = $class =~ /::([^:]+)$/; print STDERR " $short_class => [qw(\n"; print STDERR " $_\n" foreach $class->keywords($spec_version); print STDERR " )],\n"; } print STDERR " },\n"; } } print STDERR "};\n\n"; pass('table dumped'); return; } my @classes = grep load_module($_)->does('JSON::Schema::Modern::Vocabulary'), map 'JSON::Schema::Modern::Vocabulary::'.$_, map $_->basename =~ s/\.pm$//r, grep /\.pm$/, Path::Tiny::path('lib/JSON/Schema/Modern/Vocabulary/')->children; my $table = { map { my $spec_version = $_; $spec_version => { map { my $class = $_; my @keywords = eval { $class->keywords($spec_version) }; @keywords ? (($class =~ /::([^:]+)$/) => \@keywords) : (); } @classes, }; } JSON::Schema::Modern->SPECIFICATION_VERSIONS_SUPPORTED->@* }; foreach my $spec_version (sort { length($a) <=> length($b) || $a cmp $b } keys KEYWORDS->%*) { foreach my $short_class (sort keys KEYWORDS->{$spec_version}->%*) { my $class = 'JSON::Schema::Modern::Vocabulary::'.$short_class; cmp_result( [ $class->keywords($spec_version) ], KEYWORDS->{$spec_version}{$short_class}, "$spec_version, $short_class: calculated keyword list matches hardcoded table", ); } } }; done_testing; JSON-Schema-Modern-0.609/t/multipleOf.t000640 000766 000024 00000004440 15001302761 017735 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use utf8; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Config; use lib 't/lib'; use Helper; my @tests = ( # data (dividend), schema value (divisor), expected result [ 4, 2, true ], [ 4, 1, true ], [ 4, 3, false ], [ 4.5, 1.5, true ], [ 4.5, 1, false ], [ 4.5, 3, false ], [ 4, 2, true ], [ 4, 2.5, false ], [ 5, 2.5, true ], [ 4.5, 2.25, true ], [ 4.5, 2.5, false ], [ 4.5, 2, false ], [ -(~0 >> 1) -1, 0.5, true ], # min signed int [ ~0 >> 1, 0.5, true ], # max signed int [ ~0, 0.5, true ], # max unsigned int ); my $js = JSON::Schema::Modern->new; my $note = $ENV{AUTHOR_TESTING} || $ENV{AUTOMATED_TESTING} ? \&diag : \¬e; sub run_test ($data, $schema_value, $expected) { local $Test::Builder::Level = $Test::Builder::Level + 1; my $result = $js->evaluate($data, { multipleOf => $schema_value }); my $pass = ok(!($result->valid xor $expected), "$data is ".($expected ? '' : 'not ')."a multiple of $schema_value"); $note->('got result: '.$result->dump) if not $pass; } subtest 'multipleOf, native types' => sub { foreach my $test (@tests) { my ($data, $schema_value, $expected) = @$test; run_test($data, $schema_value, $expected); }; }; subtest 'multipleOf, data is a bignum' => sub { foreach my $test (@tests) { my ($data, $schema_value, $expected) = @$test; run_test(Math::BigFloat->new($data), $schema_value, $expected); }; }; subtest 'multipleOf, multipleOf is a bignum' => sub { foreach my $test (@tests) { my ($data, $schema_value, $expected) = @$test; run_test($data, Math::BigFloat->new($schema_value), $expected); }; }; subtest 'multipleOf, data and multipleOf are bignums' => sub { foreach my $test (@tests) { my ($data, $schema_value, $expected) = @$test; run_test(Math::BigFloat->new($data), Math::BigFloat->new($schema_value), $expected); }; }; done_testing; JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/000770 000766 000024 00000000000 15001302761 022537 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/read_serialized_file000640 000766 000024 00000006110 15001302761 021474 0ustar00etherstaff000000 000000 # vim: set ts=8 sts=2 sw=2 tw=100 et ft=perl : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test2::V0 (); use Sereal::Decoder; use lib 't/lib'; use Helper; my $hub = Test2::API::test2_stack->top; $hub->set_count(13); # this is a test that is run from t/serialization.t, incorporating its results into that file. my @serialized_attributes = sort qw( specification_version output_format short_circuit max_traversal_depth validate_formats validate_content_schemas collect_annotations scalarref_booleans stringy_numbers strict _resource_index _vocabulary_classes _metaschema_vocabulary_classes ); my $result = subtest 'thaw object in a separate process' => sub { local $/; binmode STDIN, ':raw'; my $thawed = Sereal::Decoder->new->decode(<>); cmp_result( [ sort keys %$thawed ], [ sort @serialized_attributes ], 'thawed object in a new process contains all the right keys', ); cmp_result( $thawed->evaluate(1, 'https://my_schema')->TO_JSON, { valid => true, annotations => [ map +{ instanceLocation => '', keywordLocation => '/'.$_, absoluteKeywordLocation => 'https://my_schema#/'.$_, annotation => Test::Deep::ignore, }, 'format', sort qw(type unknown properties contentMediaType contentSchema), ], }, 'in thawed object, evaluate data against schema with custom dialect; format and unknown keywords are collected as annotations', ); my $strict_metaschema = { '$id' => 'https://my_strict_metaschema', '$schema' => 'https://json-schema.org/draft/2020-12/schema', '$vocabulary' => { 'https://json-schema.org/draft/2020-12/vocab/core' => true, 'https://json-schema.org/draft/2020-12/vocab/format-assertion' => true, }, }; my $strict_schema = { '$id' => 'https://my_strict_schema', '$schema' => 'https://my_strict_metaschema', type => 'number', format => 'ipv4', unknown => 1, properties => { hello => false }, contentMediaType => 'application/json', contentSchema => {}, }; $thawed->add_schema($strict_metaschema); $thawed->add_schema($strict_schema); cmp_deeply( $thawed->evaluate('foo', 'https://my_strict_schema')->TO_JSON, { valid => false, errors => [ { instanceLocation => '', keywordLocation => '/format', absoluteKeywordLocation => 'https://my_strict_schema#/format', error => 'not a valid ipv4', }, ], }, 'evaluate data against schema with custom dialect; format-assertion is used', ); }; # skip the END block which would normally try to print a plan Test2::API::test2_stack->top->set_no_ending(1); exit($result ? 0 : -1); JSON-Schema-Modern-0.609/t/zzz-acceptance-draft2020-12.t000640 000766 000024 00000010105 15001302761 022353 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use List::Util 1.50 'head'; use Config; use lib 't/lib'; use Helper; use Acceptance; BEGIN { my @variables = qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING); plan skip_all => 'These tests may fail if the test suite continues to evolve! They should only be run with ' .join(', ', map $_.'=1', head(-1, @variables)).' or '.$variables[-1].'=1' if not grep $ENV{$_}, @variables; } my $version = 'draft2020-12'; my $orig_warn_handler = $SIG{__WARN__}; $SIG{__WARN__} = sub { return if $_[0] =~ /^no-longer-supported "dependencies" keyword present \(at location ""\): this should be rewritten as "dependentSchemas" or "dependentRequired"/; goto &$orig_warn_handler if $orig_warn_handler; }; acceptance_tests( acceptance => { specification => $version, skip_dir => 'optional/format', }, evaluator => { specification_version => $version, # validate_formats behaviour should default to false for this draft collect_annotations => 0, }, output_file => $version.'-acceptance.txt', test => { $ENV{NO_TODO} ? () : ( todo_tests => [ # I am not interested in forward-supporting "dependencies" { file => 'optional/dependencies-compatibility.json' }, # various edge cases that are difficult to accomodate $Config{ivsize} < 8 ? { file => 'multipleOf.json', group_description => 'small multiple of large integer', test_description => 'any integer is a multiple of 1e-8' } : (), Test::JSON::Schema::Acceptance->VERSION < '1.027' ? { file => 'optional/ecmascript-regex.json', group_description => '\a is not an ECMA 262 control escape', test_description => 'when used as a pattern' } : (), { file => 'optional/ecmascript-regex.json', group_description => '\w in patterns matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in pattern matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => '\w in patternProperties matches [A-Za-z0-9_], not unicode letters', test_description => [ 'literal unicode character in json string', 'unicode character in hex format in string' ] }, { file => 'optional/ecmascript-regex.json', group_description => '\d in patternProperties matches [0-9], not unicode digits', test_description => 'non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)' }, { file => 'optional/ecmascript-regex.json', group_description => [ 'ECMA 262 \d matches ascii digits only', 'ECMA 262 \D matches everything but ascii digits', 'ECMA 262 \w matches ascii letters only', 'ECMA 262 \W matches everything but ascii letters' ] }, # TODO, see test suite PR#505 { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \s matches whitespace', test_description => 'zero-width whitespace matches' }, { file => 'optional/ecmascript-regex.json', group_description => 'ECMA 262 \S matches everything but whitespace', test_description => 'zero-width whitespace does not match' }, # things we will never do { file => 'optional/refOfUnknownKeyword.json' }, ] ), }, ); END { diag <new->is_passing; } done_testing; __END__ see t/results/draft2020-12-acceptance.txt for test results JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/ref-and-id.json000640 000766 000024 00000006116 15001302761 025343 0ustar00etherstaff000000 000000 [ { "description": "$id must be evaluated before $ref to get the proper $ref destination (with $anchors)", "schema": { "$id": "/ref-and-id1/base.json", "$ref": "#bigint", "$defs": { "bigint": { "$comment": "canonical uri: /ref-and-id1/base.json/$defs/bigint; another valid uri for this location: /ref-and-id1/base.json#bigint", "$anchor": "bigint", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /ref-and-id1#/$defs/smallint; another valid uri for this location: /ref-and-id1/#bigint", "$id": "/ref-and-id1/", "$anchor": "bigint", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "$id must be evaluated before $ref to get the proper $ref destination (with uris)", "schema": { "$id": "/ref-and-id2/base.json", "$ref": "int.json", "$defs": { "bigint": { "$comment": "canonical uri: /ref-and-id2/int.json", "$id": "int.json", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /ref-and-id2-int.json", "$id": "/ref-and-id2-int.json", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "$id must be evaluated before $ref to get the proper $ref destination (with json pointers)", "schema": { "$id": "https://localhost:1234/base/", "allOf": [ { "$id": "allOf0/", "$ref": "#/oneOf/0", "oneOf": [ { "type": "number" } ] } ], "oneOf": [ { "type": "string" }, { "type": "number" } ] }, "tests": [ { "description": "data is valid against innermost type check", "data": 1, "valid": true }, { "description": "data is invalid against innermost type check", "data": "hello", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/formats.json000640 000766 000024 00000002441 15001302761 025105 0ustar00etherstaff000000 000000 [ { "description": "format validation after a $ref", "schema": { "$defs": { "my_format": { "type": "string", "format": "duration" } }, "$ref": "#/$defs/my_format" }, "tests": [ { "description": "invalid duration", "data": "PT1D", "valid": false }, { "description": "valid duration", "data": "P4Y", "valid": true } ] }, { "description": "format validation after an $id", "schema": { "type": "array", "items": { "$id": "bloop", "allOf": [ { "type": "string", "format": "duration" } ] } }, "tests": [ { "description": "invalid duration", "data": [ "PT1D" ], "valid": false }, { "description": "valid duration", "data": [ "P4Y" ], "valid": true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/anchor.json000640 000766 000024 00000001606 15001302761 024706 0ustar00etherstaff000000 000000 [ { "description": "non-schema object containing an $anchor property", "schema": { "$defs": { "const_not_anchor": { "const": { "$anchor": "not_a_real_anchor" } } }, "if": { "const": "skip not_a_real_anchor" }, "then": true, "else" : { "$ref": "#/$defs/const_not_anchor" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_anchor", "valid": true }, { "description": "const at const_not_anchor does not match", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/loose-types-const-enum.json000640 000766 000024 00000003660 15001302761 030007 0ustar00etherstaff000000 000000 [ { "description": "type can be inferred from const and enum values", "comment": "in some languages, 6 and '6' are equal in both stringwise and numeric comparisons, but the JSON type model is more strict", "schema": { "properties": { "const_string": { "const": "6" }, "const_number": { "const": 6 }, "enum_string": { "enum": [ "6" ] }, "enum_number": { "enum": [ 6 ] } } }, "tests": [ { "description": "const numeric value does not match string", "data": { "const_string": 6 }, "valid": false }, { "description": "const numeric value does match number", "data": { "const_number": 6 }, "valid": true }, { "description": "const string value matches string", "data": { "const_string": "6" }, "valid": true }, { "description": "const string value does not match number", "data": { "const_number": "6" }, "valid": false }, { "description": "enum numeric value does not match string", "data": { "enum_string": 6 }, "valid": false }, { "description": "enum numeric value does match number", "data": { "enum_number": 6 }, "valid": true }, { "description": "enum string value matches string", "data": { "enum_string": "6" }, "valid": true }, { "description": "enum string value does not match number", "data": { "enum_number": "6" }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/unknownKeyword.json000640 000766 000024 00000002102 15001302761 026470 0ustar00etherstaff000000 000000 [ { "description": "past and future keywords are ignored", "schema": { "id": "#bad_syntax!for anchor~", "$dynamicAnchor": "!~bunkanchor", "$dynamicRef": "http://does-not-exist.com", "dependencies": { "alpha": false, "beta": [ "zeta" ] }, "prefixItems": [ true, false ], "type": "number" }, "tests": [ { "description": "passing", "data": 1, "valid": true }, { "description": "failing (string)", "data": "foo", "valid": false }, { "description": "failing (object)", "data": { "alpha": 1, "beta": 2 }, "valid": false }, { "description": "failing (array)", "data": [ 1, 2 ], "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/vocabulary.json000640 000766 000024 00000004506 15001302761 025605 0ustar00etherstaff000000 000000 [ { "description": "valid $vocabulary", "schema": { "$id": "https://mymetaschema", "$vocabulary": { "https://json-schema.org/draft/2019-09/vocab/core": true, "https://foo.com": false } }, "tests": [ { "description": "a valid $vocabulary value", "data": 1, "valid": true } ] }, { "description": "invalid $vocabulary (wrong specification version), but not until we evaluate with this metaschema", "schema": { "$id": "https://mymetaschema2", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true } }, "tests": [ { "description": "invalid $vocabulary (wrong specification version): register this metaschema", "data": 1, "valid": true } ] }, { "description": "use schema (invalid $vocabulary) as a metaschema", "schema": { "$id": "https://mychema2", "$schema": "https://mymetaschema2" }, "tests": [ { "description": "invalid $vocabulary value: wrong specification version", "data": 1, "valid": false } ] }, { "description": "invalid $vocabulary (unknown URI), but not until we evaluate with this metaschema", "schema": { "$id": "https://mymetaschema3", "$vocabulary": { "https://json-schema.org/draft/2019-09/vocab/core": true, "https://foo.com": true } }, "tests": [ { "description": "invalid $vocabulary (unknown URI): register this metaschema", "data": 1, "valid": true } ] }, { "description": "use schema (unknown URI) as a metaschema", "schema": { "$id": "https://myschema3", "$schema": "https://mymetaschema3" }, "tests": [ { "description": "invalid $vocabulary value: unknown URI", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/badRef.json000640 000766 000024 00000003534 15001302761 024621 0ustar00etherstaff000000 000000 [ { "description": "good references in schemas", "schema": { "if": false, "then": { "allOf": [ { "$ref": "foo/bar.json" }, { "$ref": "foo/bar.json#" }, { "$ref": "foo/bar.json#anchor" }, { "$ref": "foo/bar.json#/" }, { "$ref": "foo/bar.json#/foo" }, { "$ref": "foo/bar.json#/~1/foo" }, { "$recursiveRef": "foo/bar.json" }, { "$recursiveRef": "foo/bar.json#" }, { "$recursiveRef": "foo/bar.json#anchor" }, { "$recursiveRef": "foo/bar.json#/" }, { "$recursiveRef": "foo/bar.json#/foo" }, { "$recursiveRef": "foo/bar.json#/~1/foo" } ] } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": true } ] }, { "description": "bad $ref in schema: not a valid anchor fragment", "schema": { "if": false, "then": { "$ref": "foo/bar.json#!!not-an-anchor" } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": false } ] }, { "description": "bad $ref in schema: not a valid json pointer fragment", "schema": { "if": false, "then": { "$ref": "foo/bar.json#/~foo" } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/format-ipv6.json000640 000766 000024 00000005751 15001302761 025613 0ustar00etherstaff000000 000000 [ { "description": "validation of ipv6 strings", "schema": { "type": "string", "format": "ipv6" }, "tests": [ { "description": "fully expanded with zeroes", "data": "0000:0000:0000:0000:0000:0000:0000:0000", "valid": true }, { "description": "partially collapsed zeroes", "data": "00:000:00:0:000:000:000:0000", "valid": true }, { "description": "4,2 zeroes", "data": "0:0:0:0::0:0", "valid": true }, { "description": "8 single zeroes, no double colon", "data": "0:0:0:0:0:0:0:0", "valid": true }, { "description": "7 single zeroes, leading double colon", "data": "::0:0:0:0:0:0:0", "valid": true }, { "description": "6 single zeroes, leading double colon", "data": "::0:0:0:0:0:0", "valid": true }, { "description": "5 single zeroes, leading double colon", "data": "::0:0:0:0:0", "valid": true }, { "description": "4 single zeroes, leading double colon", "data": "::0:0:0:0", "valid": true }, { "description": "3 single zeroes, leading double colon", "data": "::0:0:0", "valid": true }, { "description": "2 single zeroes, leading double colon", "data": "::0:0", "valid": true }, { "description": "1 single zero, leading double colon", "data": "::0", "valid": true }, { "description": "1 single zero, trailing double colon", "data": "0::", "valid": true }, { "description": "2 leading colons with 8 zeroes", "data": "::0:0:0:0:0:0:0:0", "valid": false }, { "description": "1 leading colon with 8 zeroes", "data": ":0:0:0:0:0:0:0:0", "valid": false }, { "description": "colons in the middle with 8 zeroes", "data": "0:0:0:0::0:0:0:0", "valid": false }, { "description": "1 trailing colon with 8 zeroes", "data": "0:0:0:0:0:0:0:0:", "valid": false }, { "description": "2 trailing colons with 8 zeroes", "data": "0:0:0:0:0:0:0:0::", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/keyword-independence.json000640 000766 000024 00000546227 15001302761 027554 0ustar00etherstaff000000 000000 [ { "description" : "contains + exclusiveMaximum", "schema" : { "contains" : true, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "contains + exclusiveMinimum", "schema" : { "contains" : true, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "contains + maximum", "schema" : { "contains" : true, "maximum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "contains + minimum", "schema" : { "contains" : true, "minimum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "contains + multipleOf", "schema" : { "contains" : true, "multipleOf" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + items", "schema" : { "exclusiveMaximum" : 2, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + items", "schema" : { "exclusiveMinimum" : 2, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "items + maximum", "schema" : { "items" : [ false ], "maximum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "items + minimum", "schema" : { "items" : [ false ], "minimum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "items + multipleOf", "schema" : { "items" : [ false ], "multipleOf" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxItems", "schema" : { "exclusiveMaximum" : 2, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxItems", "schema" : { "exclusiveMinimum" : 2, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maxItems + maximum", "schema" : { "maxItems" : 1, "maximum" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "maxItems + minimum", "schema" : { "maxItems" : 1, "minimum" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "maxItems + multipleOf", "schema" : { "maxItems" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minItems", "schema" : { "exclusiveMaximum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minItems", "schema" : { "exclusiveMinimum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + minItems", "schema" : { "maximum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minItems + minimum", "schema" : { "minItems" : 2, "minimum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "minItems + multipleOf", "schema" : { "minItems" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + unevaluatedItems", "schema" : { "exclusiveMaximum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + unevaluatedItems", "schema" : { "exclusiveMinimum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + unevaluatedItems", "schema" : { "maximum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minimum + unevaluatedItems", "schema" : { "minimum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "multipleOf + unevaluatedItems", "schema" : { "multipleOf" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + uniqueItems", "schema" : { "exclusiveMaximum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + uniqueItems", "schema" : { "exclusiveMinimum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + uniqueItems", "schema" : { "maximum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minimum + uniqueItems", "schema" : { "minimum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "multipleOf + uniqueItems", "schema" : { "multipleOf" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "additionalProperties + contains", "schema" : { "additionalProperties" : false, "contains" : true }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "contains + dependentRequired", "schema" : { "contains" : true, "dependentRequired" : { "x" : [ "y" ] } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "contains + dependentSchemas", "schema" : { "contains" : true, "dependentSchemas" : { "x" : false } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "contains + maxProperties", "schema" : { "contains" : true, "maxProperties" : 1 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "contains + minProperties", "schema" : { "contains" : true, "minProperties" : 1 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "contains + patternProperties", "schema" : { "contains" : true, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "contains + properties", "schema" : { "contains" : true, "properties" : { "x" : false } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "contains + propertyNames", "schema" : { "contains" : true, "propertyNames" : false }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "contains + required", "schema" : { "contains" : true, "required" : [ "x" ] }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "contains + unevaluatedProperties", "schema" : { "contains" : true, "unevaluatedProperties" : false }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + items", "schema" : { "additionalProperties" : false, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + items", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + items", "schema" : { "dependentSchemas" : { "x" : false }, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "items + maxProperties", "schema" : { "items" : [ false ], "maxProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "items + minProperties", "schema" : { "items" : [ false ], "minProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "items + patternProperties", "schema" : { "items" : [ false ], "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "items + properties", "schema" : { "items" : [ false ], "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "items + propertyNames", "schema" : { "items" : [ false ], "propertyNames" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "items + required", "schema" : { "items" : [ false ], "required" : [ "x" ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "items + unevaluatedProperties", "schema" : { "items" : [ false ], "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + maxItems", "schema" : { "additionalProperties" : false, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + maxItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + maxItems", "schema" : { "dependentSchemas" : { "x" : false }, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxItems + maxProperties", "schema" : { "maxItems" : 1, "maxProperties" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "maxItems + minProperties", "schema" : { "maxItems" : 1, "minProperties" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "maxItems + patternProperties", "schema" : { "maxItems" : 1, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "maxItems + properties", "schema" : { "maxItems" : 1, "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "maxItems + propertyNames", "schema" : { "maxItems" : 1, "propertyNames" : false }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "maxItems + required", "schema" : { "maxItems" : 1, "required" : [ "x" ] }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "maxItems + unevaluatedProperties", "schema" : { "maxItems" : 1, "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + minItems", "schema" : { "additionalProperties" : false, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + minItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + minItems", "schema" : { "dependentSchemas" : { "x" : false }, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + minItems", "schema" : { "maxProperties" : 1, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minItems + minProperties", "schema" : { "minItems" : 2, "minProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "minItems + patternProperties", "schema" : { "minItems" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "minItems + properties", "schema" : { "minItems" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "minItems + propertyNames", "schema" : { "minItems" : 2, "propertyNames" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "minItems + required", "schema" : { "minItems" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "minItems + unevaluatedProperties", "schema" : { "minItems" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + unevaluatedItems", "schema" : { "additionalProperties" : false, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + unevaluatedItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + unevaluatedItems", "schema" : { "dependentSchemas" : { "x" : false }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + unevaluatedItems", "schema" : { "maxProperties" : 1, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + unevaluatedItems", "schema" : { "minProperties" : 1, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "patternProperties + unevaluatedItems", "schema" : { "patternProperties" : { "no" : false, "yes" : true }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "properties + unevaluatedItems", "schema" : { "properties" : { "x" : false }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "propertyNames + unevaluatedItems", "schema" : { "propertyNames" : false, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "required + unevaluatedItems", "schema" : { "required" : [ "x" ], "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "unevaluatedItems + unevaluatedProperties", "schema" : { "unevaluatedItems" : false, "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + uniqueItems", "schema" : { "additionalProperties" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + uniqueItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + uniqueItems", "schema" : { "dependentSchemas" : { "x" : false }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + uniqueItems", "schema" : { "maxProperties" : 1, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + uniqueItems", "schema" : { "minProperties" : 1, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "patternProperties + uniqueItems", "schema" : { "patternProperties" : { "no" : false, "yes" : true }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "properties + uniqueItems", "schema" : { "properties" : { "x" : false }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "propertyNames + uniqueItems", "schema" : { "propertyNames" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "required + uniqueItems", "schema" : { "required" : [ "x" ], "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "unevaluatedProperties + uniqueItems", "schema" : { "unevaluatedProperties" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "contains + maxLength", "schema" : { "contains" : true, "maxLength" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "contains + minLength", "schema" : { "contains" : true, "minLength" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "contains + pattern", "schema" : { "contains" : true, "pattern" : "hi" }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "items + maxLength", "schema" : { "items" : [ false ], "maxLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "items + minLength", "schema" : { "items" : [ false ], "minLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "items + pattern", "schema" : { "items" : [ false ], "pattern" : "hi" }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxItems + maxLength", "schema" : { "maxItems" : 1, "maxLength" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maxItems + minLength", "schema" : { "maxItems" : 1, "minLength" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maxItems + pattern", "schema" : { "maxItems" : 1, "pattern" : "hi" }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minItems", "schema" : { "maxLength" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minItems + minLength", "schema" : { "minItems" : 2, "minLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minItems + pattern", "schema" : { "minItems" : 2, "pattern" : "hi" }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + unevaluatedItems", "schema" : { "maxLength" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + unevaluatedItems", "schema" : { "minLength" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + unevaluatedItems", "schema" : { "pattern" : "hi", "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + uniqueItems", "schema" : { "maxLength" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + uniqueItems", "schema" : { "minLength" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + uniqueItems", "schema" : { "pattern" : "hi", "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "additionalProperties + exclusiveMaximum", "schema" : { "additionalProperties" : false, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + exclusiveMaximum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + exclusiveMaximum", "schema" : { "dependentSchemas" : { "x" : false }, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxProperties", "schema" : { "exclusiveMaximum" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minProperties", "schema" : { "exclusiveMaximum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + patternProperties", "schema" : { "exclusiveMaximum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + properties", "schema" : { "exclusiveMaximum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + propertyNames", "schema" : { "exclusiveMaximum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + required", "schema" : { "exclusiveMaximum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + unevaluatedProperties", "schema" : { "exclusiveMaximum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + exclusiveMinimum", "schema" : { "additionalProperties" : false, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + exclusiveMinimum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + exclusiveMinimum", "schema" : { "dependentSchemas" : { "x" : false }, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxProperties", "schema" : { "exclusiveMinimum" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minProperties", "schema" : { "exclusiveMinimum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + patternProperties", "schema" : { "exclusiveMinimum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + properties", "schema" : { "exclusiveMinimum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + propertyNames", "schema" : { "exclusiveMinimum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + required", "schema" : { "exclusiveMinimum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + unevaluatedProperties", "schema" : { "exclusiveMinimum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + maximum", "schema" : { "additionalProperties" : false, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + maximum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + maximum", "schema" : { "dependentSchemas" : { "x" : false }, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + maximum", "schema" : { "maxProperties" : 1, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "maximum + minProperties", "schema" : { "maximum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "maximum + patternProperties", "schema" : { "maximum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "maximum + properties", "schema" : { "maximum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "maximum + propertyNames", "schema" : { "maximum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "maximum + required", "schema" : { "maximum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "maximum + unevaluatedProperties", "schema" : { "maximum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + minimum", "schema" : { "additionalProperties" : false, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + minimum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + minimum", "schema" : { "dependentSchemas" : { "x" : false }, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + minimum", "schema" : { "maxProperties" : 1, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + minimum", "schema" : { "minProperties" : 1, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "minimum + patternProperties", "schema" : { "minimum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "minimum + properties", "schema" : { "minimum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "minimum + propertyNames", "schema" : { "minimum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "minimum + required", "schema" : { "minimum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "minimum + unevaluatedProperties", "schema" : { "minimum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + multipleOf", "schema" : { "additionalProperties" : false, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + multipleOf", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + multipleOf", "schema" : { "dependentSchemas" : { "x" : false }, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + multipleOf", "schema" : { "maxProperties" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + multipleOf", "schema" : { "minProperties" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "multipleOf + patternProperties", "schema" : { "multipleOf" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "multipleOf + properties", "schema" : { "multipleOf" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "multipleOf + propertyNames", "schema" : { "multipleOf" : 2, "propertyNames" : false }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "multipleOf + required", "schema" : { "multipleOf" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "multipleOf + unevaluatedProperties", "schema" : { "multipleOf" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxLength", "schema" : { "exclusiveMaximum" : 2, "maxLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minLength", "schema" : { "exclusiveMaximum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + pattern", "schema" : { "exclusiveMaximum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxLength", "schema" : { "exclusiveMinimum" : 2, "maxLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minLength", "schema" : { "exclusiveMinimum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + pattern", "schema" : { "exclusiveMinimum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + maximum", "schema" : { "maxLength" : 2, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maximum + minLength", "schema" : { "maximum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maximum + pattern", "schema" : { "maximum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minimum", "schema" : { "maxLength" : 2, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + minimum", "schema" : { "minLength" : 2, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minimum + pattern", "schema" : { "minimum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + multipleOf", "schema" : { "maxLength" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + multipleOf", "schema" : { "minLength" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "multipleOf + pattern", "schema" : { "multipleOf" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "additionalProperties + maxLength", "schema" : { "additionalProperties" : false, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "additionalProperties + minLength", "schema" : { "additionalProperties" : false, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "additionalProperties + pattern", "schema" : { "additionalProperties" : false, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "dependentRequired + maxLength", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "dependentRequired + minLength", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "dependentRequired + pattern", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "dependentSchemas + maxLength", "schema" : { "dependentSchemas" : { "x" : false }, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "dependentSchemas + minLength", "schema" : { "dependentSchemas" : { "x" : false }, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "dependentSchemas + pattern", "schema" : { "dependentSchemas" : { "x" : false }, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + maxProperties", "schema" : { "maxLength" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maxProperties + minLength", "schema" : { "maxProperties" : 1, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maxProperties + pattern", "schema" : { "maxProperties" : 1, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minProperties", "schema" : { "maxLength" : 2, "minProperties" : 1 }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + minProperties", "schema" : { "minLength" : 2, "minProperties" : 1 }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minProperties + pattern", "schema" : { "minProperties" : 1, "pattern" : "hi" }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + patternProperties", "schema" : { "maxLength" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + patternProperties", "schema" : { "minLength" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + patternProperties", "schema" : { "pattern" : "hi", "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + properties", "schema" : { "maxLength" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + properties", "schema" : { "minLength" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + properties", "schema" : { "pattern" : "hi", "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + propertyNames", "schema" : { "maxLength" : 2, "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + propertyNames", "schema" : { "minLength" : 2, "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + propertyNames", "schema" : { "pattern" : "hi", "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + required", "schema" : { "maxLength" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + required", "schema" : { "minLength" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + required", "schema" : { "pattern" : "hi", "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + unevaluatedProperties", "schema" : { "maxLength" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + unevaluatedProperties", "schema" : { "minLength" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + unevaluatedProperties", "schema" : { "pattern" : "hi", "unevaluatedProperties" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/README000640 000766 000024 00000000121 15001302761 023410 0ustar00etherstaff000000 000000 Tests go here until/unless they are accepted by the main JSON-Schema-Test-Suite. JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/short-circuit.json000640 000766 000024 00000012734 15001302761 026237 0ustar00etherstaff000000 000000 [ { "description": "items (schema form) does not improperly short-circuit", "schema": { "items": { "multipleOf": 2 } }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "items (array form) does not improperly short-circuit", "schema": { "items": [ { "multipleOf": 2 }, { "multipleOf": 2 } ] }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "additionalItems does not improperly short-circuit", "schema": { "items": [ true ], "additionalItems": { "multipleOf": 2 } }, "tests": [ { "description": "single additional item is valid", "data": [ 1, 2 ], "valid": true }, { "description": "second additional item is invalid", "data": [ 1, 2, 1 ], "valid": false } ] }, { "description": "unevaluatedItems does not improperly short-circuit", "schema": { "unevaluatedItems": { "multipleOf": 2 } }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "properties does not improperly short-circuit", "schema": { "properties": { "alpha": { "multipleOf": 2 }, "beta": { "multipleOf": 2 } } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "properties does not improperly short-circuit (boolean schemas)", "schema": { "properties": { "alpha": true, "beta": false } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "patternProperties does not improperly short-circuit", "schema": { "patternProperties": { "^a": { "multipleOf": 2 }, "^b": { "multipleOf": 2 } } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "patternProperties does not improperly short-circuit (boolean schemas)", "schema": { "patternProperties": { "^a": true, "^b": false } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "additionalProperties does not improperly short-circuit", "schema": { "additionalProperties": { "multipleOf": 2 } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "unevaluatedProperties does not improperly short-circuit", "schema": { "unevaluatedProperties": { "multipleOf": 2 } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/optional/000770 000766 000024 00000000000 15001302761 024364 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/integers.json000640 000766 000024 00000013141 15001302761 025251 0ustar00etherstaff000000 000000 [ { "description": "type checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "type": "integer" }, "tests": [ { "description": "beyond int64 lower boundary", "data": -9223372036854775809, "valid": true }, { "description": "int64 lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "beyond int32 lower boundary", "data": -2147483649, "valid": true }, { "description": "int32 lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "int32 upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond int32 upper boundary", "data": 2147483648, "valid": true }, { "description": "upper int64 boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond int64 upper boundary", "data": 9223372036854775808, "valid": true } ] }, { "description": "int32 range checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "minimum": -2147483648, "maximum": 2147483647 }, "tests": [ { "description": "beyond lower boundary", "data": -2147483649, "valid": false }, { "description": "lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond upper boundary", "data": 2147483648, "valid": false } ] }, { "description": "int64 range checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "minimum": -9223372036854775808, "maximum": 9223372036854775807 }, "tests": [ { "description": "beyond lower boundary", "data": -9223372036854775809, "valid": false }, { "description": "lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "upper boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond upper boundary", "data": 9223372036854775808, "valid": false } ] }, { "description": "equality checks", "schema": { "$comment": "on some architectures, some of these values may json-decode to strings", "enum": [ -9223372036854775809, -9223372036854775808, -2147483649, -2147483648, 0, 2147483647, 2147483648, 9223372036854775807, 9223372036854775808 ] }, "tests": [ { "description": "beyond int64 lower boundary", "data": -9223372036854775809, "valid": true }, { "description": "int64 lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "beyond int32 lower boundary", "data": -2147483649, "valid": true }, { "description": "int32 lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "one", "data": 1, "valid": false }, { "description": "int32 upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond int32 upper boundary", "data": 2147483648, "valid": true }, { "description": "int64 upper boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond int64 upper boundary", "data": 9223372036854775808, "valid": true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/format-ipv4.json000640 000766 000024 00000002150 15001302761 025577 0ustar00etherstaff000000 000000 [ { "description": "validation of ipv4 strings", "schema": { "format": "ipv4" }, "tests": [ { "description": "leading zeroes should be rejected, as they are treated as octal values", "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/", "data": "087.10.0.1", "valid": false }, { "description": "leading zeroes should be rejected, even if not a valid octal value", "data": "099.10.0.1", "valid": false }, { "description": "value without leading zero is valid", "data": "87.10.0.1", "valid": true }, { "description": "non-ascii digits should be rejected", "data": "1২7.0.0.1", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/format-relative-json-pointer.json000640 000766 000024 00000002653 15001302761 031165 0ustar00etherstaff000000 000000 [ { "description": "validation of relative-json-pointer strings", "schema": {"format": "relative-json-pointer"}, "comment": "see https://json-schema.org/draft/2019-09/relative-json-pointer.html#rfc.section.3", "tests": [ { "description": "non-negative-integer with empty json-pointer", "data": "0", "valid": true }, { "description": "non-negative-integer with one-char json-pointer", "data": "0/", "valid": true }, { "description": "non-negative-integer with longer json-pointer", "data": "0/a", "valid": true }, { "description": "non-negative-integer #", "data": "0#", "valid": true }, { "description": "## is not a valid json-pointer", "data": "0##", "valid": false }, { "description": "zero cannot be followed by other digits, plus json-pointer", "data": "01/a", "valid": false }, { "description": "zero cannot be followed by other digits, plus octothorpe", "data": "01#", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/faux-buggy-schemas.json000640 000766 000024 00000002446 15001302761 027136 0ustar00etherstaff000000 000000 [ { "description": "identifiers in non-schemas that look like schemas", "schema": { "$id": "http://localhost:1234/faux.json", "anyOf": [ { "type": "integer", "examples": [ { "$id": "faux.json#/foo/bar" }, { "$anchor": "my$foo" } ] }, { "type": "integer", "default": { "$anchor": "not$an$anchor" } }, { "$anchor": "my_const", "const": { "$anchor": "my_const" } }, { "$id": "enum.json", "$anchor": "my_enum", "enum": [ { "$id": "enum.json" }, { "$anchor": "my_enum" } ] } ] }, "tests": [ { "description": "match the type checks", "data": 1, "valid": true }, { "description": "match none of the options", "data": "a string", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/id.json000640 000766 000024 00000003462 15001302761 024032 0ustar00etherstaff000000 000000 [ { "description": "non-schema object containing an $id property", "schema": { "$defs": { "const_not_id": { "const": { "$id": "not_a_real_id" } } }, "if": { "const": "skip not_a_real_id" }, "then": true, "else" : { "$ref": "#/$defs/const_not_id" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_id", "valid": true }, { "description": "const at const_not_id does not match", "data": 1, "valid": false } ] }, { "description": "'' not permitted as $id", "schema": { "$id": "", "type": "string" }, "tests": [ { "description": "schema would pass without $id", "data": "foo", "valid": false }, { "description": "schema would fail even without $id", "data": 1, "valid": false } ] }, { "description": "'#' not permitted as $id", "schema": { "$id": "#", "type": "string" }, "tests": [ { "description": "schema would pass without $id", "data": "foo", "valid": false }, { "description": "schema would fail even without $id", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/format-duration.json000640 000766 000024 00000003540 15001302761 026546 0ustar00etherstaff000000 000000 [ { "description": "validation of duration strings", "schema": {"format": "duration"}, "comment": "see https://en.wikipedia.org/wiki/ISO_8601#Durations for examples and clarifications", "tests": [ { "description": "years and months can appear without days", "data": "P1Y2M", "valid": true }, { "description": "months and days can appear without years", "data": "P1M2D", "valid": true }, { "description": "hours and minutes can appear without seconds", "data": "PT1H2M", "valid": true }, { "description": "minutes and seconds can appear without hour", "data": "PT1M2S", "valid": true }, { "description": "elements may be omitted if their value is zero", "data": "PT1H1S", "valid": true }, { "description": "half a year duration, with full stop decimal point", "comment": "ISO 8601 revision in year 2000 added decimals", "data": "P0.5Y", "valid": true }, { "description": "half a year duration, with comma decimal point", "data": "P0,5Y", "valid": true }, { "description": "only one unit can have a non-integer quantity", "data": "P0.5Y2.1M", "valid": false }, { "description": "only the smallest unit can have a non-integer quantity", "data": "P0.5Y2M", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/recursive-dynamic.json000640 000766 000024 00000004323 15001302761 027064 0ustar00etherstaff000000 000000 [ { "description": "dynamic $recursiveRef destination (not predictable at schema compile time)", "schema": { "$id": "main.json", "$defs": { "inner": { "$id": "inner.json", "$recursiveAnchor": true, "title": "inner", "additionalProperties": { "$recursiveRef": "#" } } }, "if": { "propertyNames": { "pattern": "^[a-m]" } }, "then": { "title": "any type of node", "$id": "anyLeafNode.json", "$recursiveAnchor": true, "$ref": "main.json#/$defs/inner" }, "else": { "title": "integer node", "$id": "integerNode.json", "$recursiveAnchor": true, "type": [ "object", "integer" ], "$ref": "main.json#/$defs/inner" } }, "tests": [ { "description": "numeric node", "data": { "alpha": 1.1 }, "valid": true }, { "description": "integer node", "data": { "november": 1.1 }, "valid": false } ] }, { "description": "$recursiveRef points to a boolean schema", "schema": { "$schema": "https://json-schema.org/draft/2019-09/schema", "$defs": { "true": true, "false": false }, "properties": { "true": { "$recursiveRef": "#/$defs/true" }, "false": { "$recursiveRef": "#/$defs/false" } } }, "tests": [ { "description": "follow $recursiveRef to a true schema", "data": { "true": 1 }, "valid": true }, { "description": "follow $recursiveRef to a false schema", "data": { "false": 1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/ref.json000640 000766 000024 00000012025 15001302761 024205 0ustar00etherstaff000000 000000 [ { "description": "order of evaluation: $id and $ref", "schema": { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "/ref1/base.json", "$ref": "int.json", "$defs": { "bigint": { "$comment": "canonical uri: /ref1/int.json", "$id": "int.json", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /int.json", "$id": "/int.json", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "order of evaluation: $id and $anchor and $ref", "schema": { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "/ref2/base.json", "$ref": "#bigint", "$defs": { "bigint": { "$comment": "canonical uri: /ref2/base.json#/$defs/bigint; another valid uri for this location: /ref2/base.json#bigint", "$anchor": "bigint", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /ref2; other valid uris for this location: /ref2/#bigint, /refs2/base.json#/$defs/smallint", "$id": "/ref2/", "$anchor": "bigint", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "naive replacement of $ref with its destination is not correct", "schema": { "$defs": { "a_string": { "type": "string" } }, "enum": [ { "$ref": "#/$defs/a_string" } ] }, "tests": [ { "description": "do not evaluate the $ref inside the enum", "data": "this is a string", "valid": false }, { "description": "match the enum exactly", "data": { "$ref": "#/$defs/a_string" }, "valid": true } ] }, { "description": "invalid $ref: invalid anchor fragment", "schema": { "$defs": { "foo": { "$ref": "https://foo.com/bar.json#-not-an-anchor" } } }, "tests": [ { "description": "an invalid anchor fragment", "data": 1, "valid": false } ] }, { "description": "invalid $ref: invalid json-pointer fragment", "schema": { "$defs": { "foo": { "$ref": "https://foo.com/bar.json#/~2/not/json/pointer" } } }, "tests": [ { "description": "an invalid json-pointer fragment", "data": 1, "valid": false } ] }, { "description": "base URI change - change folder in subschema with path from root", "comment": "based on test suite draft7/refRemote.json 'base URI change - change folder in subschema'", "schema": { "$id": "http://localhost:1234/scope_change_defs.json", "type" : "object", "properties": { "list": { "$ref": "#/$defs/baz/$defs/bar" } }, "$defs": { "folderInteger": { "$id": "baseUriChangeFolderInSubschema/folderInteger.json", "type": "integer" }, "baz": { "$id": "baseUriChangeFolderInSubschema/", "$defs": { "bar": { "type": "array", "items": { "$ref": "folderInteger.json" } } } } } }, "tests": [ { "description": "number is valid", "data": {"list": [1]}, "valid": true }, { "description": "string is invalid", "data": {"list": ["a"]}, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/annotation-collection.json000640 000766 000024 00000006212 15001302761 027735 0ustar00etherstaff000000 000000 [ { "description": "annotations come from an allOf, unevaluated is a sibling", "schema": { "allOf": [ { "properties": { "foo": true } } ], "unevaluatedProperties": false }, "tests": [ { "description": "properties evaluated from allOf", "data": { "foo": 1 }, "valid": true }, { "description": "extra property", "data": { "bar": 1 }, "valid": false } ] }, { "description": "annotations from adjacent keywords, unevaluated is a sibling", "schema": { "properties": { "foo": true }, "unevaluatedProperties": false }, "tests": [ { "description": "properties evaluated from additionalProperties", "data": { "foo": 1 }, "valid": true }, { "description": "extra property", "data": { "bar": 1 }, "valid": false } ] }, { "description": "annotations come from an allOf, unevaluated is in an allOf", "schema": { "allOf": [ { "properties": { "foo": true } } ], "anyOf": [ { "unevaluatedProperties": false } ] }, "tests": [ { "description": "properties evaluated from allOf, but unevaluated is buried", "data": { "foo": 1 }, "valid": false }, { "description": "no properties", "data": {}, "valid": true } ] }, { "description": "annotations from adjacent keywords, unevaluated is in an allOf", "schema": { "anyOf": [ { "properties": { "foo": true } } ], "allOf": [ { "unevaluatedProperties": false } ] }, "tests": [ { "description": "properties evaluated from additionalProperties, but unevaluated is buried", "data": { "foo": 1 }, "valid": false }, { "description": "no properties", "data": {}, "valid": true } ] }, { "description": "collect annotations inside a 'not', even if collection is disabled", "schema": { "not": { "$comment": "this subschema must still produce annotations internally, even though the 'not' will ultimately discard them", "anyOf": [ true, { "properties": { "foo": true } } ], "unevaluatedProperties": false } }, "tests": [ { "description": "unevaluated property", "data": { "bar": 1 }, "valid": true }, { "description": "annotations are still collected inside a 'not'", "data": { "foo": 1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/optional/format-date-time.json000640 000766 000024 00000001646 15001302761 030424 0ustar00etherstaff000000 000000 [ { "description": "leap seconds", "schema": { "format": "date-time" }, "tests": [ { "description": "a valid date-time with a leap second, UTC", "data": "1998-12-31T23:59:60Z", "valid": true }, { "description": "a valid date-time with a leap second, with minus offset", "data": "1998-12-31T15:59:60.123-08:00", "valid": true }, { "description": "an invalid date-time with a leap second, UTC", "data": "1998-12-30T23:59:60Z", "valid": false }, { "description": "an invalid date-time with a leap second, with minus offset", "data": "1998-12-30T15:59:60.123-08:00", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/optional/format-time.json000640 000766 000024 00000004770 15001302761 027512 0ustar00etherstaff000000 000000 [ { "description": "validation of time strings", "schema": { "format": "time" }, "comment": "see https://tools.ietf.org/html/rfc3339#section-5.6 and https://tools.ietf.org/html/rfc3339#appendix-D", "tests": [ { "description": "valid leap second, Zulu", "data": "23:59:60Z", "valid": true }, { "description": "invalid leap second, Zulu (wrong hour)", "data": "22:59:60Z", "valid": false }, { "description": "invalid leap second, Zulu (wrong minute)", "data": "23:58:60Z", "valid": false }, { "description": "valid leap second, positive time-offset", "data": "01:29:60+01:30", "valid": true }, { "description": "valid leap second, large positive time-offset", "data": "23:29:60+23:30", "valid": true }, { "description": "invalid leap second, positive time-offset (wrong hour)", "data": "23:59:60+01:00", "valid": false }, { "description": "invalid leap second, positive time-offset (wrong minute)", "data": "23:59:60+00:30", "valid": false }, { "description": "valid leap second, negative time-offset", "data": "22:29:60-01:30", "valid": true }, { "description": "valid leap second, large negative time-offset", "data": "00:29:60-23:30", "valid": true }, { "description": "invalid leap second, negative time-offset (wrong hour)", "data": "23:59:60-01:00", "valid": false }, { "description": "invalid leap second, negative time-offset (wrong minute)", "data": "23:59:60-00:30", "valid": false }, { "description": "no time offset", "data": "12:00:00", "valid": false }, { "description": "non-ascii digits should be rejected", "data": "1২:00:00Z", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2019-09/optional/format-date.json000640 000766 000024 00000000524 15001302761 027462 0ustar00etherstaff000000 000000 [ { "description": "non-ascii digits should be rejected", "schema": { "format": "date" }, "tests": [ { "description": "BENGALI DIGIT TWO is not valid in dates", "data": "২001-01-01", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/lib/Acceptance.pm000640 000766 000024 00000012150 15001302761 020557 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Safe::Isa; use Feature::Compat::Try; use Path::Tiny; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings' => ':fail_on_warning'; # hooks into done_testing unless overridden use Test::JSON::Schema::Acceptance 1.021; use Test::Memory::Cycle; use Test::File::ShareDir -share => { -dist => { 'JSON-Schema-Modern' => 'share' } }; use JSON::Schema::Modern; # supports options: # - acceptance: options passed to Test::JSON::Schema::Acceptance constructor # - evaluator: options passed to JSON::Schema::Modern constructor # - tests: options passed to Test::JSON::Schema::Acceptance::acceptance method # - output_file: filename to print results to (default: none) sub acceptance_tests (%options) { local $Test::Builder::Level = $Test::Builder::Level + 1; my $note = $ENV{AUTHOR_TESTING} || $ENV{AUTOMATED_TESTING} ? \&diag : \¬e; $note->(''); foreach my $env (qw(AUTHOR_TESTING AUTOMATED_TESTING EXTENDED_TESTING NO_TODO TEST_DIR NO_SHORT_CIRCUIT)) { $note->($env.': '.($ENV{$env} // '')); } $note->(''); my $accepter = Test::JSON::Schema::Acceptance->new( include_optional => 1, verbose => $ENV{AUTOMATED_TESTING}, test_schemas => $ENV{AUTHOR_TESTING}, $options{acceptance}->%*, $ENV{TEST_DIR} ? (test_dir => $ENV{TEST_DIR}) : $ENV{TEST_PREFIXDIR} ? (test_dir => path($ENV{TEST_PREFIXDIR}, 'tests', $options{acceptance}{specification})) : (), supported_specifications => [ qw(draft4 draft6 draft7 draft2019-09 draft2020-12) ], ); $accepter = $accepter->new(%$accepter, test_dir => $accepter->test_dir->child($options{acceptance}{test_subdir})) if not $ENV{TEST_DIR} and $options{acceptance}{test_subdir}; $accepter->json_decoder->allow_bignum if Test::JSON::Schema::Acceptance->VERSION < '1.022'; my $js = JSON::Schema::Modern->new($options{evaluator}->%*); my $js_short_circuit = $ENV{NO_SHORT_CIRCUIT} || JSON::Schema::Modern->new($options{evaluator}->%*, short_circuit => 1); my $add_resource = sub ($uri, $schema, @options) { return if $uri =~ m{/draft-next/}; try { # suppress warnings from parsing remotes/* intended for draft <= 7 with 'definitions' local $SIG{__WARN__} = sub { warn @_ if $_[0] !~ /^no-longer-supported "definitions" keyword present/; } if $options{acceptance}{specification} !~ /^draft[467]$/ and Test::JSON::Schema::Acceptance->VERSION < '1.028'; my $doc = my $document = JSON::Schema::Modern::Document->new( schema => $schema, evaluator => $js, @options, ); $js->add_document($uri => $doc); $js_short_circuit->add_document($uri => $doc) if not $ENV{NO_SHORT_CIRCUIT}; } catch ($e) { die $e->$_isa('JSON::Schema::Modern::Result') ? $e->dump : $e; } }; $accepter->acceptance( validate_data => sub ($schema, $instance_data) { my $result = $js->evaluate($instance_data, $schema); my $result_short = $ENV{NO_SHORT_CIRCUIT} || $js_short_circuit->evaluate($instance_data, $schema); die 'result is not a JSON::Schema::Modern::Result object' if not $result->isa('JSON::Schema::Modern::Result'); note 'result: ', $result->dump; if (not $ENV{NO_SHORT_CIRCUIT}) { die 'short-circuited result is not a JSON::Schema::Modern::Result object' if not $result_short->isa('JSON::Schema::Modern::Result'); note 'short-circuited result: ', $result_short->dump; die 'results inconsistent between short_circuit = false and true' if ($result->valid xor $result_short->valid); } my $in_todo; # if any errors contain an exception, generate a warning so we can be sure # to count that as a failure (an exception would be caught and perhaps TODO'd). # (This might change if tests are added that are expected to produce exceptions.) foreach my $r ($result, ($ENV{NO_SHORT_CIRCUIT} ? () : $result_short)) { diag 'evaluation generated an exception: '.$_->dump foreach grep +($_->{error} =~ /^EXCEPTION/ && $_->{error} !~ /(max|min)imum value is not a number$/) # optional/bignum.json && !($in_todo //= grep $_->{todo}, Test2::API::test2_stack->top->{_pre_filters}->@*), $r->errors; } $result->valid; }, add_resource => $add_resource, @ARGV ? (tests => { file => \@ARGV }) : (), ($options{test} // {})->%*, ); memory_cycle_ok($js, 'no leaks in the main evaluator object'); memory_cycle_ok($js_short_circuit, 'no leaks in the short-circuiting evaluator object') if not $ENV{NO_SHORT_CIRCUIT}; path('t/results/'.$options{output_file})->spew_utf8($accepter->results_text) if $ENV{AUTHOR_TESTING}; } 1; JSON-Schema-Modern-0.609/t/lib/Helper.pm000640 000766 000024 00000004052 15001302761 017752 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : # no package, so things defined here appear in the namespace of the parent. use strictures 2; use 5.020; use stable 0.031 'postderef'; use experimental 'signatures'; no autovivification warn => qw(fetch store exists delete); use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; use open ':std', ':encoding(UTF-8)'; # force stdin, stdout, stderr into utf8 use Test::More 0.96; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::Deep; # import symbols: ignore, re etc use Test2::API 'context_do'; use JSON::Schema::Modern; use Test::File::ShareDir -share => { -dist => { 'JSON-Schema-Modern' => 'share' } }; use JSON::PP (); use constant { true => JSON::PP::true, false => JSON::PP::false }; my $encoder = JSON::Schema::Modern::_JSON_BACKEND()->new ->allow_nonref(1) ->utf8(0) ->allow_bignum(1) ->allow_blessed(1) ->convert_blessed(1) ->canonical(1) ->pretty(1) ->indent_length(2); # like sprintf, but all list items are JSON-encoded. assumes placeholders are %s! sub json_sprintf { sprintf(shift, map +(ref($_) =~ /^Math::Big(?:Int|Float)$/ ? ref($_).'->new(\''.$_.'\')' : $encoder->indent(0)->encode($_)), @_); } # deep comparison, with Test::Deep syntax sugar sub cmp_result ($got, $expected, $test_name) { context_do { my $ctx = shift; my ($got, $expected, $test_name) = @_; my ($equal, $stack) = Test::Deep::cmp_details($got, $expected); if ($equal) { $ctx->pass($test_name); } else { $ctx->fail($test_name); my $method = # be less noisy for expected failures (grep $_->{todo}, Test2::API::test2_stack->top->{_pre_filters}->@*) ? 'note' : $ENV{AUTHOR_TESTING} || $ENV{AUTOMATED_TESTING} ? 'diag' : 'note'; $ctx->$method(Test::Deep::deep_diag($stack)); $ctx->$method("got result:\n".$encoder->encode($got)); } return $equal; } $got, $expected, $test_name; } 1; JSON-Schema-Modern-0.609/t/lib/MyVocabulary/000770 000766 000024 00000000000 15001302761 020612 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/lib/MyVocabulary/BadVocabularySub2.pm000640 000766 000024 00000000350 15001302761 024417 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : package MyVocabulary::BadVocabularySub2; use Moo; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary { 'https://some/uri#/invalid/uri' => 'draft2020-12', } sub keywords {} 1; JSON-Schema-Modern-0.609/t/lib/MyVocabulary/StringComparison.pm000640 000766 000024 00000001776 15001302761 024463 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : package MyVocabulary::StringComparison; use Moo; use strictures 2; use stable 0.031 'postderef'; use experimental 'signatures'; use if "$]" >= 5.022, experimental => 're_strict'; no if "$]" >= 5.031009, feature => 'indirect'; no if "$]" >= 5.033001, feature => 'multidimensional'; no if "$]" >= 5.033006, feature => 'bareword_filehandles'; with 'JSON::Schema::Modern::Vocabulary'; use JSON::Schema::Modern::Utilities qw(assert_keyword_type is_type E); sub vocabulary { 'https://vocabulary/string/comparison' => 'draft2020-12', } sub keywords { 'stringLessThan' } sub _traverse_keyword_stringLessThan ($self, $schema, $state) { return if not assert_keyword_type($state, $schema, 'string'); return 1; } sub _eval_keyword_stringLessThan ($self, $data, $schema, $state) { return 1 if not is_type('string', $data); return 1 if ($data cmp $schema->{stringLessThan}) == -1; return E($state, 'value is not stringwise less than %s', $schema->{stringLessThan}); } 1; JSON-Schema-Modern-0.609/t/lib/MyVocabulary/BadEvaluationOrder.pm000640 000766 000024 00000000474 15001302761 024666 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : package MyVocabulary::BadEvaluationOrder; use Moo; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary { 'https://vocabulary/with/bad/evaluation/order' => 'draft2020-12', } sub keywords {} sub evaluation_order { 1 } # conflicts with Validation vocabulary 1; JSON-Schema-Modern-0.609/t/lib/MyVocabulary/BadVocabularySub3.pm000640 000766 000024 00000000331 15001302761 024417 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : package MyVocabulary::BadVocabularySub3; use Moo; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary { 'https://some/uri' => 'wrongdraft', } sub keywords {} 1; JSON-Schema-Modern-0.609/t/lib/MyVocabulary/MissingRole.pm000640 000766 000024 00000000222 15001302761 023376 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : package MyVocabulary::MissingRole; sub vocabulary { 'https://some/uri' => 'draft2020-12', } 1; JSON-Schema-Modern-0.609/t/lib/MyVocabulary/BadVocabularySub1.pm000640 000766 000024 00000000307 15001302761 024420 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : package MyVocabulary::BadVocabularySub1; use Moo; with 'JSON::Schema::Modern::Vocabulary'; sub vocabulary { 'https://wrong_data' } sub keywords {} 1; JSON-Schema-Modern-0.609/t/lib/MyVocabulary/MissingSub.pm000640 000766 000024 00000000247 15001302761 023235 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : package MyVocabulary::MissingSub; use Moo; with 'JSON::Schema::Modern::Vocabulary'; # missing vocabulary, keywords 1; JSON-Schema-Modern-0.609/t/results/draft2020-12-additional-tests.txt000640 000766 000024 00000003613 15001302761 024765 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 specification version: draft2020-12 using custom test directory: t/additional-tests-draft2020-12 optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- anchor.json 2 0 0 annotation-collection.json 10 0 0 badRef.json 3 0 0 dynamicRef.json 7 0 0 faux-buggy-schemas.json 2 0 0 format-duration.json 9 0 0 format-ipv4.json 4 0 0 format-ipv6.json 17 0 0 format-relative-json-pointer.json 7 0 0 formats.json 4 0 0 id.json 6 0 0 integers.json 29 0 0 keyword-independence.json 884 0 0 loose-types-const-enum.json 8 0 0 recursive-dynamic.json 2 0 0 ref-and-id.json 6 0 0 ref.json 10 0 0 short-circuit.json 20 0 0 unknownKeyword.json 4 0 0 vocabulary.json 11 0 0 optional/format-date-time.json 4 0 0 optional/format-date.json 1 0 0 optional/format-time.json 13 0 0 --------------------------------------------------------------- TOTAL 1063 0 0 JSON-Schema-Modern-0.609/t/results/draft7-acceptance.txt000640 000766 000024 00000006704 15001302761 023152 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft7 optional tests included: yes skipping directory: optional/format filename pass todo-fail fail --------------------------------------------------------------- additionalItems.json 20 0 0 additionalProperties.json 16 0 0 allOf.json 30 0 0 anyOf.json 18 0 0 boolean_schema.json 18 0 0 const.json 50 0 0 contains.json 21 0 0 default.json 7 0 0 definitions.json 2 0 0 dependencies.json 36 0 0 enum.json 45 0 0 exclusiveMaximum.json 4 0 0 exclusiveMinimum.json 4 0 0 format.json 102 0 0 if-then-else.json 26 0 0 infinite-loop-detection.json 2 0 0 items.json 28 0 0 maxItems.json 6 0 0 maxLength.json 7 0 0 maxProperties.json 10 0 0 maximum.json 8 0 0 minItems.json 6 0 0 minLength.json 7 0 0 minProperties.json 8 0 0 minimum.json 11 0 0 multipleOf.json 10 0 0 not.json 38 0 0 oneOf.json 27 0 0 pattern.json 9 0 0 patternProperties.json 23 0 0 properties.json 28 0 0 propertyNames.json 13 0 0 ref.json 78 0 0 refRemote.json 23 0 0 required.json 16 0 0 type.json 80 0 0 uniqueItems.json 69 0 0 optional/bignum.json 9 0 0 optional/content.json 10 0 0 optional/cross-draft.json 2 0 0 optional/ecmascript-regex.json 60 14 0 optional/float-overflow.json 1 0 0 optional/id.json 7 0 0 optional/non-bmp-regex.json 12 0 0 optional/unknownKeyword.json 3 0 0 --------------------------------------------------------------- TOTAL 1010 14 0 JSON-Schema-Modern-0.609/t/results/draft4-acceptance-format.txt000640 000766 000024 00000002132 15001302761 024424 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft4 using custom test directory: /optional/format optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- date-time.json 25 0 0 email.json 15 0 0 hostname.json 24 0 0 ipv4.json 16 0 0 ipv6.json 40 0 0 unknown.json 7 0 0 uri.json 25 1 0 --------------------------------------------------------------- TOTAL 152 1 0 JSON-Schema-Modern-0.609/t/results/draft6-acceptance.txt000640 000766 000024 00000006404 15001302761 023146 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft6 optional tests included: yes skipping directory: optional/format filename pass todo-fail fail --------------------------------------------------------------- additionalItems.json 20 0 0 additionalProperties.json 16 0 0 allOf.json 30 0 0 anyOf.json 18 0 0 boolean_schema.json 18 0 0 const.json 50 0 0 contains.json 19 0 0 default.json 7 0 0 definitions.json 2 0 0 dependencies.json 36 0 0 enum.json 45 0 0 exclusiveMaximum.json 4 0 0 exclusiveMinimum.json 4 0 0 format.json 54 0 0 infinite-loop-detection.json 2 0 0 items.json 28 0 0 maxItems.json 6 0 0 maxLength.json 7 0 0 maxProperties.json 10 0 0 maximum.json 8 0 0 minItems.json 6 0 0 minLength.json 7 0 0 minProperties.json 8 0 0 minimum.json 11 0 0 multipleOf.json 10 0 0 not.json 38 0 0 oneOf.json 27 0 0 pattern.json 9 0 0 patternProperties.json 23 0 0 properties.json 28 0 0 propertyNames.json 13 0 0 ref.json 70 0 0 refRemote.json 23 0 0 required.json 16 0 0 type.json 80 0 0 uniqueItems.json 69 0 0 optional/bignum.json 9 0 0 optional/ecmascript-regex.json 60 14 0 optional/float-overflow.json 1 0 0 optional/id.json 7 0 0 optional/non-bmp-regex.json 12 0 0 optional/unknownKeyword.json 3 0 0 --------------------------------------------------------------- TOTAL 914 14 0 JSON-Schema-Modern-0.609/t/results/draft6-acceptance-format.txt000640 000766 000024 00000002432 15001302761 024431 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft6 using custom test directory: /optional/format optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- date-time.json 25 0 0 email.json 15 0 0 hostname.json 24 0 0 ipv4.json 16 0 0 ipv6.json 40 0 0 json-pointer.json 38 0 0 unknown.json 7 0 0 uri-reference.json 13 0 0 uri-template.json 9 1 0 uri.json 25 1 0 --------------------------------------------------------------- TOTAL 212 2 0 JSON-Schema-Modern-0.609/t/results/draft2019-09-acceptance.txt000640 000766 000024 00000010312 15001302761 023613 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft2019-09 optional tests included: yes skipping directory: optional/format filename pass todo-fail fail --------------------------------------------------------------- additionalItems.json 20 0 0 additionalProperties.json 21 0 0 allOf.json 30 0 0 anchor.json 8 0 0 anyOf.json 18 0 0 boolean_schema.json 18 0 0 const.json 50 0 0 contains.json 21 0 0 content.json 18 0 0 default.json 7 0 0 defs.json 2 0 0 dependentRequired.json 20 0 0 dependentSchemas.json 20 0 0 enum.json 45 0 0 exclusiveMaximum.json 4 0 0 exclusiveMinimum.json 4 0 0 format.json 114 0 0 if-then-else.json 26 0 0 infinite-loop-detection.json 2 0 0 items.json 28 0 0 maxContains.json 12 0 0 maxItems.json 6 0 0 maxLength.json 7 0 0 maxProperties.json 10 0 0 maximum.json 8 0 0 minContains.json 28 0 0 minItems.json 6 0 0 minLength.json 7 0 0 minProperties.json 8 0 0 minimum.json 11 0 0 multipleOf.json 10 0 0 not.json 40 0 0 oneOf.json 27 0 0 pattern.json 9 0 0 patternProperties.json 23 0 0 properties.json 28 0 0 propertyNames.json 13 0 0 recursiveRef.json 34 0 0 ref.json 79 0 0 refRemote.json 31 0 0 required.json 16 0 0 type.json 80 0 0 unevaluatedItems.json 55 0 0 unevaluatedProperties.json 122 0 0 uniqueItems.json 69 0 0 vocabulary.json 5 0 0 optional/anchor.json 4 0 0 optional/bignum.json 9 0 0 optional/cross-draft.json 3 0 0 optional/dependencies-compatibility.json 22 14 0 optional/ecmascript-regex.json 60 14 0 optional/float-overflow.json 1 0 0 optional/id.json 3 0 0 optional/no-schema.json 3 0 0 optional/non-bmp-regex.json 12 0 0 optional/refOfUnknownKeyword.json 3 3 0 optional/unknownKeyword.json 3 0 0 --------------------------------------------------------------- TOTAL 1343 31 0 JSON-Schema-Modern-0.609/t/results/draft4-acceptance.txt000640 000766 000024 00000005604 15001302761 023145 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft4 optional tests included: yes skipping directory: optional/format filename pass todo-fail fail --------------------------------------------------------------- additionalItems.json 18 0 0 additionalProperties.json 16 0 0 allOf.json 27 0 0 anyOf.json 15 0 0 default.json 7 0 0 definitions.json 2 0 0 dependencies.json 29 0 0 enum.json 45 0 0 format.json 36 0 0 infinite-loop-detection.json 2 0 0 items.json 21 0 0 maxItems.json 4 0 0 maxLength.json 5 0 0 maxProperties.json 8 0 0 maximum.json 14 0 0 minItems.json 4 0 0 minLength.json 5 0 0 minProperties.json 6 0 0 minimum.json 17 0 0 multipleOf.json 10 0 0 not.json 20 0 0 oneOf.json 23 0 0 pattern.json 9 0 0 patternProperties.json 18 0 0 properties.json 24 0 0 ref.json 45 0 0 refRemote.json 17 0 0 required.json 15 0 0 type.json 79 0 0 uniqueItems.json 69 0 0 optional/bignum.json 9 0 0 optional/ecmascript-regex.json 60 14 0 optional/float-overflow.json 1 0 0 optional/id.json 3 0 0 optional/non-bmp-regex.json 12 0 0 optional/zeroTerminatedFloats.json 0 1 0 --------------------------------------------------------------- TOTAL 695 15 0 JSON-Schema-Modern-0.609/t/results/draft2020-12-acceptance-format.txt000640 000766 000024 00000003740 15001302761 025072 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft2020-12 using custom test directory: /optional/format optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- date-time.json 25 0 0 date.json 47 0 0 duration.json 26 0 0 ecmascript-regex.json 0 1 0 email.json 20 2 0 hostname.json 25 0 0 idn-email.json 10 0 0 idn-hostname.json 43 15 0 ipv4.json 16 0 0 ipv6.json 40 0 0 iri-reference.json 11 2 0 iri.json 14 1 0 json-pointer.json 38 0 0 regex.json 8 0 0 relative-json-pointer.json 18 0 0 time.json 45 0 0 unknown.json 7 0 0 uri-reference.json 13 0 0 uri-template.json 9 1 0 uri.json 25 1 0 uuid.json 21 0 0 --------------------------------------------------------------- TOTAL 461 23 0 JSON-Schema-Modern-0.609/t/results/draft2019-09-acceptance-format.txt000640 000766 000024 00000003640 15001302761 025107 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft2019-09 using custom test directory: /optional/format optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- date-time.json 25 0 0 date.json 47 0 0 duration.json 26 0 0 email.json 15 0 0 hostname.json 25 0 0 idn-email.json 10 0 0 idn-hostname.json 43 15 0 ipv4.json 16 0 0 ipv6.json 40 0 0 iri-reference.json 11 2 0 iri.json 14 1 0 json-pointer.json 38 0 0 regex.json 8 0 0 relative-json-pointer.json 18 0 0 time.json 45 0 0 unknown.json 7 0 0 uri-reference.json 13 0 0 uri-template.json 9 1 0 uri.json 25 1 0 uuid.json 21 0 0 --------------------------------------------------------------- TOTAL 456 20 0 JSON-Schema-Modern-0.609/t/results/draft2019-09-additional-tests.txt000640 000766 000024 00000003513 15001302761 025002 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 specification version: draft2019-09 using custom test directory: t/additional-tests-draft2019-09 optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- anchor.json 2 0 0 annotation-collection.json 10 0 0 badRef.json 3 0 0 faux-buggy-schemas.json 2 0 0 format-duration.json 9 0 0 format-ipv4.json 4 0 0 format-ipv6.json 17 0 0 format-relative-json-pointer.json 7 0 0 formats.json 4 0 0 id.json 6 0 0 integers.json 29 0 0 keyword-independence.json 812 0 0 loose-types-const-enum.json 8 0 0 recursive-dynamic.json 4 0 0 ref-and-id.json 6 0 0 ref.json 10 0 0 short-circuit.json 20 0 0 unknownKeyword.json 4 0 0 vocabulary.json 5 0 0 optional/format-date-time.json 4 0 0 optional/format-date.json 1 0 0 optional/format-time.json 13 0 0 --------------------------------------------------------------- TOTAL 980 0 0 JSON-Schema-Modern-0.609/t/results/draft2020-12-acceptance.txt000640 000766 000024 00000010512 15001302761 023577 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft2020-12 optional tests included: yes skipping directory: optional/format filename pass todo-fail fail --------------------------------------------------------------- additionalProperties.json 21 0 0 allOf.json 30 0 0 anchor.json 8 0 0 anyOf.json 18 0 0 boolean_schema.json 18 0 0 const.json 50 0 0 contains.json 21 0 0 content.json 18 0 0 default.json 7 0 0 defs.json 2 0 0 dependentRequired.json 20 0 0 dependentSchemas.json 20 0 0 dynamicRef.json 42 0 0 enum.json 45 0 0 exclusiveMaximum.json 4 0 0 exclusiveMinimum.json 4 0 0 format.json 133 0 0 if-then-else.json 26 0 0 infinite-loop-detection.json 2 0 0 items.json 29 0 0 maxContains.json 12 0 0 maxItems.json 6 0 0 maxLength.json 7 0 0 maxProperties.json 10 0 0 maximum.json 8 0 0 minContains.json 28 0 0 minItems.json 6 0 0 minLength.json 7 0 0 minProperties.json 8 0 0 minimum.json 11 0 0 multipleOf.json 10 0 0 not.json 40 0 0 oneOf.json 27 0 0 pattern.json 9 0 0 patternProperties.json 23 0 0 prefixItems.json 11 0 0 properties.json 28 0 0 propertyNames.json 10 0 0 ref.json 77 0 0 refRemote.json 31 0 0 required.json 16 0 0 type.json 80 0 0 unevaluatedItems.json 66 0 0 unevaluatedProperties.json 122 0 0 uniqueItems.json 69 0 0 vocabulary.json 5 0 0 optional/anchor.json 4 0 0 optional/bignum.json 9 0 0 optional/cross-draft.json 1 0 0 optional/dependencies-compatibility.json 22 14 0 optional/dynamicRef.json 2 0 0 optional/ecmascript-regex.json 60 14 0 optional/float-overflow.json 1 0 0 optional/format-assertion.json 4 0 0 optional/id.json 3 0 0 optional/no-schema.json 3 0 0 optional/non-bmp-regex.json 12 0 0 optional/refOfUnknownKeyword.json 3 3 0 optional/unknownKeyword.json 3 0 0 --------------------------------------------------------------- TOTAL 1372 31 0 JSON-Schema-Modern-0.609/t/results/draft7-acceptance-format.txt000640 000766 000024 00000003432 15001302761 024433 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 with commit: e524505b8ac4a61c5dc162b51d68c2385a134706 (Test-JSON-Schema-Acceptance-1.026-16-ge524505) from repository: git://github.com/json-schema-org/JSON-Schema-Test-Suite.git specification version: draft7 using custom test directory: /optional/format optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- date-time.json 25 0 0 date.json 47 0 0 email.json 15 0 0 hostname.json 24 0 0 idn-email.json 10 0 0 idn-hostname.json 42 15 0 ipv4.json 16 0 0 ipv6.json 40 0 0 iri-reference.json 11 2 0 iri.json 14 1 0 json-pointer.json 38 0 0 regex.json 8 0 0 relative-json-pointer.json 18 0 0 time.json 45 0 0 unknown.json 7 0 0 uri-reference.json 13 0 0 uri-template.json 9 1 0 uri.json 25 1 0 --------------------------------------------------------------- TOTAL 407 20 0 JSON-Schema-Modern-0.609/t/results/draft2019-09-invalid-schemas.txt000640 000766 000024 00000001174 15001302761 024602 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 specification version: draft2019-09 using custom test directory: t/invalid-schemas optional tests included: no filename pass todo-fail fail --------------------------------------------------------------- invalid-input.json 2 0 0 ref.json 2 0 0 vocabulary.json 4 0 0 --------------------------------------------------------------- TOTAL 8 0 0 JSON-Schema-Modern-0.609/t/results/draft2020-12-invalid-schemas.txt000640 000766 000024 00000001174 15001302761 024564 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 specification version: draft2020-12 using custom test directory: t/invalid-schemas optional tests included: no filename pass todo-fail fail --------------------------------------------------------------- invalid-input.json 2 0 0 ref.json 2 0 0 vocabulary.json 4 0 0 --------------------------------------------------------------- TOTAL 8 0 0 JSON-Schema-Modern-0.609/t/results/draft7-additional-tests.txt000640 000766 000024 00000003077 15001302761 024334 0ustar00etherstaff000000 000000 generated with: Test::JSON::Schema::Acceptance 1.027 specification version: draft7 using custom test directory: t/additional-tests-draft7 optional tests included: yes filename pass todo-fail fail --------------------------------------------------------------- badRef.json 3 0 0 faux-buggy-schemas.json 2 0 0 format-ipv4.json 4 0 0 format-relative-json-pointer.json 7 0 0 id.json 9 0 0 integers.json 29 0 0 keyword-independence.json 636 0 0 loose-types-const-enum.json 8 0 0 not-an-anchor.json 2 0 0 not-an-id.json 2 0 0 ref-and-id.json 2 0 0 ref.json 10 0 0 short-circuit.json 16 0 0 unknownKeyword.json 8 0 0 vocabulary.json 3 0 0 optional/format-date-time.json 4 0 0 optional/format-date.json 1 0 0 optional/format-time.json 13 0 0 --------------------------------------------------------------- TOTAL 759 0 0 JSON-Schema-Modern-0.609/t/additional-tests-draft7/ref-and-id.json000640 000766 000024 00000002002 15001302761 024656 0ustar00etherstaff000000 000000 [ { "description": "$id must be evaluated before $ref to get the proper $ref destination", "schema": { "$id": "/ref-and-id2/base.json", "allOf": [ { "$ref": "int.json" } ], "definitions": { "bigint": { "$comment": "canonical uri: /ref-and-id2/int.json", "$id": "int.json", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /ref-and-id2-int.json", "$id": "/ref-and-id2-int.json", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/loose-types-const-enum.json000640 000766 000024 00000003660 15001302761 027334 0ustar00etherstaff000000 000000 [ { "description": "type can be inferred from const and enum values", "comment": "in some languages, 6 and '6' are equal in both stringwise and numeric comparisons, but the JSON type model is more strict", "schema": { "properties": { "const_string": { "const": "6" }, "const_number": { "const": 6 }, "enum_string": { "enum": [ "6" ] }, "enum_number": { "enum": [ 6 ] } } }, "tests": [ { "description": "const numeric value does not match string", "data": { "const_string": 6 }, "valid": false }, { "description": "const numeric value does match number", "data": { "const_number": 6 }, "valid": true }, { "description": "const string value matches string", "data": { "const_string": "6" }, "valid": true }, { "description": "const string value does not match number", "data": { "const_number": "6" }, "valid": false }, { "description": "enum numeric value does not match string", "data": { "enum_string": 6 }, "valid": false }, { "description": "enum numeric value does match number", "data": { "enum_number": 6 }, "valid": true }, { "description": "enum string value matches string", "data": { "enum_string": "6" }, "valid": true }, { "description": "enum string value does not match number", "data": { "enum_number": "6" }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/unknownKeyword.json000640 000766 000024 00000003643 15001302761 026030 0ustar00etherstaff000000 000000 [ { "description": "past and future keywords are ignored", "schema": { "id": "#bad_syntax!for anchor~", "dependentSchemas": { "alpha": false }, "dependentRequired": { "beta": [ "zeta" ] }, "prefixItems": [ true, false ], "type": "number" }, "tests": [ { "description": "passing", "data": 1, "valid": true }, { "description": "failing (string)", "data": "foo", "valid": false }, { "description": "failing (object)", "data": { "alpha": 1, "beta": 2 }, "valid": false }, { "description": "failing (array)", "data": [ 1, 2 ], "valid": false } ] }, { "description": "$recursiveRef without $recursiveAnchor works like $ref", "schema": { "properties": { "foo": {"$recursiveRef": "#"} }, "additionalProperties": false }, "tests": [ { "description": "match", "data": {"foo": false}, "valid": true }, { "description": "recursive match", "data": {"foo": {"foo": false}}, "valid": true }, { "description": "mismatch", "data": {"bar": false}, "valid": false }, { "description": "recursive mismatch (but $dynamicRef is ignored)", "data": {"foo": {"bar": false}}, "valid": true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/vocabulary.json000640 000766 000024 00000002645 15001302761 025134 0ustar00etherstaff000000 000000 [ { "description": "valid $vocabulary: everything is valid", "schema": { "$id": "https://mymetaschema", "$vocabulary": { "https://json-schema.org/draft/2019-09/vocab/core": true, "https://foo.com": false } }, "tests": [ { "description": "a valid $vocabulary value", "data": 1, "valid": true } ] }, { "description": "wrong specification version: but this is draft7", "schema": { "$id": "https://mymetaschema2", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true } }, "tests": [ { "description": "invalid $vocabulary value: wrong specification version", "data": 1, "valid": true } ] }, { "description": "but this is draft7", "schema": { "$id": "https://mymetaschema3", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://foo.com": true } }, "tests": [ { "description": "invalid $vocabulary value: unknown URI", "data": 1, "valid": true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/badRef.json000640 000766 000024 00000003534 15001302761 024146 0ustar00etherstaff000000 000000 [ { "description": "good references in schemas", "schema": { "if": false, "then": { "allOf": [ { "$ref": "foo/bar.json" }, { "$ref": "foo/bar.json#" }, { "$ref": "foo/bar.json#anchor" }, { "$ref": "foo/bar.json#/" }, { "$ref": "foo/bar.json#/foo" }, { "$ref": "foo/bar.json#/~1/foo" }, { "$recursiveRef": "foo/bar.json" }, { "$recursiveRef": "foo/bar.json#" }, { "$recursiveRef": "foo/bar.json#anchor" }, { "$recursiveRef": "foo/bar.json#/" }, { "$recursiveRef": "foo/bar.json#/foo" }, { "$recursiveRef": "foo/bar.json#/~1/foo" } ] } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": true } ] }, { "description": "bad $ref in schema: not a valid anchor fragment", "schema": { "if": false, "then": { "$ref": "foo/bar.json#!!not-an-anchor" } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": false } ] }, { "description": "bad $ref in schema: not a valid json pointer fragment", "schema": { "if": false, "then": { "$ref": "foo/bar.json#/~foo" } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/keyword-independence.json000640 000766 000024 00000424142 15001302761 027070 0ustar00etherstaff000000 000000 [ { "description" : "contains + exclusiveMaximum", "schema" : { "contains" : true, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "contains + exclusiveMinimum", "schema" : { "contains" : true, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "contains + maximum", "schema" : { "contains" : true, "maximum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "contains + minimum", "schema" : { "contains" : true, "minimum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "contains + multipleOf", "schema" : { "contains" : true, "multipleOf" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + items", "schema" : { "exclusiveMaximum" : 2, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + items", "schema" : { "exclusiveMinimum" : 2, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "items + maximum", "schema" : { "items" : [ false ], "maximum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "items + minimum", "schema" : { "items" : [ false ], "minimum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "items + multipleOf", "schema" : { "items" : [ false ], "multipleOf" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxItems", "schema" : { "exclusiveMaximum" : 2, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxItems", "schema" : { "exclusiveMinimum" : 2, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maxItems + maximum", "schema" : { "maxItems" : 1, "maximum" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "maxItems + minimum", "schema" : { "maxItems" : 1, "minimum" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "maxItems + multipleOf", "schema" : { "maxItems" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minItems", "schema" : { "exclusiveMaximum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minItems", "schema" : { "exclusiveMinimum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + minItems", "schema" : { "maximum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minItems + minimum", "schema" : { "minItems" : 2, "minimum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "minItems + multipleOf", "schema" : { "minItems" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + uniqueItems", "schema" : { "exclusiveMaximum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + uniqueItems", "schema" : { "exclusiveMinimum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + uniqueItems", "schema" : { "maximum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minimum + uniqueItems", "schema" : { "minimum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "multipleOf + uniqueItems", "schema" : { "multipleOf" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "additionalProperties + contains", "schema" : { "additionalProperties" : false, "contains" : true }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "contains + dependencies", "schema" : { "contains" : true, "dependencies" : { "x" : false } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "contains + maxProperties", "schema" : { "contains" : true, "maxProperties" : 1 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "contains + minProperties", "schema" : { "contains" : true, "minProperties" : 1 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "contains + patternProperties", "schema" : { "contains" : true, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "contains + properties", "schema" : { "contains" : true, "properties" : { "x" : false } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "contains + propertyNames", "schema" : { "contains" : true, "propertyNames" : false }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "contains + required", "schema" : { "contains" : true, "required" : [ "x" ] }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + items", "schema" : { "additionalProperties" : false, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + items", "schema" : { "dependencies" : { "x" : false }, "items" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "items + maxProperties", "schema" : { "items" : [ false ], "maxProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "items + minProperties", "schema" : { "items" : [ false ], "minProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "items + patternProperties", "schema" : { "items" : [ false ], "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "items + properties", "schema" : { "items" : [ false ], "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "items + propertyNames", "schema" : { "items" : [ false ], "propertyNames" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "items + required", "schema" : { "items" : [ false ], "required" : [ "x" ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + maxItems", "schema" : { "additionalProperties" : false, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + maxItems", "schema" : { "dependencies" : { "x" : false }, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "maxItems + maxProperties", "schema" : { "maxItems" : 1, "maxProperties" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "maxItems + minProperties", "schema" : { "maxItems" : 1, "minProperties" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "maxItems + patternProperties", "schema" : { "maxItems" : 1, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "maxItems + properties", "schema" : { "maxItems" : 1, "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "maxItems + propertyNames", "schema" : { "maxItems" : 1, "propertyNames" : false }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "maxItems + required", "schema" : { "maxItems" : 1, "required" : [ "x" ] }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + minItems", "schema" : { "additionalProperties" : false, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + minItems", "schema" : { "dependencies" : { "x" : false }, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "maxProperties + minItems", "schema" : { "maxProperties" : 1, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minItems + minProperties", "schema" : { "minItems" : 2, "minProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "minItems + patternProperties", "schema" : { "minItems" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "minItems + properties", "schema" : { "minItems" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "minItems + propertyNames", "schema" : { "minItems" : 2, "propertyNames" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "minItems + required", "schema" : { "minItems" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + uniqueItems", "schema" : { "additionalProperties" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + uniqueItems", "schema" : { "dependencies" : { "x" : false }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "maxProperties + uniqueItems", "schema" : { "maxProperties" : 1, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + uniqueItems", "schema" : { "minProperties" : 1, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "patternProperties + uniqueItems", "schema" : { "patternProperties" : { "no" : false, "yes" : true }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "properties + uniqueItems", "schema" : { "properties" : { "x" : false }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "propertyNames + uniqueItems", "schema" : { "propertyNames" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "required + uniqueItems", "schema" : { "required" : [ "x" ], "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "contains + maxLength", "schema" : { "contains" : true, "maxLength" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "contains + minLength", "schema" : { "contains" : true, "minLength" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "contains + pattern", "schema" : { "contains" : true, "pattern" : "hi" }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "items + maxLength", "schema" : { "items" : [ false ], "maxLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "items + minLength", "schema" : { "items" : [ false ], "minLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "items + pattern", "schema" : { "items" : [ false ], "pattern" : "hi" }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxItems + maxLength", "schema" : { "maxItems" : 1, "maxLength" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maxItems + minLength", "schema" : { "maxItems" : 1, "minLength" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maxItems + pattern", "schema" : { "maxItems" : 1, "pattern" : "hi" }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minItems", "schema" : { "maxLength" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minItems + minLength", "schema" : { "minItems" : 2, "minLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minItems + pattern", "schema" : { "minItems" : 2, "pattern" : "hi" }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + uniqueItems", "schema" : { "maxLength" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + uniqueItems", "schema" : { "minLength" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + uniqueItems", "schema" : { "pattern" : "hi", "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "additionalProperties + exclusiveMaximum", "schema" : { "additionalProperties" : false, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + exclusiveMaximum", "schema" : { "dependencies" : { "x" : false }, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxProperties", "schema" : { "exclusiveMaximum" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minProperties", "schema" : { "exclusiveMaximum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + patternProperties", "schema" : { "exclusiveMaximum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + properties", "schema" : { "exclusiveMaximum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + propertyNames", "schema" : { "exclusiveMaximum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + required", "schema" : { "exclusiveMaximum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + exclusiveMinimum", "schema" : { "additionalProperties" : false, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + exclusiveMinimum", "schema" : { "dependencies" : { "x" : false }, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxProperties", "schema" : { "exclusiveMinimum" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minProperties", "schema" : { "exclusiveMinimum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + patternProperties", "schema" : { "exclusiveMinimum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + properties", "schema" : { "exclusiveMinimum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + propertyNames", "schema" : { "exclusiveMinimum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + required", "schema" : { "exclusiveMinimum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + maximum", "schema" : { "additionalProperties" : false, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + maximum", "schema" : { "dependencies" : { "x" : false }, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "maxProperties + maximum", "schema" : { "maxProperties" : 1, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "maximum + minProperties", "schema" : { "maximum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "maximum + patternProperties", "schema" : { "maximum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "maximum + properties", "schema" : { "maximum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "maximum + propertyNames", "schema" : { "maximum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "maximum + required", "schema" : { "maximum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + minimum", "schema" : { "additionalProperties" : false, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + minimum", "schema" : { "dependencies" : { "x" : false }, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "maxProperties + minimum", "schema" : { "maxProperties" : 1, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + minimum", "schema" : { "minProperties" : 1, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "minimum + patternProperties", "schema" : { "minimum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "minimum + properties", "schema" : { "minimum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "minimum + propertyNames", "schema" : { "minimum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "minimum + required", "schema" : { "minimum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "additionalProperties + multipleOf", "schema" : { "additionalProperties" : false, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependencies + multipleOf", "schema" : { "dependencies" : { "x" : false }, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true } ] }, { "description" : "maxProperties + multipleOf", "schema" : { "maxProperties" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + multipleOf", "schema" : { "minProperties" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "multipleOf + patternProperties", "schema" : { "multipleOf" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "multipleOf + properties", "schema" : { "multipleOf" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "multipleOf + propertyNames", "schema" : { "multipleOf" : 2, "propertyNames" : false }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "multipleOf + required", "schema" : { "multipleOf" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxLength", "schema" : { "exclusiveMaximum" : 2, "maxLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minLength", "schema" : { "exclusiveMaximum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + pattern", "schema" : { "exclusiveMaximum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxLength", "schema" : { "exclusiveMinimum" : 2, "maxLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minLength", "schema" : { "exclusiveMinimum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + pattern", "schema" : { "exclusiveMinimum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + maximum", "schema" : { "maxLength" : 2, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maximum + minLength", "schema" : { "maximum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maximum + pattern", "schema" : { "maximum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minimum", "schema" : { "maxLength" : 2, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + minimum", "schema" : { "minLength" : 2, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minimum + pattern", "schema" : { "minimum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + multipleOf", "schema" : { "maxLength" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + multipleOf", "schema" : { "minLength" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "multipleOf + pattern", "schema" : { "multipleOf" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "additionalProperties + maxLength", "schema" : { "additionalProperties" : false, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "additionalProperties + minLength", "schema" : { "additionalProperties" : false, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "additionalProperties + pattern", "schema" : { "additionalProperties" : false, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "dependencies + maxLength", "schema" : { "dependencies" : { "x" : false }, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "dependencies + minLength", "schema" : { "dependencies" : { "x" : false }, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "dependencies + pattern", "schema" : { "dependencies" : { "x" : false }, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependencies invalid", "valid" : false }, { "data" : {}, "description" : "object, dependencies valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + maxProperties", "schema" : { "maxLength" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maxProperties + minLength", "schema" : { "maxProperties" : 1, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maxProperties + pattern", "schema" : { "maxProperties" : 1, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minProperties", "schema" : { "maxLength" : 2, "minProperties" : 1 }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + minProperties", "schema" : { "minLength" : 2, "minProperties" : 1 }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minProperties + pattern", "schema" : { "minProperties" : 1, "pattern" : "hi" }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + patternProperties", "schema" : { "maxLength" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + patternProperties", "schema" : { "minLength" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + patternProperties", "schema" : { "pattern" : "hi", "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + properties", "schema" : { "maxLength" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + properties", "schema" : { "minLength" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + properties", "schema" : { "pattern" : "hi", "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + propertyNames", "schema" : { "maxLength" : 2, "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + propertyNames", "schema" : { "minLength" : 2, "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + propertyNames", "schema" : { "pattern" : "hi", "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + required", "schema" : { "maxLength" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + required", "schema" : { "minLength" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + required", "schema" : { "pattern" : "hi", "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/README000640 000766 000024 00000000121 15001302761 022735 0ustar00etherstaff000000 000000 Tests go here until/unless they are accepted by the main JSON-Schema-Test-Suite. JSON-Schema-Modern-0.609/t/additional-tests-draft7/short-circuit.json000640 000766 000024 00000010716 15001302761 025562 0ustar00etherstaff000000 000000 [ { "description": "items (schema form) does not improperly short-circuit", "schema": { "items": { "multipleOf": 2 } }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "items (array form) does not improperly short-circuit", "schema": { "items": [ { "multipleOf": 2 }, { "multipleOf": 2 } ] }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "additionalItems does not improperly short-circuit", "schema": { "items": [ true ], "additionalItems": { "multipleOf": 2 } }, "tests": [ { "description": "single additional item is valid", "data": [ 1, 2 ], "valid": true }, { "description": "second additional item is invalid", "data": [ 1, 2, 1 ], "valid": false } ] }, { "description": "properties does not improperly short-circuit", "schema": { "properties": { "alpha": { "multipleOf": 2 }, "beta": { "multipleOf": 2 } } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "properties does not improperly short-circuit (boolean schemas)", "schema": { "properties": { "alpha": true, "beta": false } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "patternProperties does not improperly short-circuit", "schema": { "patternProperties": { "^a": { "multipleOf": 2 }, "^b": { "multipleOf": 2 } } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "patternProperties does not improperly short-circuit (boolean schemas)", "schema": { "patternProperties": { "^a": true, "^b": false } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "additionalProperties does not improperly short-circuit", "schema": { "additionalProperties": { "multipleOf": 2 } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/optional/000770 000766 000024 00000000000 15001302761 023711 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/additional-tests-draft7/integers.json000640 000766 000024 00000013141 15001302761 024576 0ustar00etherstaff000000 000000 [ { "description": "type checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "type": "integer" }, "tests": [ { "description": "beyond int64 lower boundary", "data": -9223372036854775809, "valid": true }, { "description": "int64 lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "beyond int32 lower boundary", "data": -2147483649, "valid": true }, { "description": "int32 lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "int32 upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond int32 upper boundary", "data": 2147483648, "valid": true }, { "description": "upper int64 boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond int64 upper boundary", "data": 9223372036854775808, "valid": true } ] }, { "description": "int32 range checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "minimum": -2147483648, "maximum": 2147483647 }, "tests": [ { "description": "beyond lower boundary", "data": -2147483649, "valid": false }, { "description": "lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond upper boundary", "data": 2147483648, "valid": false } ] }, { "description": "int64 range checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "minimum": -9223372036854775808, "maximum": 9223372036854775807 }, "tests": [ { "description": "beyond lower boundary", "data": -9223372036854775809, "valid": false }, { "description": "lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "upper boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond upper boundary", "data": 9223372036854775808, "valid": false } ] }, { "description": "equality checks", "schema": { "$comment": "on some architectures, some of these values may json-decode to strings", "enum": [ -9223372036854775809, -9223372036854775808, -2147483649, -2147483648, 0, 2147483647, 2147483648, 9223372036854775807, 9223372036854775808 ] }, "tests": [ { "description": "beyond int64 lower boundary", "data": -9223372036854775809, "valid": true }, { "description": "int64 lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "beyond int32 lower boundary", "data": -2147483649, "valid": true }, { "description": "int32 lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "one", "data": 1, "valid": false }, { "description": "int32 upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond int32 upper boundary", "data": 2147483648, "valid": true }, { "description": "int64 upper boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond int64 upper boundary", "data": 9223372036854775808, "valid": true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/format-ipv4.json000640 000766 000024 00000002150 15001302761 025124 0ustar00etherstaff000000 000000 [ { "description": "validation of ipv4 strings", "schema": { "format": "ipv4" }, "tests": [ { "description": "leading zeroes should be rejected, as they are treated as octal values", "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/", "data": "087.10.0.1", "valid": false }, { "description": "leading zeroes should be rejected, even if not a valid octal value", "data": "099.10.0.1", "valid": false }, { "description": "value without leading zero is valid", "data": "87.10.0.1", "valid": true }, { "description": "non-ascii digits should be rejected", "data": "1২7.0.0.1", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/format-relative-json-pointer.json000640 000766 000024 00000002664 15001302761 030514 0ustar00etherstaff000000 000000 [ { "description": "validation of relative-json-pointer strings", "schema": {"format": "relative-json-pointer"}, "comment": "see https://datatracker.ietf.org/doc/html/draft-handrews-relative-json-pointer-01#section-3", "tests": [ { "description": "non-negative-integer with empty json-pointer", "data": "0", "valid": true }, { "description": "non-negative-integer with one-char json-pointer", "data": "0/", "valid": true }, { "description": "non-negative-integer with longer json-pointer", "data": "0/a", "valid": true }, { "description": "non-negative-integer #", "data": "0#", "valid": true }, { "description": "## is not a valid json-pointer", "data": "0##", "valid": false }, { "description": "zero cannot be followed by other digits, plus json-pointer", "data": "01/a", "valid": false }, { "description": "zero cannot be followed by other digits, plus octothorpe", "data": "01#", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/faux-buggy-schemas.json000640 000766 000024 00000002446 15001302761 026463 0ustar00etherstaff000000 000000 [ { "description": "identifiers in non-schemas that look like schemas", "schema": { "$id": "http://localhost:1234/faux.json", "anyOf": [ { "type": "integer", "examples": [ { "$id": "faux.json#/foo/bar" }, { "$anchor": "my$foo" } ] }, { "type": "integer", "default": { "$anchor": "not$an$anchor" } }, { "$anchor": "my_const", "const": { "$anchor": "my_const" } }, { "$id": "enum.json", "$anchor": "my_enum", "enum": [ { "$id": "enum.json" }, { "$anchor": "my_enum" } ] } ] }, "tests": [ { "description": "match the type checks", "data": 1, "valid": true }, { "description": "match none of the options", "data": "a string", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/id.json000640 000766 000024 00000005776 15001302761 023371 0ustar00etherstaff000000 000000 [ { "description": "non-schema object containing an $id property", "schema": { "definitions": { "const_not_id": { "const": { "$id": "not_a_real_id" } } }, "if": { "const": "skip not_a_real_id" }, "then": true, "else" : { "$ref": "#/definitions/const_not_id" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_id", "valid": true }, { "description": "const at const_not_id does not match", "data": 1, "valid": false } ] }, { "description": "non-schema object containing an anchor property", "schema": { "definitions": { "const_not_anchor": { "const": { "$id": "#not_a_real_anchor" } } }, "if": { "const": "skip not_a_real_anchor" }, "then": true, "else" : { "$ref": "#/definitions/const_not_anchor" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_anchor", "valid": true }, { "description": "const at const_not_anchor does not match", "data": 1, "valid": false } ] }, { "description": "'' not permitted as $id", "schema": { "$id": "", "type": "string" }, "tests": [ { "description": "schema would pass without $id", "data": "foo", "valid": false }, { "description": "schema would fail even without $id", "data": 1, "valid": false } ] }, { "description": "'#' not permitted as $id", "schema": { "$id": "#", "type": "string" }, "tests": [ { "description": "schema would pass without $id", "data": "foo", "valid": false }, { "description": "schema would fail even without $id", "data": 1, "valid": false } ] }, { "description": "bad anchor syntax", "schema": { "$id": "#!!hello~" }, "tests": [ { "description": "draft7 $id passes through to $anchor", "data": "foo", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/ref.json000640 000766 000024 00000012473 15001302761 023541 0ustar00etherstaff000000 000000 [ { "description": "order of evaluation: $id and $ref", "schema": { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "/ref1/base.json", "allOf": [ { "$ref": "int.json" } ], "definitions": { "bigint": { "$comment": "canonical uri: /ref1/int.json", "$id": "int.json", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /int.json", "$id": "/int.json", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "order of evaluation: $id and $anchor and $ref", "schema": { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "/ref2/base.json", "allOf": [ { "$ref": "#bigint" } ], "definitions": { "bigint": { "$comment": "canonical uri: /ref2/base.json#/definitions/bigint; another valid uri for this location: /ref2/base.json#bigint", "$id": "#bigint", "maximum": 10 }, "smallint": { "$id": "/ref2/", "definitions": { "bigint": { "$comment": "canonical uri: /ref2/#/definitions/smallint; another valid uri for this location: /ref2/#bigint, /ref2/base.json#/definitions/smallint/definitions/bigint", "$id": "#bigint", "maximum": 2 } } } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "naive replacement of $ref with its destination is not correct", "schema": { "definitions": { "a_string": { "type": "string" } }, "enum": [ { "$ref": "#/definitions/a_string" } ] }, "tests": [ { "description": "do not evaluate the $ref inside the enum", "data": "this is a string", "valid": false }, { "description": "match the enum exactly", "data": { "$ref": "#/definitions/a_string" }, "valid": true } ] }, { "description": "invalid $ref: invalid anchor fragment", "schema": { "definitions": { "foo": { "$ref": "https://foo.com/bar.json#-not-an-anchor" } } }, "tests": [ { "description": "an invalid anchor fragment", "data": 1, "valid": false } ] }, { "description": "invalid $ref: invalid json-pointer fragment", "schema": { "definitions": { "foo": { "$ref": "https://foo.com/bar.json#/~2/not/json/pointer" } } }, "tests": [ { "description": "an invalid json-pointer fragment", "data": 1, "valid": false } ] }, { "description": "base URI change - change folder in subschema with path from root", "comment": "based on test suite draft7/refRemote.json 'base URI change - change folder in subschema'", "schema": { "$id": "http://localhost:1234/scope_change_defs.json", "type" : "object", "properties": { "list": { "$ref": "#/definitions/baz/definitions/bar" } }, "definitions": { "folderInteger": { "$id": "baseUriChangeFolderInSubschema/folderInteger.json", "type": "integer" }, "baz": { "$id": "baseUriChangeFolderInSubschema/", "definitions": { "bar": { "type": "array", "items": { "$ref": "folderInteger.json" } } } } } }, "tests": [ { "description": "number is valid", "data": {"list": [1]}, "valid": true }, { "description": "string is invalid", "data": {"list": ["a"]}, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/not-an-anchor.json000640 000766 000024 00000001634 15001302761 025426 0ustar00etherstaff000000 000000 [ { "description": "object that is not a schema containing an $anchor property", "schema": { "definitions": { "const_not_anchor": { "const": { "$anchor": "not_a_real_anchor" } } }, "if": { "const": "skip not_a_real_anchor" }, "then": true, "else" : { "$ref": "#/definitions/const_not_anchor" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_anchor", "valid": true }, { "description": "const at const_not_anchor does not match", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/not-an-id.json000640 000766 000024 00000001574 15001302761 024553 0ustar00etherstaff000000 000000 [ { "description": "object that is not a schema containing an $id property", "schema": { "definitions": { "const_not_id": { "const": { "$id": "not_a_real_id" } } }, "if": { "const": "skip not_a_real_id" }, "then": true, "else" : { "$ref": "#/definitions/const_not_id" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_id", "valid": true }, { "description": "const at const_not_id does not match", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/optional/format-date-time.json000640 000766 000024 00000001646 15001302761 027751 0ustar00etherstaff000000 000000 [ { "description": "leap seconds", "schema": { "format": "date-time" }, "tests": [ { "description": "a valid date-time with a leap second, UTC", "data": "1998-12-31T23:59:60Z", "valid": true }, { "description": "a valid date-time with a leap second, with minus offset", "data": "1998-12-31T15:59:60.123-08:00", "valid": true }, { "description": "an invalid date-time with a leap second, UTC", "data": "1998-12-30T23:59:60Z", "valid": false }, { "description": "an invalid date-time with a leap second, with minus offset", "data": "1998-12-30T15:59:60.123-08:00", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/optional/format-time.json000640 000766 000024 00000004770 15001302761 027037 0ustar00etherstaff000000 000000 [ { "description": "validation of time strings", "schema": { "format": "time" }, "comment": "see https://tools.ietf.org/html/rfc3339#section-5.6 and https://tools.ietf.org/html/rfc3339#appendix-D", "tests": [ { "description": "valid leap second, Zulu", "data": "23:59:60Z", "valid": true }, { "description": "invalid leap second, Zulu (wrong hour)", "data": "22:59:60Z", "valid": false }, { "description": "invalid leap second, Zulu (wrong minute)", "data": "23:58:60Z", "valid": false }, { "description": "valid leap second, positive time-offset", "data": "01:29:60+01:30", "valid": true }, { "description": "valid leap second, large positive time-offset", "data": "23:29:60+23:30", "valid": true }, { "description": "invalid leap second, positive time-offset (wrong hour)", "data": "23:59:60+01:00", "valid": false }, { "description": "invalid leap second, positive time-offset (wrong minute)", "data": "23:59:60+00:30", "valid": false }, { "description": "valid leap second, negative time-offset", "data": "22:29:60-01:30", "valid": true }, { "description": "valid leap second, large negative time-offset", "data": "00:29:60-23:30", "valid": true }, { "description": "invalid leap second, negative time-offset (wrong hour)", "data": "23:59:60-01:00", "valid": false }, { "description": "invalid leap second, negative time-offset (wrong minute)", "data": "23:59:60-00:30", "valid": false }, { "description": "no time offset", "data": "12:00:00", "valid": false }, { "description": "non-ascii digits should be rejected", "data": "1২:00:00Z", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft7/optional/format-date.json000640 000766 000024 00000000524 15001302761 027007 0ustar00etherstaff000000 000000 [ { "description": "non-ascii digits should be rejected", "schema": { "format": "date" }, "tests": [ { "description": "BENGALI DIGIT TWO is not valid in dates", "data": "২001-01-01", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/ref-and-id.json000640 000766 000024 00000006116 15001302761 025325 0ustar00etherstaff000000 000000 [ { "description": "$id must be evaluated before $ref to get the proper $ref destination (with $anchors)", "schema": { "$id": "/ref-and-id1/base.json", "$ref": "#bigint", "$defs": { "bigint": { "$comment": "canonical uri: /ref-and-id1/base.json/$defs/bigint; another valid uri for this location: /ref-and-id1/base.json#bigint", "$anchor": "bigint", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /ref-and-id1#/$defs/smallint; another valid uri for this location: /ref-and-id1/#bigint", "$id": "/ref-and-id1/", "$anchor": "bigint", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "$id must be evaluated before $ref to get the proper $ref destination (with uris)", "schema": { "$id": "/ref-and-id2/base.json", "$ref": "int.json", "$defs": { "bigint": { "$comment": "canonical uri: /ref-and-id2/int.json", "$id": "int.json", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /ref-and-id2-int.json", "$id": "/ref-and-id2-int.json", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "$id must be evaluated before $ref to get the proper $ref destination (with json pointers)", "schema": { "$id": "https://localhost:1234/base/", "allOf": [ { "$id": "allOf0/", "$ref": "#/oneOf/0", "oneOf": [ { "type": "number" } ] } ], "oneOf": [ { "type": "string" }, { "type": "number" } ] }, "tests": [ { "description": "data is valid against innermost type check", "data": 1, "valid": true }, { "description": "data is invalid against innermost type check", "data": "hello", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/formats.json000640 000766 000024 00000002441 15001302761 025067 0ustar00etherstaff000000 000000 [ { "description": "format validation after a $ref", "schema": { "$defs": { "my_format": { "type": "string", "format": "duration" } }, "$ref": "#/$defs/my_format" }, "tests": [ { "description": "invalid duration", "data": "PT1D", "valid": false }, { "description": "valid duration", "data": "P4Y", "valid": true } ] }, { "description": "format validation after an $id", "schema": { "type": "array", "items": { "$id": "bloop", "allOf": [ { "type": "string", "format": "duration" } ] } }, "tests": [ { "description": "invalid duration", "data": [ "PT1D" ], "valid": false }, { "description": "valid duration", "data": [ "P4Y" ], "valid": true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/anchor.json000640 000766 000024 00000001606 15001302761 024670 0ustar00etherstaff000000 000000 [ { "description": "non-schema object containing an $anchor property", "schema": { "$defs": { "const_not_anchor": { "const": { "$anchor": "not_a_real_anchor" } } }, "if": { "const": "skip not_a_real_anchor" }, "then": true, "else" : { "$ref": "#/$defs/const_not_anchor" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_anchor", "valid": true }, { "description": "const at const_not_anchor does not match", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/loose-types-const-enum.json000640 000766 000024 00000003660 15001302761 027771 0ustar00etherstaff000000 000000 [ { "description": "type can be inferred from const and enum values", "comment": "in some languages, 6 and '6' are equal in both stringwise and numeric comparisons, but the JSON type model is more strict", "schema": { "properties": { "const_string": { "const": "6" }, "const_number": { "const": 6 }, "enum_string": { "enum": [ "6" ] }, "enum_number": { "enum": [ 6 ] } } }, "tests": [ { "description": "const numeric value does not match string", "data": { "const_string": 6 }, "valid": false }, { "description": "const numeric value does match number", "data": { "const_number": 6 }, "valid": true }, { "description": "const string value matches string", "data": { "const_string": "6" }, "valid": true }, { "description": "const string value does not match number", "data": { "const_number": "6" }, "valid": false }, { "description": "enum numeric value does not match string", "data": { "enum_string": 6 }, "valid": false }, { "description": "enum numeric value does match number", "data": { "enum_number": 6 }, "valid": true }, { "description": "enum string value matches string", "data": { "enum_string": "6" }, "valid": true }, { "description": "enum string value does not match number", "data": { "enum_number": "6" }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/unknownKeyword.json000640 000766 000024 00000002020 15001302761 026451 0ustar00etherstaff000000 000000 [ { "description": "past and future keywords are ignored", "schema": { "id": "#bad_syntax!for anchor~", "$recursiveAnchor": "ananchor", "$recursiveRef": "http://does-not-exist.com", "dependencies": { "alpha": false, "beta": [ "zeta" ] }, "additionalItems": false, "type": "number" }, "tests": [ { "description": "passing", "data": 1, "valid": true }, { "description": "failing (string)", "data": "foo", "valid": false }, { "description": "failing (object)", "data": { "alpha": 1, "beta": 2 }, "valid": false }, { "description": "failing (array)", "data": [ 1, 2 ], "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/vocabulary.json000640 000766 000024 00000011362 15001302761 025565 0ustar00etherstaff000000 000000 [ { "description": "valid $vocabulary", "schema": { "$id": "https://mymetaschema", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://foo.com": false } }, "tests": [ { "description": "a valid $vocabulary value", "data": 1, "valid": true } ] }, { "description": "invalid $vocabulary (wrong specification version), but not until we evaluate with this metaschema", "schema": { "$id": "https://mymetaschema2", "$vocabulary": { "https://json-schema.org/draft/2019-09/vocab/core": true } }, "tests": [ { "description": "invalid $vocabulary (wrong specification version): register this metaschema", "data": 1, "valid": true } ] }, { "description": "use schema (invalid $vocabulary) as a metaschema", "schema": { "$id": "https://mychema2", "$schema": "https://mymetaschema2" }, "tests": [ { "description": "invalid $vocabulary value: wrong specification version", "data": 1, "valid": false } ] }, { "description": "invalid $vocabulary (unknown URI), but not until we evaluate with this metaschema", "schema": { "$id": "https://mymetaschema3", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://foo.com": true } }, "tests": [ { "description": "invalid $vocabulary (unknown URI): register this metaschema", "data": 1, "valid": true } ] }, { "description": "use schema (unknown URI) as a metaschema", "schema": { "$id": "https://myschema3", "$schema": "https://mymetaschema3" }, "tests": [ { "description": "invalid $vocabulary value: unknown URI", "data": 1, "valid": false } ] }, { "description": "custom metaschema with format-assertion: false", "schema": { "$id": "https://mymetaschema4/with/format-assertion/false", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://json-schema.org/draft/2020-12/vocab/format-assertion": false } }, "tests": [ { "description": "custom metaschema is loaded", "data": 1, "valid": true } ] }, { "description": "custom metaschema with format-assertion: true", "schema": { "$id": "https://mymetaschema4/with/format-assertion/true", "$vocabulary": { "https://json-schema.org/draft/2020-12/vocab/core": true, "https://json-schema.org/draft/2020-12/vocab/format-assertion": true } }, "tests": [ { "description": "custom metaschema is loaded", "data": 1, "valid": true } ] }, { "description": "schema that uses custom metaschema with format-assertion: false", "schema": { "$id": "https://schema/using/format-assertion/false", "$schema": "https://mymetaschema4/with/format-assertion/false", "format": "ipv4" }, "tests": [ { "description": "format-assertion: false: valid string", "data": "127.0.0.1", "valid": true }, { "description": "format-assertion: false: invalid string", "data": "not-an-ipv4", "valid": false } ] }, { "description": "schema that uses custom metaschema with format-assertion: true", "schema": { "$id": "https://schema/using/format-assertion/true", "$schema": "https://mymetaschema4/with/format-assertion/true", "format": "ipv4" }, "tests": [ { "description": "format-assertion: true: valid string", "data": "127.0.0.1", "valid": true }, { "description": "format-assertion: true: invalid string", "data": "not-an-ipv4", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/badRef.json000640 000766 000024 00000003520 15001302761 024576 0ustar00etherstaff000000 000000 [ { "description": "good references in schemas", "schema": { "if": false, "then": { "allOf": [ { "$ref": "foo/bar.json" }, { "$ref": "foo/bar.json#" }, { "$ref": "foo/bar.json#anchor" }, { "$ref": "foo/bar.json#/" }, { "$ref": "foo/bar.json#/foo" }, { "$ref": "foo/bar.json#/~1/foo" }, { "$dynamicRef": "foo/bar.json" }, { "$dynamicRef": "foo/bar.json#" }, { "$dynamicRef": "foo/bar.json#anchor" }, { "$dynamicRef": "foo/bar.json#/" }, { "$dynamicRef": "foo/bar.json#/foo" }, { "$dynamicRef": "foo/bar.json#/~1/foo" } ] } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": true } ] }, { "description": "bad $ref in schema: not a valid anchor fragment", "schema": { "if": false, "then": { "$ref": "foo/bar.json#!!not-an-anchor" } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": false } ] }, { "description": "bad $ref in schema: not a valid json pointer fragment", "schema": { "if": false, "then": { "$ref": "foo/bar.json#/~foo" } }, "tests": [ { "description": "this data is never used", "data": "ignore", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/format-ipv6.json000640 000766 000024 00000005751 15001302761 025575 0ustar00etherstaff000000 000000 [ { "description": "validation of ipv6 strings", "schema": { "type": "string", "format": "ipv6" }, "tests": [ { "description": "fully expanded with zeroes", "data": "0000:0000:0000:0000:0000:0000:0000:0000", "valid": true }, { "description": "partially collapsed zeroes", "data": "00:000:00:0:000:000:000:0000", "valid": true }, { "description": "4,2 zeroes", "data": "0:0:0:0::0:0", "valid": true }, { "description": "8 single zeroes, no double colon", "data": "0:0:0:0:0:0:0:0", "valid": true }, { "description": "7 single zeroes, leading double colon", "data": "::0:0:0:0:0:0:0", "valid": true }, { "description": "6 single zeroes, leading double colon", "data": "::0:0:0:0:0:0", "valid": true }, { "description": "5 single zeroes, leading double colon", "data": "::0:0:0:0:0", "valid": true }, { "description": "4 single zeroes, leading double colon", "data": "::0:0:0:0", "valid": true }, { "description": "3 single zeroes, leading double colon", "data": "::0:0:0", "valid": true }, { "description": "2 single zeroes, leading double colon", "data": "::0:0", "valid": true }, { "description": "1 single zero, leading double colon", "data": "::0", "valid": true }, { "description": "1 single zero, trailing double colon", "data": "0::", "valid": true }, { "description": "2 leading colons with 8 zeroes", "data": "::0:0:0:0:0:0:0:0", "valid": false }, { "description": "1 leading colon with 8 zeroes", "data": ":0:0:0:0:0:0:0:0", "valid": false }, { "description": "colons in the middle with 8 zeroes", "data": "0:0:0:0::0:0:0:0", "valid": false }, { "description": "1 trailing colon with 8 zeroes", "data": "0:0:0:0:0:0:0:0:", "valid": false }, { "description": "2 trailing colons with 8 zeroes", "data": "0:0:0:0:0:0:0:0::", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/keyword-independence.json000640 000766 000024 00000605566 15001302761 027540 0ustar00etherstaff000000 000000 [ { "description" : "contains + exclusiveMaximum", "schema" : { "contains" : true, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "contains + exclusiveMinimum", "schema" : { "contains" : true, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "contains + maximum", "schema" : { "contains" : true, "maximum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "contains + minimum", "schema" : { "contains" : true, "minimum" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "contains + multipleOf", "schema" : { "contains" : true, "multipleOf" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + items", "schema" : { "exclusiveMaximum" : 2, "items" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + items", "schema" : { "exclusiveMinimum" : 2, "items" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "items + maximum", "schema" : { "items" : false, "maximum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "items + minimum", "schema" : { "items" : false, "minimum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "items + multipleOf", "schema" : { "items" : false, "multipleOf" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxItems", "schema" : { "exclusiveMaximum" : 2, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxItems", "schema" : { "exclusiveMinimum" : 2, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maxItems + maximum", "schema" : { "maxItems" : 1, "maximum" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "maxItems + minimum", "schema" : { "maxItems" : 1, "minimum" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "maxItems + multipleOf", "schema" : { "maxItems" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minItems", "schema" : { "exclusiveMaximum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minItems", "schema" : { "exclusiveMinimum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + minItems", "schema" : { "maximum" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minItems + minimum", "schema" : { "minItems" : 2, "minimum" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "minItems + multipleOf", "schema" : { "minItems" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + prefixItems", "schema" : { "exclusiveMaximum" : 2, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + prefixItems", "schema" : { "exclusiveMinimum" : 2, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + prefixItems", "schema" : { "maximum" : 2, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minimum + prefixItems", "schema" : { "minimum" : 2, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "multipleOf + prefixItems", "schema" : { "multipleOf" : 2, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + unevaluatedItems", "schema" : { "exclusiveMaximum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + unevaluatedItems", "schema" : { "exclusiveMinimum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + unevaluatedItems", "schema" : { "maximum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minimum + unevaluatedItems", "schema" : { "minimum" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "multipleOf + unevaluatedItems", "schema" : { "multipleOf" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + uniqueItems", "schema" : { "exclusiveMaximum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + uniqueItems", "schema" : { "exclusiveMinimum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true } ] }, { "description" : "maximum + uniqueItems", "schema" : { "maximum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true } ] }, { "description" : "minimum + uniqueItems", "schema" : { "minimum" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true } ] }, { "description" : "multipleOf + uniqueItems", "schema" : { "multipleOf" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true } ] }, { "description" : "additionalProperties + contains", "schema" : { "additionalProperties" : false, "contains" : true }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "contains + dependentRequired", "schema" : { "contains" : true, "dependentRequired" : { "x" : [ "y" ] } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "contains + dependentSchemas", "schema" : { "contains" : true, "dependentSchemas" : { "x" : false } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "contains + maxProperties", "schema" : { "contains" : true, "maxProperties" : 1 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "contains + minProperties", "schema" : { "contains" : true, "minProperties" : 1 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "contains + patternProperties", "schema" : { "contains" : true, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "contains + properties", "schema" : { "contains" : true, "properties" : { "x" : false } }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "contains + propertyNames", "schema" : { "contains" : true, "propertyNames" : false }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "contains + required", "schema" : { "contains" : true, "required" : [ "x" ] }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "contains + unevaluatedProperties", "schema" : { "contains" : true, "unevaluatedProperties" : false }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + items", "schema" : { "additionalProperties" : false, "items" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + items", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "items" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + items", "schema" : { "dependentSchemas" : { "x" : false }, "items" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "items + maxProperties", "schema" : { "items" : false, "maxProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "items + minProperties", "schema" : { "items" : false, "minProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "items + patternProperties", "schema" : { "items" : false, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "items + properties", "schema" : { "items" : false, "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "items + propertyNames", "schema" : { "items" : false, "propertyNames" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "items + required", "schema" : { "items" : false, "required" : [ "x" ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "items + unevaluatedProperties", "schema" : { "items" : false, "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + maxItems", "schema" : { "additionalProperties" : false, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + maxItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + maxItems", "schema" : { "dependentSchemas" : { "x" : false }, "maxItems" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxItems + maxProperties", "schema" : { "maxItems" : 1, "maxProperties" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "maxItems + minProperties", "schema" : { "maxItems" : 1, "minProperties" : 1 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "maxItems + patternProperties", "schema" : { "maxItems" : 1, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "maxItems + properties", "schema" : { "maxItems" : 1, "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "maxItems + propertyNames", "schema" : { "maxItems" : 1, "propertyNames" : false }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "maxItems + required", "schema" : { "maxItems" : 1, "required" : [ "x" ] }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "maxItems + unevaluatedProperties", "schema" : { "maxItems" : 1, "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + minItems", "schema" : { "additionalProperties" : false, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + minItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + minItems", "schema" : { "dependentSchemas" : { "x" : false }, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + minItems", "schema" : { "maxProperties" : 1, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minItems + minProperties", "schema" : { "minItems" : 2, "minProperties" : 1 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "minItems + patternProperties", "schema" : { "minItems" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "minItems + properties", "schema" : { "minItems" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "minItems + propertyNames", "schema" : { "minItems" : 2, "propertyNames" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "minItems + required", "schema" : { "minItems" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "minItems + unevaluatedProperties", "schema" : { "minItems" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + prefixItems", "schema" : { "additionalProperties" : false, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + prefixItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + prefixItems", "schema" : { "dependentSchemas" : { "x" : false }, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + prefixItems", "schema" : { "maxProperties" : 1, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + prefixItems", "schema" : { "minProperties" : 1, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "patternProperties + prefixItems", "schema" : { "patternProperties" : { "no" : false, "yes" : true }, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "prefixItems + properties", "schema" : { "prefixItems" : [ false ], "properties" : { "x" : false } }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "prefixItems + propertyNames", "schema" : { "prefixItems" : [ false ], "propertyNames" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "prefixItems + required", "schema" : { "prefixItems" : [ false ], "required" : [ "x" ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "prefixItems + unevaluatedProperties", "schema" : { "prefixItems" : [ false ], "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + unevaluatedItems", "schema" : { "additionalProperties" : false, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + unevaluatedItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + unevaluatedItems", "schema" : { "dependentSchemas" : { "x" : false }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + unevaluatedItems", "schema" : { "maxProperties" : 1, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + unevaluatedItems", "schema" : { "minProperties" : 1, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "patternProperties + unevaluatedItems", "schema" : { "patternProperties" : { "no" : false, "yes" : true }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "properties + unevaluatedItems", "schema" : { "properties" : { "x" : false }, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "propertyNames + unevaluatedItems", "schema" : { "propertyNames" : false, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "required + unevaluatedItems", "schema" : { "required" : [ "x" ], "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "unevaluatedItems + unevaluatedProperties", "schema" : { "unevaluatedItems" : false, "unevaluatedProperties" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + uniqueItems", "schema" : { "additionalProperties" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + uniqueItems", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + uniqueItems", "schema" : { "dependentSchemas" : { "x" : false }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + uniqueItems", "schema" : { "maxProperties" : 1, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + uniqueItems", "schema" : { "minProperties" : 1, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "patternProperties + uniqueItems", "schema" : { "patternProperties" : { "no" : false, "yes" : true }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "properties + uniqueItems", "schema" : { "properties" : { "x" : false }, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "propertyNames + uniqueItems", "schema" : { "propertyNames" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "required + uniqueItems", "schema" : { "required" : [ "x" ], "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "unevaluatedProperties + uniqueItems", "schema" : { "unevaluatedProperties" : false, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "contains + maxLength", "schema" : { "contains" : true, "maxLength" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "contains + minLength", "schema" : { "contains" : true, "minLength" : 2 }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "contains + pattern", "schema" : { "contains" : true, "pattern" : "hi" }, "tests" : [ { "data" : [], "description" : "array, contains invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, contains valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "items + maxLength", "schema" : { "items" : false, "maxLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "items + minLength", "schema" : { "items" : false, "minLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "items + pattern", "schema" : { "items" : false, "pattern" : "hi" }, "tests" : [ { "data" : [ 1 ], "description" : "array, items invalid", "valid" : false }, { "data" : [], "description" : "array, items valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxItems + maxLength", "schema" : { "maxItems" : 1, "maxLength" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maxItems + minLength", "schema" : { "maxItems" : 1, "minLength" : 2 }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maxItems + pattern", "schema" : { "maxItems" : 1, "pattern" : "hi" }, "tests" : [ { "data" : [ 1, 2 ], "description" : "array, maxItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, maxItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minItems", "schema" : { "maxLength" : 2, "minItems" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minItems + minLength", "schema" : { "minItems" : 2, "minLength" : 2 }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minItems + pattern", "schema" : { "minItems" : 2, "pattern" : "hi" }, "tests" : [ { "data" : [ 1 ], "description" : "array, minItems invalid", "valid" : false }, { "data" : [ 1, 2 ], "description" : "array, minItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + prefixItems", "schema" : { "maxLength" : 2, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + prefixItems", "schema" : { "minLength" : 2, "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + prefixItems", "schema" : { "pattern" : "hi", "prefixItems" : [ false ] }, "tests" : [ { "data" : [ 1 ], "description" : "array, prefixItems invalid", "valid" : false }, { "data" : [], "description" : "array, prefixItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + unevaluatedItems", "schema" : { "maxLength" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + unevaluatedItems", "schema" : { "minLength" : 2, "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + unevaluatedItems", "schema" : { "pattern" : "hi", "unevaluatedItems" : false }, "tests" : [ { "data" : [ 1 ], "description" : "array, unevaluatedItems invalid", "valid" : false }, { "data" : [], "description" : "array, unevaluatedItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + uniqueItems", "schema" : { "maxLength" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + uniqueItems", "schema" : { "minLength" : 2, "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + uniqueItems", "schema" : { "pattern" : "hi", "uniqueItems" : true }, "tests" : [ { "data" : [ 1, 1 ], "description" : "array, uniqueItems invalid", "valid" : false }, { "data" : [ 1 ], "description" : "array, uniqueItems valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "additionalProperties + exclusiveMaximum", "schema" : { "additionalProperties" : false, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + exclusiveMaximum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + exclusiveMaximum", "schema" : { "dependentSchemas" : { "x" : false }, "exclusiveMaximum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxProperties", "schema" : { "exclusiveMaximum" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minProperties", "schema" : { "exclusiveMaximum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + patternProperties", "schema" : { "exclusiveMaximum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + properties", "schema" : { "exclusiveMaximum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + propertyNames", "schema" : { "exclusiveMaximum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + required", "schema" : { "exclusiveMaximum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + unevaluatedProperties", "schema" : { "exclusiveMaximum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + exclusiveMinimum", "schema" : { "additionalProperties" : false, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + exclusiveMinimum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + exclusiveMinimum", "schema" : { "dependentSchemas" : { "x" : false }, "exclusiveMinimum" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxProperties", "schema" : { "exclusiveMinimum" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minProperties", "schema" : { "exclusiveMinimum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + patternProperties", "schema" : { "exclusiveMinimum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + properties", "schema" : { "exclusiveMinimum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + propertyNames", "schema" : { "exclusiveMinimum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + required", "schema" : { "exclusiveMinimum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + unevaluatedProperties", "schema" : { "exclusiveMinimum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + maximum", "schema" : { "additionalProperties" : false, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + maximum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + maximum", "schema" : { "dependentSchemas" : { "x" : false }, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + maximum", "schema" : { "maxProperties" : 1, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "maximum + minProperties", "schema" : { "maximum" : 2, "minProperties" : 1 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "maximum + patternProperties", "schema" : { "maximum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "maximum + properties", "schema" : { "maximum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "maximum + propertyNames", "schema" : { "maximum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "maximum + required", "schema" : { "maximum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "maximum + unevaluatedProperties", "schema" : { "maximum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + minimum", "schema" : { "additionalProperties" : false, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + minimum", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + minimum", "schema" : { "dependentSchemas" : { "x" : false }, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + minimum", "schema" : { "maxProperties" : 1, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + minimum", "schema" : { "minProperties" : 1, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "minimum + patternProperties", "schema" : { "minimum" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "minimum + properties", "schema" : { "minimum" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "minimum + propertyNames", "schema" : { "minimum" : 2, "propertyNames" : false }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "minimum + required", "schema" : { "minimum" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "minimum + unevaluatedProperties", "schema" : { "minimum" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "additionalProperties + multipleOf", "schema" : { "additionalProperties" : false, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true } ] }, { "description" : "dependentRequired + multipleOf", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true } ] }, { "description" : "dependentSchemas + multipleOf", "schema" : { "dependentSchemas" : { "x" : false }, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true } ] }, { "description" : "maxProperties + multipleOf", "schema" : { "maxProperties" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true } ] }, { "description" : "minProperties + multipleOf", "schema" : { "minProperties" : 1, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true } ] }, { "description" : "multipleOf + patternProperties", "schema" : { "multipleOf" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true } ] }, { "description" : "multipleOf + properties", "schema" : { "multipleOf" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true } ] }, { "description" : "multipleOf + propertyNames", "schema" : { "multipleOf" : 2, "propertyNames" : false }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true } ] }, { "description" : "multipleOf + required", "schema" : { "multipleOf" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true } ] }, { "description" : "multipleOf + unevaluatedProperties", "schema" : { "multipleOf" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + maxLength", "schema" : { "exclusiveMaximum" : 2, "maxLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + minLength", "schema" : { "exclusiveMaximum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "exclusiveMaximum + pattern", "schema" : { "exclusiveMaximum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMaximum invalid", "valid" : false }, { "data" : 1, "description" : "number, exclusiveMaximum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + maxLength", "schema" : { "exclusiveMinimum" : 2, "maxLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + minLength", "schema" : { "exclusiveMinimum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "exclusiveMinimum + pattern", "schema" : { "exclusiveMinimum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 2, "description" : "number, exclusiveMinimum invalid", "valid" : false }, { "data" : 3, "description" : "number, exclusiveMinimum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + maximum", "schema" : { "maxLength" : 2, "maximum" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maximum + minLength", "schema" : { "maximum" : 2, "minLength" : 2 }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maximum + pattern", "schema" : { "maximum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 3, "description" : "number, maximum invalid", "valid" : false }, { "data" : 2, "description" : "number, maximum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minimum", "schema" : { "maxLength" : 2, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + minimum", "schema" : { "minLength" : 2, "minimum" : 2 }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minimum + pattern", "schema" : { "minimum" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 1, "description" : "number, minimum invalid", "valid" : false }, { "data" : 2, "description" : "number, minimum valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + multipleOf", "schema" : { "maxLength" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + multipleOf", "schema" : { "minLength" : 2, "multipleOf" : 2 }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "multipleOf + pattern", "schema" : { "multipleOf" : 2, "pattern" : "hi" }, "tests" : [ { "data" : 1, "description" : "number, multipleOf invalid", "valid" : false }, { "data" : 2, "description" : "number, multipleOf valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "additionalProperties + maxLength", "schema" : { "additionalProperties" : false, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "additionalProperties + minLength", "schema" : { "additionalProperties" : false, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "additionalProperties + pattern", "schema" : { "additionalProperties" : false, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, additionalProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, additionalProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "dependentRequired + maxLength", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "dependentRequired + minLength", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "dependentRequired + pattern", "schema" : { "dependentRequired" : { "x" : [ "y" ] }, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentRequired invalid", "valid" : false }, { "data" : { "x" : 1, "y" : 2 }, "description" : "object, dependentRequired valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "dependentSchemas + maxLength", "schema" : { "dependentSchemas" : { "x" : false }, "maxLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "dependentSchemas + minLength", "schema" : { "dependentSchemas" : { "x" : false }, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "dependentSchemas + pattern", "schema" : { "dependentSchemas" : { "x" : false }, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, dependentSchemas invalid", "valid" : false }, { "data" : {}, "description" : "object, dependentSchemas valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + maxProperties", "schema" : { "maxLength" : 2, "maxProperties" : 1 }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "maxProperties + minLength", "schema" : { "maxProperties" : 1, "minLength" : 2 }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "maxProperties + pattern", "schema" : { "maxProperties" : 1, "pattern" : "hi" }, "tests" : [ { "data" : { "x" : 1, "y" : 2 }, "description" : "object, maxProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, maxProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + minProperties", "schema" : { "maxLength" : 2, "minProperties" : 1 }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + minProperties", "schema" : { "minLength" : 2, "minProperties" : 1 }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "minProperties + pattern", "schema" : { "minProperties" : 1, "pattern" : "hi" }, "tests" : [ { "data" : {}, "description" : "object, minProperties invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, minProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + patternProperties", "schema" : { "maxLength" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + patternProperties", "schema" : { "minLength" : 2, "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + patternProperties", "schema" : { "pattern" : "hi", "patternProperties" : { "no" : false, "yes" : true } }, "tests" : [ { "data" : { "no" : 1 }, "description" : "object, patternProperties invalid", "valid" : false }, { "data" : { "yes" : 1 }, "description" : "object, patternProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + properties", "schema" : { "maxLength" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + properties", "schema" : { "minLength" : 2, "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + properties", "schema" : { "pattern" : "hi", "properties" : { "x" : false } }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, properties invalid", "valid" : false }, { "data" : {}, "description" : "object, properties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + propertyNames", "schema" : { "maxLength" : 2, "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + propertyNames", "schema" : { "minLength" : 2, "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + propertyNames", "schema" : { "pattern" : "hi", "propertyNames" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, propertyNames invalid", "valid" : false }, { "data" : {}, "description" : "object, propertyNames valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + required", "schema" : { "maxLength" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + required", "schema" : { "minLength" : 2, "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + required", "schema" : { "pattern" : "hi", "required" : [ "x" ] }, "tests" : [ { "data" : {}, "description" : "object, required invalid", "valid" : false }, { "data" : { "x" : 1 }, "description" : "object, required valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] }, { "description" : "maxLength + unevaluatedProperties", "schema" : { "maxLength" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, maxLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, maxLength valid", "valid" : true } ] }, { "description" : "minLength + unevaluatedProperties", "schema" : { "minLength" : 2, "unevaluatedProperties" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true }, { "data" : "x", "description" : "string, minLength invalid", "valid" : false }, { "data" : "hi", "description" : "string, minLength valid", "valid" : true } ] }, { "description" : "pattern + unevaluatedProperties", "schema" : { "pattern" : "hi", "unevaluatedProperties" : false }, "tests" : [ { "data" : { "x" : 1 }, "description" : "object, unevaluatedProperties invalid", "valid" : false }, { "data" : {}, "description" : "object, unevaluatedProperties valid", "valid" : true }, { "data" : "hello", "description" : "string, pattern invalid", "valid" : false }, { "data" : "hihi", "description" : "string, pattern valid", "valid" : true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/README000640 000766 000024 00000000121 15001302761 023372 0ustar00etherstaff000000 000000 Tests go here until/unless they are accepted by the main JSON-Schema-Test-Suite. JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/short-circuit.json000640 000766 000024 00000012715 15001302761 026220 0ustar00etherstaff000000 000000 [ { "description": "items (schema form) does not improperly short-circuit", "schema": { "items": { "multipleOf": 2 } }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "prefixItems does not improperly short-circuit", "schema": { "prefixItems": [ { "multipleOf": 2 }, { "multipleOf": 2 } ] }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "items does not improperly short-circuit", "schema": { "prefixItems": [ true ], "items": { "multipleOf": 2 } }, "tests": [ { "description": "single additional item is valid", "data": [ 1, 2 ], "valid": true }, { "description": "second additional item is invalid", "data": [ 1, 2, 1 ], "valid": false } ] }, { "description": "unevaluatedItems does not improperly short-circuit", "schema": { "unevaluatedItems": { "multipleOf": 2 } }, "tests": [ { "description": "single item is valid", "data": [ 2 ], "valid": true }, { "description": "second item is invalid", "data": [ 2, 1 ], "valid": false } ] }, { "description": "properties does not improperly short-circuit", "schema": { "properties": { "alpha": { "multipleOf": 2 }, "beta": { "multipleOf": 2 } } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "properties does not improperly short-circuit (boolean schemas)", "schema": { "properties": { "alpha": true, "beta": false } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "patternProperties does not improperly short-circuit", "schema": { "patternProperties": { "^a": { "multipleOf": 2 }, "^b": { "multipleOf": 2 } } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "patternProperties does not improperly short-circuit (boolean schemas)", "schema": { "patternProperties": { "^a": true, "^b": false } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "additionalProperties does not improperly short-circuit", "schema": { "additionalProperties": { "multipleOf": 2 } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] }, { "description": "unevaluatedProperties does not improperly short-circuit", "schema": { "unevaluatedProperties": { "multipleOf": 2 } }, "tests": [ { "description": "single property is valid", "data": { "alpha": 2 }, "valid": true }, { "description": "second property is invalid", "data": { "alpha": 2, "beta": 1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/optional/000770 000766 000024 00000000000 15001302761 024346 5ustar00etherstaff000000 000000 JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/integers.json000640 000766 000024 00000013141 15001302761 025233 0ustar00etherstaff000000 000000 [ { "description": "type checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "type": "integer" }, "tests": [ { "description": "beyond int64 lower boundary", "data": -9223372036854775809, "valid": true }, { "description": "int64 lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "beyond int32 lower boundary", "data": -2147483649, "valid": true }, { "description": "int32 lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "int32 upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond int32 upper boundary", "data": 2147483648, "valid": true }, { "description": "upper int64 boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond int64 upper boundary", "data": 9223372036854775808, "valid": true } ] }, { "description": "int32 range checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "minimum": -2147483648, "maximum": 2147483647 }, "tests": [ { "description": "beyond lower boundary", "data": -2147483649, "valid": false }, { "description": "lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond upper boundary", "data": 2147483648, "valid": false } ] }, { "description": "int64 range checks", "schema": { "$comment": "on some architectures, some of these values may decode to strings", "minimum": -9223372036854775808, "maximum": 9223372036854775807 }, "tests": [ { "description": "beyond lower boundary", "data": -9223372036854775809, "valid": false }, { "description": "lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "upper boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond upper boundary", "data": 9223372036854775808, "valid": false } ] }, { "description": "equality checks", "schema": { "$comment": "on some architectures, some of these values may json-decode to strings", "enum": [ -9223372036854775809, -9223372036854775808, -2147483649, -2147483648, 0, 2147483647, 2147483648, 9223372036854775807, 9223372036854775808 ] }, "tests": [ { "description": "beyond int64 lower boundary", "data": -9223372036854775809, "valid": true }, { "description": "int64 lower boundary", "data": -9223372036854775808, "valid": true }, { "description": "beyond int32 lower boundary", "data": -2147483649, "valid": true }, { "description": "int32 lower boundary", "data": -2147483648, "valid": true }, { "description": "zero", "data": 0, "valid": true }, { "description": "one", "data": 1, "valid": false }, { "description": "int32 upper boundary", "data": 2147483647, "valid": true }, { "description": "beyond int32 upper boundary", "data": 2147483648, "valid": true }, { "description": "int64 upper boundary", "data": 9223372036854775807, "valid": true }, { "description": "beyond int64 upper boundary", "data": 9223372036854775808, "valid": true } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/format-ipv4.json000640 000766 000024 00000002150 15001302761 025561 0ustar00etherstaff000000 000000 [ { "description": "validation of ipv4 strings", "schema": { "format": "ipv4" }, "tests": [ { "description": "leading zeroes should be rejected, as they are treated as octal values", "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/", "data": "087.10.0.1", "valid": false }, { "description": "leading zeroes should be rejected, even if not a valid octal value", "data": "099.10.0.1", "valid": false }, { "description": "value without leading zero is valid", "data": "87.10.0.1", "valid": true }, { "description": "non-ascii digits should be rejected", "data": "1২7.0.0.1", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/format-relative-json-pointer.json000640 000766 000024 00000002653 15001302761 031147 0ustar00etherstaff000000 000000 [ { "description": "validation of relative-json-pointer strings", "schema": {"format": "relative-json-pointer"}, "comment": "see https://json-schema.org/draft/2020-12/relative-json-pointer.html#rfc.section.3", "tests": [ { "description": "non-negative-integer with empty json-pointer", "data": "0", "valid": true }, { "description": "non-negative-integer with one-char json-pointer", "data": "0/", "valid": true }, { "description": "non-negative-integer with longer json-pointer", "data": "0/a", "valid": true }, { "description": "non-negative-integer #", "data": "0#", "valid": true }, { "description": "## is not a valid json-pointer", "data": "0##", "valid": false }, { "description": "zero cannot be followed by other digits, plus json-pointer", "data": "01/a", "valid": false }, { "description": "zero cannot be followed by other digits, plus octothorpe", "data": "01#", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/faux-buggy-schemas.json000640 000766 000024 00000002446 15001302761 027120 0ustar00etherstaff000000 000000 [ { "description": "identifiers in non-schemas that look like schemas", "schema": { "$id": "http://localhost:1234/faux.json", "anyOf": [ { "type": "integer", "examples": [ { "$id": "faux.json#/foo/bar" }, { "$anchor": "my$foo" } ] }, { "type": "integer", "default": { "$anchor": "not$an$anchor" } }, { "$anchor": "my_const", "const": { "$anchor": "my_const" } }, { "$id": "enum.json", "$anchor": "my_enum", "enum": [ { "$id": "enum.json" }, { "$anchor": "my_enum" } ] } ] }, "tests": [ { "description": "match the type checks", "data": 1, "valid": true }, { "description": "match none of the options", "data": "a string", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/id.json000640 000766 000024 00000003462 15001302761 024014 0ustar00etherstaff000000 000000 [ { "description": "non-schema object containing an $id property", "schema": { "$defs": { "const_not_id": { "const": { "$id": "not_a_real_id" } } }, "if": { "const": "skip not_a_real_id" }, "then": true, "else" : { "$ref": "#/$defs/const_not_id" } }, "tests": [ { "description": "skip traversing definition for a valid result", "data": "skip not_a_real_id", "valid": true }, { "description": "const at const_not_id does not match", "data": 1, "valid": false } ] }, { "description": "'' not permitted as $id", "schema": { "$id": "", "type": "string" }, "tests": [ { "description": "schema would pass without $id", "data": "foo", "valid": false }, { "description": "schema would fail even without $id", "data": 1, "valid": false } ] }, { "description": "'#' not permitted as $id", "schema": { "$id": "#", "type": "string" }, "tests": [ { "description": "schema would pass without $id", "data": "foo", "valid": false }, { "description": "schema would fail even without $id", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/format-duration.json000640 000766 000024 00000003540 15001302761 026530 0ustar00etherstaff000000 000000 [ { "description": "validation of duration strings", "schema": {"format": "duration"}, "comment": "see https://en.wikipedia.org/wiki/ISO_8601#Durations for examples and clarifications", "tests": [ { "description": "years and months can appear without days", "data": "P1Y2M", "valid": true }, { "description": "months and days can appear without years", "data": "P1M2D", "valid": true }, { "description": "hours and minutes can appear without seconds", "data": "PT1H2M", "valid": true }, { "description": "minutes and seconds can appear without hour", "data": "PT1M2S", "valid": true }, { "description": "elements may be omitted if their value is zero", "data": "PT1H1S", "valid": true }, { "description": "half a year duration, with full stop decimal point", "comment": "ISO 8601 revision in year 2000 added decimals", "data": "P0.5Y", "valid": true }, { "description": "half a year duration, with comma decimal point", "data": "P0,5Y", "valid": true }, { "description": "only one unit can have a non-integer quantity", "data": "P0.5Y2.1M", "valid": false }, { "description": "only the smallest unit can have a non-integer quantity", "data": "P0.5Y2M", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/recursive-dynamic.json000640 000766 000024 00000002535 15001302761 027051 0ustar00etherstaff000000 000000 [ { "description": "dynamic $dynamicRef destination (not predictable at schema compile time)", "schema": { "$id": "main.json", "$defs": { "inner": { "$id": "inner.json", "$dynamicAnchor": "hello", "title": "inner", "additionalProperties": { "$dynamicRef": "#hello" } } }, "if": { "propertyNames": { "pattern": "^[a-m]" } }, "then": { "title": "any type of node", "$id": "anyLeafNode.json", "$dynamicAnchor": "hello", "$ref": "main.json#/$defs/inner" }, "else": { "title": "integer node", "$id": "integerNode.json", "$dynamicAnchor": "hello", "type": [ "object", "integer" ], "$ref": "main.json#/$defs/inner" } }, "tests": [ { "description": "numeric node", "data": { "alpha": 1.1 }, "valid": true }, { "description": "integer node", "data": { "november": 1.1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/ref.json000640 000766 000024 00000012025 15001302761 024167 0ustar00etherstaff000000 000000 [ { "description": "order of evaluation: $id and $ref", "schema": { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "/ref1/base.json", "$ref": "int.json", "$defs": { "bigint": { "$comment": "canonical uri: /ref1/int.json", "$id": "int.json", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /int.json", "$id": "/int.json", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "order of evaluation: $id and $anchor and $ref", "schema": { "$comment": "$id must be evaluated before $ref to get the proper $ref destination", "$id": "/ref2/base.json", "$ref": "#bigint", "$defs": { "bigint": { "$comment": "canonical uri: /ref2/base.json#/$defs/bigint; another valid uri for this location: /ref2/base.json#bigint", "$anchor": "bigint", "maximum": 10 }, "smallint": { "$comment": "canonical uri: /ref2; other valid uris for this location: /ref2/#bigint, /refs2/base.json#/$defs/smallint", "$id": "/ref2/", "$anchor": "bigint", "maximum": 2 } } }, "tests": [ { "description": "data is valid against first definition", "data": 5, "valid": true }, { "description": "data is invalid against first definition", "data": 50, "valid": false } ] }, { "description": "naive replacement of $ref with its destination is not correct", "schema": { "$defs": { "a_string": { "type": "string" } }, "enum": [ { "$ref": "#/$defs/a_string" } ] }, "tests": [ { "description": "do not evaluate the $ref inside the enum", "data": "this is a string", "valid": false }, { "description": "match the enum exactly", "data": { "$ref": "#/$defs/a_string" }, "valid": true } ] }, { "description": "invalid $ref: invalid anchor fragment", "schema": { "$defs": { "foo": { "$ref": "https://foo.com/bar.json#-not-an-anchor" } } }, "tests": [ { "description": "an invalid anchor fragment", "data": 1, "valid": false } ] }, { "description": "invalid $ref: invalid json-pointer fragment", "schema": { "$defs": { "foo": { "$ref": "https://foo.com/bar.json#/~2/not/json/pointer" } } }, "tests": [ { "description": "an invalid json-pointer fragment", "data": 1, "valid": false } ] }, { "description": "base URI change - change folder in subschema with path from root", "comment": "based on test suite draft7/refRemote.json 'base URI change - change folder in subschema'", "schema": { "$id": "http://localhost:1234/scope_change_defs.json", "type" : "object", "properties": { "list": { "$ref": "#/$defs/baz/$defs/bar" } }, "$defs": { "folderInteger": { "$id": "baseUriChangeFolderInSubschema/folderInteger.json", "type": "integer" }, "baz": { "$id": "baseUriChangeFolderInSubschema/", "$defs": { "bar": { "type": "array", "items": { "$ref": "folderInteger.json" } } } } } }, "tests": [ { "description": "number is valid", "data": {"list": [1]}, "valid": true }, { "description": "string is invalid", "data": {"list": ["a"]}, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/annotation-collection.json000640 000766 000024 00000006212 15001302761 027717 0ustar00etherstaff000000 000000 [ { "description": "annotations come from an allOf, unevaluated is a sibling", "schema": { "allOf": [ { "properties": { "foo": true } } ], "unevaluatedProperties": false }, "tests": [ { "description": "properties evaluated from allOf", "data": { "foo": 1 }, "valid": true }, { "description": "extra property", "data": { "bar": 1 }, "valid": false } ] }, { "description": "annotations from adjacent keywords, unevaluated is a sibling", "schema": { "properties": { "foo": true }, "unevaluatedProperties": false }, "tests": [ { "description": "properties evaluated from additionalProperties", "data": { "foo": 1 }, "valid": true }, { "description": "extra property", "data": { "bar": 1 }, "valid": false } ] }, { "description": "annotations come from an allOf, unevaluated is in an allOf", "schema": { "allOf": [ { "properties": { "foo": true } } ], "anyOf": [ { "unevaluatedProperties": false } ] }, "tests": [ { "description": "properties evaluated from allOf, but unevaluated is buried", "data": { "foo": 1 }, "valid": false }, { "description": "no properties", "data": {}, "valid": true } ] }, { "description": "annotations from adjacent keywords, unevaluated is in an allOf", "schema": { "anyOf": [ { "properties": { "foo": true } } ], "allOf": [ { "unevaluatedProperties": false } ] }, "tests": [ { "description": "properties evaluated from additionalProperties, but unevaluated is buried", "data": { "foo": 1 }, "valid": false }, { "description": "no properties", "data": {}, "valid": true } ] }, { "description": "collect annotations inside a 'not', even if collection is disabled", "schema": { "not": { "$comment": "this subschema must still produce annotations internally, even though the 'not' will ultimately discard them", "anyOf": [ true, { "properties": { "foo": true } } ], "unevaluatedProperties": false } }, "tests": [ { "description": "unevaluated property", "data": { "bar": 1 }, "valid": true }, { "description": "annotations are still collected inside a 'not'", "data": { "foo": 1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/dynamicRef.json000640 000766 000024 00000007643 15001302761 025506 0ustar00etherstaff000000 000000 [ { "description": "after leaving a dynamic scope, it should not be used by a $dynamicRef", "schema": { "$id": "main", "if": { "$id": "first_scope", "$defs": { "thingy": { "$comment": "this is first_scope#thingy", "$dynamicAnchor": "thingy", "type": "number" } } }, "then": { "$id": "second_scope", "$ref": "start", "$defs": { "thingy": { "$comment": "this is second_scope#thingy, the final destination of the $dynamicRef", "$dynamicAnchor": "thingy", "type": "null" } } }, "$defs": { "start": { "$comment": "this is the landing spot from $ref", "$id": "start", "$dynamicRef": "inner_scope#thingy" }, "thingy": { "$comment": "this is the first stop by the $dynamicRef", "$id": "inner_scope", "$dynamicAnchor": "thingy", "type": "string" } } }, "tests": [ { "description": "string matches /$defs/thingy, but the $dynamicRef does not stop here", "data": "a string", "valid": false }, { "description": "first_scope is not in dynamic scope for the $dynamicRef", "data": 42, "valid": false }, { "description": "value validates against /then/$defs/thingy, the final stop for the $dynamicRef", "data": null, "valid": true } ] }, { "description": "schema in dynamic scope must have a $dynamicAnchor that matches the fragment", "schema": { "$defs": { "enhanced": { "$comment": "a matching $anchor is not sufficient: it must be a $dynamicAnchor", "$anchor": "thingy", "$dynamicAnchor": "something_else", "minimum": 10 }, "orig": { "$id": "orig", "$dynamicAnchor": "thingy", "minimum": 2 } }, "$dynamicRef": "orig#thingy" }, "tests": [ { "description": "value would fail under either subschema", "data": 1, "valid": false }, { "description": "$anchor does not match $dynamicAnchor; original subschema is used", "data": 5, "valid": true } ] }, { "description": "$dynamicRef points to a boolean schema", "schema": { "$comment": "submitted as https://github.com/json-schema-org/JSON-Schema-Test-Suite/pull/701", "$schema": "https://json-schema.org/draft/2020-12/schema", "$defs": { "true": true, "false": false }, "properties": { "true": { "$dynamicRef": "#/$defs/true" }, "false": { "$dynamicRef": "#/$defs/false" } } }, "tests": [ { "description": "follow $dynamicRef to a true schema", "data": { "true": 1 }, "valid": true }, { "description": "follow $dynamicRef to a false schema", "data": { "false": 1 }, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/optional/format-date-time.json000640 000766 000024 00000001646 15001302761 030406 0ustar00etherstaff000000 000000 [ { "description": "leap seconds", "schema": { "format": "date-time" }, "tests": [ { "description": "a valid date-time with a leap second, UTC", "data": "1998-12-31T23:59:60Z", "valid": true }, { "description": "a valid date-time with a leap second, with minus offset", "data": "1998-12-31T15:59:60.123-08:00", "valid": true }, { "description": "an invalid date-time with a leap second, UTC", "data": "1998-12-30T23:59:60Z", "valid": false }, { "description": "an invalid date-time with a leap second, with minus offset", "data": "1998-12-30T15:59:60.123-08:00", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/optional/format-time.json000640 000766 000024 00000004770 15001302761 027474 0ustar00etherstaff000000 000000 [ { "description": "validation of time strings", "schema": { "format": "time" }, "comment": "see https://tools.ietf.org/html/rfc3339#section-5.6 and https://tools.ietf.org/html/rfc3339#appendix-D", "tests": [ { "description": "valid leap second, Zulu", "data": "23:59:60Z", "valid": true }, { "description": "invalid leap second, Zulu (wrong hour)", "data": "22:59:60Z", "valid": false }, { "description": "invalid leap second, Zulu (wrong minute)", "data": "23:58:60Z", "valid": false }, { "description": "valid leap second, positive time-offset", "data": "01:29:60+01:30", "valid": true }, { "description": "valid leap second, large positive time-offset", "data": "23:29:60+23:30", "valid": true }, { "description": "invalid leap second, positive time-offset (wrong hour)", "data": "23:59:60+01:00", "valid": false }, { "description": "invalid leap second, positive time-offset (wrong minute)", "data": "23:59:60+00:30", "valid": false }, { "description": "valid leap second, negative time-offset", "data": "22:29:60-01:30", "valid": true }, { "description": "valid leap second, large negative time-offset", "data": "00:29:60-23:30", "valid": true }, { "description": "invalid leap second, negative time-offset (wrong hour)", "data": "23:59:60-01:00", "valid": false }, { "description": "invalid leap second, negative time-offset (wrong minute)", "data": "23:59:60-00:30", "valid": false }, { "description": "no time offset", "data": "12:00:00", "valid": false }, { "description": "non-ascii digits should be rejected", "data": "1২:00:00Z", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/additional-tests-draft2020-12/optional/format-date.json000640 000766 000024 00000000524 15001302761 027444 0ustar00etherstaff000000 000000 [ { "description": "non-ascii digits should be rejected", "schema": { "format": "date" }, "tests": [ { "description": "BENGALI DIGIT TWO is not valid in dates", "data": "২001-01-01", "valid": false } ] } ] JSON-Schema-Modern-0.609/t/invalid-schemas/vocabulary.json000640 000766 000024 00000003174 15001302761 023544 0ustar00etherstaff000000 000000 [ { "description": "invalid $vocabulary: need boolean values", "schema": { "$vocabulary": { "https://foo.com": "not-bool" } }, "tests": [ { "description": "an invalid $vocabulary value (value should be a boolean)", "data": 1, "valid": false } ] }, { "description": "invalid $vocabulary: uri must not have unencoded characters", "comment": "when properly encoded, this would be http://foo%5Ebar/path", "schema": { "$vocabulary": { "http://foo^bar/path": false } }, "tests": [ { "description": "unencoded characters in $vocabulary uri", "data": 1, "valid": false } ] }, { "description": "invalid $vocabulary: non-ascii characters in uri", "schema": { "$vocabulary": { "https://ಠ_ಠ.com": false } }, "tests": [ { "description": "non-ascii characters in $vocabulary uri", "data": 1, "valid": false } ] }, { "description": "invalid $vocabulary: missing scheme", "schema": { "$vocabulary": { "foo.com": false } }, "tests": [ { "description": "$vocabulary uri is missing a scheme", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/invalid-schemas/invalid-input.json000640 000766 000024 00000001203 15001302761 024147 0ustar00etherstaff000000 000000 [ { "description": "invalid user input: type (string)", "schema": { "type": null }, "tests": [ { "description": "graceful error from 'type' (string)", "data": 1, "valid": false } ] }, { "description": "invalid user input: type (array)", "schema": { "type": [ "string", null ] }, "tests": [ { "description": "graceful error from 'type' (array)", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/t/invalid-schemas/ref.json000640 000766 000024 00000001626 15001302761 022151 0ustar00etherstaff000000 000000 [ { "description": "invalid $ref: unencoded characters", "comment": "when properly encoded, this would be http://foo%5Ebar/path", "schema": { "$defs": { "foo": { "$ref": "http://foo^bar/path" } } }, "tests": [ { "description": "unencoded characters in $ref uri", "data": 1, "valid": false } ] }, { "description": "invalid $ref: non-ascii characters", "schema": { "$defs": { "foo": { "$ref": "https://ಠ_ಠ.com" } } }, "tests": [ { "description": "non-ascii characters in $ref uri", "data": 1, "valid": false } ] } ] JSON-Schema-Modern-0.609/inc/CheckConflicts.pm000640 000766 000024 00000001752 15001302761 021161 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strict; use warnings; package inc::CheckConflicts; use Moose; with 'Dist::Zilla::Role::InstallTool'; sub setup_installer { my $self = shift; my @mfpl = grep +($_->name eq 'Makefile.PL' or $_->name eq 'Build.PL'), $self->zilla->files->@*; $self->log_fatal('No Makefile.PL or Build.PL was found.') if not @mfpl; foreach my $file (@mfpl) { $self->log_debug([ 'munging %s in setup_installer phase', $file->name ]); my $orig_content = $file->content; $self->log_fatal('could not find position in ' . $file->name . ' to modify!') if not $orig_content =~ m/use strict;\nuse warnings;\n\n/g; my $pos = pos($orig_content); my $content = <<'CONTENT'; if ("$]" < 5.038) { die "This distribution will not install where builtin::Backport exists.\n" if eval { +require builtin::Backport; 1 }; } CONTENT $file->content(substr($orig_content, 0, $pos) . $content . substr($orig_content, $pos)); } return; } 1; JSON-Schema-Modern-0.609/inc/AppendSection.pm000640 000766 000024 00000002635 15001302761 021034 0ustar00etherstaff000000 000000 # vim: set ft=perl ts=8 sts=2 sw=2 tw=100 et : use strict; use warnings; package inc::AppendSection; use Moose; extends 'Pod::Weaver::Section::AllowOverride'; # Warning: dirty hack ahead! # This chicanery is to allow for [GenerateSection] followed by [AllowOverride], # where the intention is the content added by that GenerateSection should be appended to the # original section that was generated by a weaver bundle (as opposed to appearing in the literal # .pm file). # This plugin does its initial work (to find the node to pluck out and later append) in # transform_document, and the Transformer phase is run before GenerateSection's weave_section # has a chance to create the node. # So this hack just runs the plugin's transform_document again if nothing was found the first time. # Because it picks the first node it finds (to relocate to the location of the second match), we # will now run GenerateSection first, before calling the weaver bundle. # All of this really should be replaced by a new plugin called something like AppendSection, # which subclasses GenerateSection to add the options provided by AllowOverride, letting us # append (or prepend) to an existing section rather than generating a new one. before weave_section => sub { my ($self, $document, $input) = @_; # if we haven't already found a matching section, look again now $self->transform_document($document) if not $self->_override_with; }; 1;