From 598846dbec737e03503a5afd8fa8de41931ecfca Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Tue, 11 Aug 2026 11:50:24 -0300 Subject: [PATCH 1/4] Make use of a Tag URI to identify a schema from standard input Signed-off-by: Juan Cruz Viotti --- docs/test.markdown | 4 +- src/command_bundle.cc | 8 +-- src/command_codegen.cc | 2 +- src/command_compile.cc | 3 +- src/command_inspect.cc | 6 +-- src/command_lint.cc | 2 +- src/command_rdf.cc | 4 +- src/command_test.cc | 7 ++- src/command_upgrade.cc | 15 +++--- src/command_validate.cc | 24 +++------ src/utils.h | 17 ++++-- test/CMakeLists.txt | 3 ++ test/bundle/pass_stdin_no_id.sh | 34 ++++++++++++ test/inspect/pass_stdin_no_id.sh | 18 +++---- test/test/pass_stdin_data_path.sh | 53 +++++++++++++++++++ test/validate/fail_stdin_relative_ref.sh | 43 +++++++++++++++ .../fail_stdin_schema_validation_no_id.sh | 2 +- 17 files changed, 195 insertions(+), 50 deletions(-) create mode 100755 test/bundle/pass_stdin_no_id.sh create mode 100755 test/test/pass_stdin_data_path.sh create mode 100755 test/validate/fail_stdin_relative_ref.sh diff --git a/docs/test.markdown b/docs/test.markdown index 332484127..824512aed 100644 --- a/docs/test.markdown +++ b/docs/test.markdown @@ -47,7 +47,9 @@ as JSON files that follow a specific format: into the resolution context using the `--resolve` or `--http` options_. If the `target` is relative, it will be interpreted as a file path relative - to the test file location. + to the test file location. A test document read from standard input has no + location of its own, so its relative paths are interpreted against the + current working directory instead. This property may also be set to a non-empty array of URIs, in which case every test case in `tests` will be run against each of the listed schemas diff --git a/src/command_bundle.cc b/src/command_bundle.cc index 71d75fbae..5e9588aff 100644 --- a/src/command_bundle.cc +++ b/src/command_bundle.cc @@ -32,14 +32,14 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options) throw sourcemeta::core::IOIsADirectoryError{schema_path}; } - const auto schema_resolution_base{ + const auto schema_config_base{ schema_from_stdin ? std::filesystem::current_path() : schema_path}; const auto schema_display_path{schema_from_stdin ? stdin_path() : schema_path}; - const auto configuration_path{find_configuration(schema_resolution_base)}; + const auto configuration_path{find_configuration(schema_config_base)}; const auto &configuration{ - read_configuration(options, configuration_path, schema_resolution_base)}; + read_configuration(options, configuration_path, schema_config_base)}; const auto dialect{default_dialect(options, configuration)}; auto parsed_schema{schema_from_stdin ? read_from_stdin() : read_file(schema_path)}; @@ -57,7 +57,7 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options) sourcemeta::blaze::bundle( schema, sourcemeta::blaze::schema_walker, custom_resolver, sourcemeta::blaze::BundleMode::NonOfficialMetaschemas, dialect, - sourcemeta::jsonschema::default_id(schema_resolution_base)); + sourcemeta::jsonschema::default_id(schema_path, schema_from_stdin)); if (options.contains("without-id")) { sourcemeta::jsonschema::LOG_WARNING() diff --git a/src/command_codegen.cc b/src/command_codegen.cc index ae5194419..3fd4bbebc 100644 --- a/src/command_codegen.cc +++ b/src/command_codegen.cc @@ -53,7 +53,7 @@ auto sourcemeta::jsonschema::codegen(const sourcemeta::core::Options &options) result = sourcemeta::blaze::compile( schema, sourcemeta::blaze::schema_walker, custom_resolver, sourcemeta::blaze::default_compiler, dialect, - sourcemeta::jsonschema::default_id(schema_path)); + sourcemeta::jsonschema::default_id(schema_path, false)); } catch (const sourcemeta::blaze::SchemaKeywordError &error) { throw sourcemeta::core::FileError( schema_path, error); diff --git a/src/command_compile.cc b/src/command_compile.cc index f0af0f931..65e438e3e 100644 --- a/src/command_compile.cc +++ b/src/command_compile.cc @@ -45,7 +45,8 @@ auto sourcemeta::jsonschema::compile(const sourcemeta::core::Options &options) const auto fast_mode{options.contains("fast")}; const auto &custom_resolver{ resolver(options, options.contains("http"), dialect, configuration)}; - const auto schema_default_id{sourcemeta::jsonschema::default_id(schema_path)}; + const auto schema_default_id{ + sourcemeta::jsonschema::default_id(schema_path, false)}; sourcemeta::blaze::Template schema_template; try { diff --git a/src/command_inspect.cc b/src/command_inspect.cc index 8da39ef09..b2e05bb4a 100644 --- a/src/command_inspect.cc +++ b/src/command_inspect.cc @@ -213,9 +213,9 @@ auto sourcemeta::jsonschema::inspect(const sourcemeta::core::Options &options) // Only use the file-based URI if the schema has no // identifier, as otherwise we make the output unnecessarily // hard when it comes to debugging schemas - !identifier.empty() - ? "" - : sourcemeta::jsonschema::default_id(schema_resolution_base)); + !identifier.empty() ? "" + : sourcemeta::jsonschema::default_id( + schema_resolution_base, schema_from_stdin)); } catch (const sourcemeta::blaze::SchemaKeywordError &error) { throw sourcemeta::core::FileError( schema_resolution_base, error); diff --git a/src/command_lint.cc b/src/command_lint.cc index 1b4c9ce63..b11b6a0d7 100644 --- a/src/command_lint.cc +++ b/src/command_lint.cc @@ -111,7 +111,7 @@ static auto get_lint_callback(sourcemeta::core::JSON &errors_array, errors_array.push_back(error_obj); } else { if (entry.from_stdin) { - std::cout << "/dev/stdin"; + std::cout << sourcemeta::jsonschema::stdin_path().string(); } else { std::cout << std::filesystem::relative(entry.resolution_base).string(); diff --git a/src/command_rdf.cc b/src/command_rdf.cc index b6e6a56bb..648c1129c 100644 --- a/src/command_rdf.cc +++ b/src/command_rdf.cc @@ -94,8 +94,8 @@ auto sourcemeta::jsonschema::rdf(const sourcemeta::core::Options &options) const auto &custom_resolver{ resolver(options, options.contains("http"), dialect, configuration)}; const auto fast_mode{options.contains("fast")}; - const auto schema_default_id{ - sourcemeta::jsonschema::default_id(schema_resolution_base)}; + const auto schema_default_id{sourcemeta::jsonschema::default_id( + schema_resolution_base, schema_from_stdin)}; const auto bundled{ bundle_for_evaluation(schema, custom_resolver, dialect, schema_default_id, diff --git a/src/command_test.cc b/src/command_test.cc index 07a72511f..10ec2d836 100644 --- a/src/command_test.cc +++ b/src/command_test.cc @@ -114,7 +114,12 @@ auto parse_test_suite(const sourcemeta::jsonschema::InputJSON &entry, -> sourcemeta::blaze::TestSuite { try { return sourcemeta::blaze::TestSuite::parse( - entry.second, entry.positions, entry.resolution_base.parent_path(), + entry.second, entry.positions, + // A test document read from standard input has no directory of its + // own, and the base path must remain a real directory, as relative + // `dataPath` and `rdfPath` entries are opened from it + entry.from_stdin ? std::filesystem::current_path() + : entry.resolution_base.parent_path(), schema_resolver, sourcemeta::blaze::schema_walker, sourcemeta::blaze::default_schema_compiler, dialect, "", tweaks); } catch (const sourcemeta::blaze::TestParseError &error) { diff --git a/src/command_upgrade.cc b/src/command_upgrade.cc index a873ad2ba..5ccac4369 100644 --- a/src/command_upgrade.cc +++ b/src/command_upgrade.cc @@ -66,14 +66,14 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options) throw sourcemeta::core::IOIsADirectoryError{schema_path}; } - const auto schema_resolution_base{ + const auto schema_config_base{ schema_from_stdin ? std::filesystem::current_path() : schema_path}; const auto schema_display_path{schema_from_stdin ? stdin_path() : schema_path}; - const auto configuration_path{find_configuration(schema_resolution_base)}; + const auto configuration_path{find_configuration(schema_config_base)}; const auto &configuration{ - read_configuration(options, configuration_path, schema_resolution_base)}; + read_configuration(options, configuration_path, schema_config_base)}; const auto dialect{default_dialect(options, configuration)}; auto parsed_schema{schema_from_stdin ? read_from_stdin() : read_file(schema_path)}; @@ -91,9 +91,9 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options) sourcemeta::blaze::SchemaFrame::Mode::Locations}; try { - frame.analyse(schema, sourcemeta::blaze::schema_walker, custom_resolver, - dialect, - sourcemeta::jsonschema::default_id(schema_resolution_base)); + frame.analyse( + schema, sourcemeta::blaze::schema_walker, custom_resolver, dialect, + sourcemeta::jsonschema::default_id(schema_path, schema_from_stdin)); } catch (const sourcemeta::blaze::SchemaKeywordError &error) { throw sourcemeta::core::FileError( schema_display_path, error); @@ -198,7 +198,8 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options) std::ignore = transformer.apply( schema, sourcemeta::blaze::schema_walker, custom_resolver, [](const auto &, const auto, const auto, const auto &, const auto) {}, - dialect, sourcemeta::jsonschema::default_id(schema_resolution_base), "", + dialect, + sourcemeta::jsonschema::default_id(schema_path, schema_from_stdin), "", options.contains("meta")); } diff --git a/src/command_validate.cc b/src/command_validate.cc index ff77334f9..31be266b7 100644 --- a/src/command_validate.cc +++ b/src/command_validate.cc @@ -140,8 +140,7 @@ auto process_entry( const sourcemeta::jsonschema::CustomResolver &custom_resolver, const sourcemeta::blaze::SchemaFrame &frame, bool benchmark, std::uint64_t benchmark_loop, bool trace, bool fast_mode, bool json_output, - bool continue_on_error, bool schema_from_stdin, - const std::filesystem::path &schema_resolution_base, + bool continue_on_error, const std::filesystem::path &schema_resolution_base, const sourcemeta::core::Options &options, bool &result) -> bool { std::ostringstream error; sourcemeta::blaze::SimpleOutput output{entry.second}; @@ -205,10 +204,7 @@ auto process_entry( } sourcemeta::jsonschema::LOG_VERBOSE(options) << "\n matches " - << (schema_from_stdin - ? "/dev/stdin" - : sourcemeta::core::weakly_canonical(schema_resolution_base) - .string()) + << sourcemeta::jsonschema::stdin_path_string(schema_resolution_base) << "\n"; sourcemeta::jsonschema::print_annotations(output, options, entry.positions, std::cerr); @@ -306,8 +302,8 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options) "Re-compile the template with --format-assertion instead"}; } - const auto schema_default_id{ - sourcemeta::jsonschema::default_id(schema_resolution_base)}; + const auto schema_default_id{sourcemeta::jsonschema::default_id( + schema_resolution_base, schema_from_stdin)}; const auto bundled{ bundle_for_evaluation(schema, custom_resolver, dialect, schema_default_id, @@ -374,8 +370,8 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options) for (const auto &entry : for_each_json({}, options)) { if (!process_entry(entry, evaluator, schema_template, custom_resolver, frame, benchmark, benchmark_loop, trace, fast_mode, - json_output, continue_on_error, schema_from_stdin, - schema_resolution_base, options, result)) { + json_output, continue_on_error, schema_resolution_base, + options, result)) { break; } } @@ -409,7 +405,7 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options) for (const auto &entry : for_each_json({instance_path_view}, options)) { if (!process_entry(entry, evaluator, schema_template, custom_resolver, frame, benchmark, benchmark_loop, trace, fast_mode, - json_output, continue_on_error, schema_from_stdin, + json_output, continue_on_error, schema_resolution_base, options, result)) { break; } @@ -474,11 +470,7 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options) LOG_VERBOSE(options) << "ok: " << sourcemeta::core::weakly_canonical(instance_path).string() - << "\n matches " - << (schema_from_stdin ? "/dev/stdin" - : sourcemeta::core::weakly_canonical( - schema_resolution_base) - .string()) + << "\n matches " << stdin_path_string(schema_resolution_base) << "\n"; print_annotations(output, options, tracker, std::cerr); } else { diff --git a/src/utils.h b/src/utils.h index 97f4fbae6..bc0e0969c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -33,8 +33,19 @@ namespace sourcemeta::jsonschema { -inline auto default_id(const std::filesystem::path &schema_path) - -> std::string { +// A schema read from standard input has no retrieval URI, so JSON Schema +// 2020-12 section 9.1.1 and RFC 3986 section 5.1.4 let us pick an +// implementation-specific default. We deliberately pick an RFC 4151 tag URI, +// as it cannot be mistaken for a locator the way a file URI can +constexpr std::string_view STDIN_DEFAULT_ID{ + "tag:sourcemeta.com,2026:jsonschema/stdin"}; + +inline auto default_id(const std::filesystem::path &schema_path, + const bool from_stdin) -> std::string { + if (from_stdin) { + return std::string{STDIN_DEFAULT_ID}; + } + return sourcemeta::core::URI::from_path( sourcemeta::core::weakly_canonical(schema_path)) .recompose(); @@ -70,7 +81,7 @@ inline auto resolve_relative_uri(const std::string &value, } inline auto default_id(const InputJSON &entry) -> std::string { - return default_id(entry.resolution_base); + return default_id(entry.resolution_base, entry.from_stdin); } inline auto resolve_entrypoint(const sourcemeta::blaze::SchemaFrame &frame, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c15e2d4fe..064c6d46d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -308,6 +308,7 @@ add_jsonschema_test_unix(validate/pass_stdin_schema) add_jsonschema_test_unix(validate/pass_stdin_schema_verbose) add_jsonschema_test_unix(validate/pass_stdin_schema_json) add_jsonschema_test_unix(validate/pass_stdin_schema_no_id) +add_jsonschema_test_unix(validate/fail_stdin_relative_ref) add_jsonschema_test_unix(validate/fail_stdin_schema_and_instance) add_jsonschema_test_unix(validate/fail_stdin_schema_validation) add_jsonschema_test_unix(validate/fail_stdin_schema_validation_no_id) @@ -491,6 +492,7 @@ add_jsonschema_test_unix(test/fail_anchor_collision) add_jsonschema_test_unix(test/pass_config_ignore) add_jsonschema_test_unix(test/pass_stdin) add_jsonschema_test_unix(test/pass_stdin_verbose) +add_jsonschema_test_unix(test/pass_stdin_data_path) add_jsonschema_test_unix(test/fail_stdin_invalid_json) add_jsonschema_test_unix(test/fail_stdin_not_object) add_jsonschema_test_unix(test/fail_stdin_test_failure) @@ -554,6 +556,7 @@ add_jsonschema_test_unix(bundle/pass_config_ignore) add_jsonschema_test_unix(bundle/pass_ref_in_bundled_resolves_against_id) add_jsonschema_test_unix(bundle/pass_resolve_deduplicate_embedded) add_jsonschema_test_unix(bundle/pass_stdin) +add_jsonschema_test_unix(bundle/pass_stdin_no_id) add_jsonschema_test_unix(bundle/fail_stdin_not_schema) add_jsonschema_test_unix(bundle/fail_stdin_invalid_json) add_jsonschema_test_unix(bundle/fail_invalid_header) diff --git a/test/bundle/pass_stdin_no_id.sh b/test/bundle/pass_stdin_no_id.sh new file mode 100755 index 000000000..e73ead7d8 --- /dev/null +++ b/test/bundle/pass_stdin_no_id.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} +EOF + +mkdir -p "$TMP/nested/deeper" + +# The identifier of a schema read from standard input must not depend on +# the directory the command happens to run from +(cd "$TMP" && "$1" bundle - < "$TMP/schema.json") > "$TMP/first.json" +(cd "$TMP/nested/deeper" && "$1" bundle - < "$TMP/schema.json") \ + > "$TMP/second.json" + +cat << 'EOF' > "$TMP/expected.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "tag:sourcemeta.com,2026:jsonschema/stdin", + "type": "string" +} +EOF + +diff "$TMP/first.json" "$TMP/expected.json" +diff "$TMP/second.json" "$TMP/expected.json" diff --git a/test/inspect/pass_stdin_no_id.sh b/test/inspect/pass_stdin_no_id.sh index 339794c75..5efbab8f4 100755 --- a/test/inspect/pass_stdin_no_id.sh +++ b/test/inspect/pass_stdin_no_id.sh @@ -15,12 +15,12 @@ cat << 'EOF' | "$1" inspect - > "$TMP/output.txt" 2>&1 EOF cat << EOF > "$TMP/expected.txt" -(RESOURCE) URI: file:///dev/stdin +(RESOURCE) URI: tag:sourcemeta.com,2026:jsonschema/stdin Type : Static - Root : file:///dev/stdin + Root : tag:sourcemeta.com,2026:jsonschema/stdin Pointer : File Position : 1:1 - Base : file:///dev/stdin + Base : tag:sourcemeta.com,2026:jsonschema/stdin Relative Pointer : Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema @@ -28,12 +28,12 @@ cat << EOF > "$TMP/expected.txt" Property Name : no Orphan : no -(POINTER) URI: file:///dev/stdin#/\$schema +(POINTER) URI: tag:sourcemeta.com,2026:jsonschema/stdin#/\$schema Type : Static - Root : file:///dev/stdin + Root : tag:sourcemeta.com,2026:jsonschema/stdin Pointer : /\$schema File Position : 2:3 - Base : file:///dev/stdin + Base : tag:sourcemeta.com,2026:jsonschema/stdin Relative Pointer : /\$schema Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema @@ -41,12 +41,12 @@ cat << EOF > "$TMP/expected.txt" Property Name : no Orphan : no -(POINTER) URI: file:///dev/stdin#/type +(POINTER) URI: tag:sourcemeta.com,2026:jsonschema/stdin#/type Type : Static - Root : file:///dev/stdin + Root : tag:sourcemeta.com,2026:jsonschema/stdin Pointer : /type File Position : 3:3 - Base : file:///dev/stdin + Base : tag:sourcemeta.com,2026:jsonschema/stdin Relative Pointer : /type Dialect : https://json-schema.org/draft/2020-12/schema Base Dialect : https://json-schema.org/draft/2020-12/schema diff --git a/test/test/pass_stdin_data_path.sh b/test/test/pass_stdin_data_path.sh new file mode 100755 index 000000000..626142fe4 --- /dev/null +++ b/test/test/pass_stdin_data_path.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com", + "type": "string" +} +EOF + +cat << 'EOF' > "$TMP/data-valid.json" +"Hello World" +EOF + +cat << 'EOF' > "$TMP/data-invalid.json" +{ "type": "Hello World" } +EOF + +cat << 'EOF' > "$TMP/test.json" +{ + "target": "https://example.com", + "tests": [ + { + "description": "First test", + "valid": true, + "dataPath": "./data-valid.json" + }, + { + "description": "Second test", + "valid": false, + "dataPath": "./data-invalid.json" + } + ] +} +EOF + +# A test document read from standard input has no directory of its own, so +# its relative data paths resolve against the working directory +(cd "$TMP" && "$1" test - --resolve "$TMP/schema.json" < "$TMP/test.json") \ + 1> "$TMP/output.txt" 2>&1 + +cat << 'EOF' > "$TMP/expected.txt" +/dev/stdin: PASS 2/2 +EOF + +diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/validate/fail_stdin_relative_ref.sh b/test/validate/fail_stdin_relative_ref.sh new file mode 100755 index 000000000..19bae3ac8 --- /dev/null +++ b/test/validate/fail_stdin_relative_ref.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/nested.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} +EOF + +cat << 'EOF' > "$TMP/instance.json" +"Hello World" +EOF + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$ref": "./nested.json" +} +EOF + +# A relative reference from standard input must fail loudly rather than +# quietly resolve against whatever sits in the working directory +(cd "$TMP" && "$1" validate - "$TMP/instance.json" < "$TMP/schema.json") \ + > "$TMP/output.txt" 2>&1 && EXIT_CODE="$?" || EXIT_CODE="$?" +# Schema input error +test "$EXIT_CODE" = "4" + +cat << 'EOF' > "$TMP/expected.txt" +error: Could not resolve the reference to an external schema + at identifier tag:sourcemeta.com,2026:jsonschema/nested.json + at file path /dev/stdin + +This is likely because you forgot to import such schema using `--resolve/-r` +EOF + +diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/validate/fail_stdin_schema_validation_no_id.sh b/test/validate/fail_stdin_schema_validation_no_id.sh index 3f8958196..8e128ab55 100755 --- a/test/validate/fail_stdin_schema_validation_no_id.sh +++ b/test/validate/fail_stdin_schema_validation_no_id.sh @@ -48,7 +48,7 @@ cat << 'EOF' > "$TMP/expected.txt" "errors": [ { "keywordLocation": "/type", - "absoluteKeywordLocation": "file:///dev/stdin#/type", + "absoluteKeywordLocation": "tag:sourcemeta.com,2026:jsonschema/stdin#/type", "instanceLocation": "", "instancePosition": [ 1, 1, 1, 3 ], "error": "The value was expected to be of type string but it was of type integer" From 82a5d1d3f3ac71048a6992001bd27d47942ab698 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Tue, 11 Aug 2026 11:58:28 -0300 Subject: [PATCH 2/4] More Signed-off-by: Juan Cruz Viotti --- test/CMakeLists.txt | 1 + test/bundle/pass_stdin_no_id.sh | 8 +-- test/bundle/pass_stdin_no_id_without_id.sh | 63 ++++++++++++++++++++++ test/test/pass_stdin_data_path.sh | 4 +- test/validate/fail_stdin_relative_ref.sh | 4 +- 5 files changed, 75 insertions(+), 5 deletions(-) create mode 100755 test/bundle/pass_stdin_no_id_without_id.sh diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 064c6d46d..38c5135fe 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -557,6 +557,7 @@ add_jsonschema_test_unix(bundle/pass_ref_in_bundled_resolves_against_id) add_jsonschema_test_unix(bundle/pass_resolve_deduplicate_embedded) add_jsonschema_test_unix(bundle/pass_stdin) add_jsonschema_test_unix(bundle/pass_stdin_no_id) +add_jsonschema_test_unix(bundle/pass_stdin_no_id_without_id) add_jsonschema_test_unix(bundle/fail_stdin_not_schema) add_jsonschema_test_unix(bundle/fail_stdin_invalid_json) add_jsonschema_test_unix(bundle/fail_invalid_header) diff --git a/test/bundle/pass_stdin_no_id.sh b/test/bundle/pass_stdin_no_id.sh index e73ead7d8..fdad8b5c5 100755 --- a/test/bundle/pass_stdin_no_id.sh +++ b/test/bundle/pass_stdin_no_id.sh @@ -18,9 +18,11 @@ mkdir -p "$TMP/nested/deeper" # The identifier of a schema read from standard input must not depend on # the directory the command happens to run from -(cd "$TMP" && "$1" bundle - < "$TMP/schema.json") > "$TMP/first.json" -(cd "$TMP/nested/deeper" && "$1" bundle - < "$TMP/schema.json") \ - > "$TMP/second.json" +cd "$TMP" +"$1" bundle - < "$TMP/schema.json" > "$TMP/first.json" + +cd "$TMP/nested/deeper" +"$1" bundle - < "$TMP/schema.json" > "$TMP/second.json" cat << 'EOF' > "$TMP/expected.json" { diff --git a/test/bundle/pass_stdin_no_id_without_id.sh b/test/bundle/pass_stdin_no_id_without_id.sh new file mode 100755 index 000000000..82c945e46 --- /dev/null +++ b/test/bundle/pass_stdin_no_id_without_id.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/other.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/other", + "type": "string" +} +EOF + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "local": { "$ref": "#/$defs/string" }, + "anchored": { "$ref": "#thing" }, + "external": { "$ref": "https://example.com/other" } + }, + "$defs": { + "string": { "$anchor": "thing", "type": "string" } + } +} +EOF + +# Removing identifiers must not leave references dangling against the +# identifier the schema was assigned for being read from standard input +"$1" bundle - --without-id --resolve "$TMP/other.json" < "$TMP/schema.json" \ + > "$TMP/output.json" 2> "$TMP/stderr.txt" + +cat << 'EOF' > "$TMP/expected.json" +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "local": { + "$ref": "#/$defs/string" + }, + "anchored": { + "$ref": "#/$defs/string" + }, + "external": { + "$ref": "#/$defs/https:~1~1example.com~1other" + } + }, + "$defs": { + "string": { + "type": "string" + }, + "https://example.com/other": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" + } + } +} +EOF + +diff "$TMP/output.json" "$TMP/expected.json" diff --git a/test/test/pass_stdin_data_path.sh b/test/test/pass_stdin_data_path.sh index 626142fe4..b691c3064 100755 --- a/test/test/pass_stdin_data_path.sh +++ b/test/test/pass_stdin_data_path.sh @@ -41,9 +41,11 @@ cat << 'EOF' > "$TMP/test.json" } EOF +cd "$TMP" + # A test document read from standard input has no directory of its own, so # its relative data paths resolve against the working directory -(cd "$TMP" && "$1" test - --resolve "$TMP/schema.json" < "$TMP/test.json") \ +"$1" test - --resolve "$TMP/schema.json" < "$TMP/test.json" \ 1> "$TMP/output.txt" 2>&1 cat << 'EOF' > "$TMP/expected.txt" diff --git a/test/validate/fail_stdin_relative_ref.sh b/test/validate/fail_stdin_relative_ref.sh index 19bae3ac8..7d16801db 100755 --- a/test/validate/fail_stdin_relative_ref.sh +++ b/test/validate/fail_stdin_relative_ref.sh @@ -25,9 +25,11 @@ cat << 'EOF' > "$TMP/schema.json" } EOF +cd "$TMP" + # A relative reference from standard input must fail loudly rather than # quietly resolve against whatever sits in the working directory -(cd "$TMP" && "$1" validate - "$TMP/instance.json" < "$TMP/schema.json") \ +"$1" validate - "$TMP/instance.json" < "$TMP/schema.json" \ > "$TMP/output.txt" 2>&1 && EXIT_CODE="$?" || EXIT_CODE="$?" # Schema input error test "$EXIT_CODE" = "4" From d9bb7b853edbd69fc7cf7104885be3aa3542a5a5 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Tue, 11 Aug 2026 12:08:27 -0300 Subject: [PATCH 3/4] More Signed-off-by: Juan Cruz Viotti --- src/command_lint.cc | 2 +- src/command_rdf.cc | 9 ++---- src/error.h | 31 +++++++++++-------- src/input.h | 5 +-- src/utils.h | 7 ----- test/bundle/fail_stdin_invalid_json.sh | 4 +-- test/bundle/fail_stdin_not_schema.sh | 4 +-- test/format/fail_stdin_check.sh | 4 +-- test/format/fail_stdin_invalid_json.sh | 4 +-- test/format/fail_stdin_missing_newline.sh | 4 +-- test/format/pass_stdin_check_verbose.sh | 2 +- test/format/pass_stdin_test_document.sh | 2 +- test/inspect/fail_stdin_invalid_json.sh | 4 +-- test/inspect/fail_stdin_not_schema.sh | 4 +-- test/lint/fail_stdin_invalid_json.sh | 4 +-- test/lint/fail_stdin_lint.sh | 8 ++--- test/lint/pass_stdin_lint_verbose.sh | 2 +- test/metaschema/fail_stdin_invalid_json.sh | 4 +-- test/metaschema/fail_stdin_metaschema.sh | 4 +-- test/metaschema/fail_stdin_not_schema.sh | 4 +-- .../pass_stdin_metaschema_verbose.sh | 2 +- test/rdf/fail_invalid_instance_stdin.sh | 2 +- test/rdf/fail_stdin_empty.sh | 4 +-- test/test/fail_stdin_invalid_json.sh | 4 +-- test/test/fail_stdin_not_object.sh | 4 +-- test/test/fail_stdin_test_failure.sh | 2 +- test/test/pass_stdin.sh | 2 +- test/test/pass_stdin_data_path.sh | 2 +- test/test/pass_stdin_verbose.sh | 2 +- test/upgrade/fail_stdin_invalid_json.sh | 4 +-- test/validate/fail_stdin_empty.sh | 4 +-- test/validate/fail_stdin_instance.sh | 4 +-- .../fail_stdin_instance_invalid_json.sh | 4 +-- test/validate/fail_stdin_instance_trace.sh | 2 +- test/validate/fail_stdin_relative_ref.sh | 2 +- .../fail_stdin_schema_invalid_json.sh | 4 +-- test/validate/fail_stdin_schema_not_schema.sh | 4 +-- test/validate/fail_stdin_whitespace.sh | 4 +-- test/validate/fail_stdin_yaml_input.sh | 4 +-- .../validate/pass_stdin_instance_benchmark.sh | 2 +- test/validate/pass_stdin_instance_verbose.sh | 2 +- test/validate/pass_stdin_mixed_ordering.sh | 2 +- test/validate/pass_stdin_resolve.sh | 2 +- test/validate/pass_stdin_schema_verbose.sh | 2 +- 44 files changed, 88 insertions(+), 94 deletions(-) diff --git a/src/command_lint.cc b/src/command_lint.cc index b11b6a0d7..feb9031fa 100644 --- a/src/command_lint.cc +++ b/src/command_lint.cc @@ -111,7 +111,7 @@ static auto get_lint_callback(sourcemeta::core::JSON &errors_array, errors_array.push_back(error_obj); } else { if (entry.from_stdin) { - std::cout << sourcemeta::jsonschema::stdin_path().string(); + std::cout << sourcemeta::jsonschema::STDIN_DEFAULT_ID; } else { std::cout << std::filesystem::relative(entry.resolution_base).string(); diff --git a/src/command_rdf.cc b/src/command_rdf.cc index 648c1129c..5edc74b36 100644 --- a/src/command_rdf.cc +++ b/src/command_rdf.cc @@ -126,7 +126,7 @@ auto sourcemeta::jsonschema::rdf(const sourcemeta::core::Options &options) const auto &instance{parsed_instance.document}; const auto instance_display_path{ instance_from_stdin - ? stdin_path().string() + ? std::string{STDIN_DEFAULT_ID} : sourcemeta::core::weakly_canonical(instance_path).string()}; sourcemeta::blaze::Evaluator evaluator; @@ -218,12 +218,7 @@ auto sourcemeta::jsonschema::rdf(const sourcemeta::core::Options &options) } LOG_VERBOSE(options) << "ok: " << instance_display_path << "\n matches " - << (schema_from_stdin - ? stdin_path().string() - : sourcemeta::core::weakly_canonical( - schema_resolution_base) - .string()) - << "\n"; + << stdin_path_string(schema_resolution_base) << "\n"; sourcemeta::core::prettify(document, std::cout); std::cout << "\n"; } diff --git a/src/error.h b/src/error.h index c11b8a98a..36c66d3aa 100644 --- a/src/error.h +++ b/src/error.h @@ -26,6 +26,7 @@ #include // std::optional #include // std::runtime_error #include // std::string +#include // std::string_view #include // std::is_base_of_v, std::is_same_v #include // std::forward, std::move #include // std::vector @@ -462,24 +463,28 @@ class Fail : public std::runtime_error { int exit_code_; }; +// Input read from standard input has no retrieval URI, so JSON Schema 2020-12 +// section 9.1.1 and RFC 3986 section 5.1.4 let us pick an implementation +// specific default. We deliberately pick an RFC 4151 tag URI, as it cannot be +// mistaken for a locator the way a file URI can. This doubles as how we refer +// to standard input in output, so that we never print a path that does not +// exist and never differ across platforms +constexpr std::string_view STDIN_DEFAULT_ID{ + "tag:sourcemeta.com,2026:jsonschema/stdin"}; + +// Input read from standard input never corresponds to a file, so we carry the +// identifier itself where a path would otherwise go. It is never resolved +// against the filesystem, as every such site is guarded on whether the input +// came from standard input inline auto stdin_path() -> std::filesystem::path { -#ifdef _WIN32 - return std::filesystem::path{""}; -#else - return std::filesystem::path{"/dev/stdin"}; -#endif + return std::filesystem::path{STDIN_DEFAULT_ID}; } inline auto stdin_path_string(const std::filesystem::path &p) -> std::string { -#ifdef _WIN32 - if (p.string() == "") { - return ""; - } -#else - if (p == std::filesystem::path{"/dev/stdin"}) { - return "/dev/stdin"; + if (p == stdin_path()) { + return std::string{STDIN_DEFAULT_ID}; } -#endif + return sourcemeta::core::weakly_canonical(p).string(); } diff --git a/src/input.h b/src/input.h index 2e35722b5..dcf14f9eb 100644 --- a/src/input.h +++ b/src/input.h @@ -234,8 +234,9 @@ handle_json_entry(const std::filesystem::path &entry_path, if (entry_path == "-") { auto parsed{read_from_stdin()}; const auto path{stdin_path()}; - result.push_back({path.string(), path, std::move(parsed.document), - std::move(parsed.positions), 0, false, parsed.yaml, true, + result.push_back({std::string{STDIN_DEFAULT_ID}, path, + std::move(parsed.document), std::move(parsed.positions), + 0, false, parsed.yaml, true, std::move(parsed.property_storage)}); return; } diff --git a/src/utils.h b/src/utils.h index bc0e0969c..644c49397 100644 --- a/src/utils.h +++ b/src/utils.h @@ -33,13 +33,6 @@ namespace sourcemeta::jsonschema { -// A schema read from standard input has no retrieval URI, so JSON Schema -// 2020-12 section 9.1.1 and RFC 3986 section 5.1.4 let us pick an -// implementation-specific default. We deliberately pick an RFC 4151 tag URI, -// as it cannot be mistaken for a locator the way a file URI can -constexpr std::string_view STDIN_DEFAULT_ID{ - "tag:sourcemeta.com,2026:jsonschema/stdin"}; - inline auto default_id(const std::filesystem::path &schema_path, const bool from_stdin) -> std::string { if (from_stdin) { diff --git a/test/bundle/fail_stdin_invalid_json.sh b/test/bundle/fail_stdin_invalid_json.sh index 5ab155e04..bb8d76ad0 100755 --- a/test/bundle/fail_stdin_invalid_json.sh +++ b/test/bundle/fail_stdin_invalid_json.sh @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/bundle/fail_stdin_not_schema.sh b/test/bundle/fail_stdin_not_schema.sh index c69e3af5f..cac9f4f34 100755 --- a/test/bundle/fail_stdin_not_schema.sh +++ b/test/bundle/fail_stdin_not_schema.sh @@ -14,7 +14,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" error: The schema file you provided does not represent a valid JSON Schema - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -28,7 +28,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" { "error": "The schema file you provided does not represent a valid JSON Schema", - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/format/fail_stdin_check.sh b/test/format/fail_stdin_check.sh index 48a031340..e7f19b123 100755 --- a/test/format/fail_stdin_check.sh +++ b/test/format/fail_stdin_check.sh @@ -15,7 +15,7 @@ EOF test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -fail: /dev/stdin +fail: tag:sourcemeta.com,2026:jsonschema/stdin Run the `fmt` command without `--check/-c` to fix the formatting EOF @@ -33,7 +33,7 @@ test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" { "valid": false, - "errors": [ "/dev/stdin" ] + "errors": [ "tag:sourcemeta.com,2026:jsonschema/stdin" ] } EOF diff --git a/test/format/fail_stdin_invalid_json.sh b/test/format/fail_stdin_invalid_json.sh index 344c4b368..54adb404e 100755 --- a/test/format/fail_stdin_invalid_json.sh +++ b/test/format/fail_stdin_invalid_json.sh @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/format/fail_stdin_missing_newline.sh b/test/format/fail_stdin_missing_newline.sh index d24bbb38a..e1f872ab6 100755 --- a/test/format/fail_stdin_missing_newline.sh +++ b/test/format/fail_stdin_missing_newline.sh @@ -18,7 +18,7 @@ printf '%s' "$(cat "$TMP/schema_noeol.json")" \ test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -fail: /dev/stdin +fail: tag:sourcemeta.com,2026:jsonschema/stdin Run the `fmt` command without `--check/-c` to fix the formatting EOF @@ -35,7 +35,7 @@ test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" { "valid": false, - "errors": [ "/dev/stdin" ] + "errors": [ "tag:sourcemeta.com,2026:jsonschema/stdin" ] } EOF diff --git a/test/format/pass_stdin_check_verbose.sh b/test/format/pass_stdin_check_verbose.sh index d0b000f34..e16c74803 100755 --- a/test/format/pass_stdin_check_verbose.sh +++ b/test/format/pass_stdin_check_verbose.sh @@ -15,7 +15,7 @@ cat << 'EOF' | "$1" fmt --check - --verbose > "$TMP/output.txt" 2>&1 EOF cat << 'EOF' > "$TMP/expected.txt" -ok: /dev/stdin +ok: tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/format/pass_stdin_test_document.sh b/test/format/pass_stdin_test_document.sh index 69e9532fc..6e94ed075 100755 --- a/test/format/pass_stdin_test_document.sh +++ b/test/format/pass_stdin_test_document.sh @@ -23,7 +23,7 @@ EOF "$1" fmt - < "$TMP/test.json" > "$TMP/output.txt" 2>&1 cat << 'EOF' > "$TMP/expected.txt" -Interpreting as a test file: /dev/stdin +Interpreting as a test file: tag:sourcemeta.com,2026:jsonschema/stdin { "target": "https://example.com/my-schema", "tests": [ diff --git a/test/inspect/fail_stdin_invalid_json.sh b/test/inspect/fail_stdin_invalid_json.sh index 5afc86555..3c5a7f75a 100755 --- a/test/inspect/fail_stdin_invalid_json.sh +++ b/test/inspect/fail_stdin_invalid_json.sh @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/inspect/fail_stdin_not_schema.sh b/test/inspect/fail_stdin_not_schema.sh index 384774e1e..54763c5eb 100755 --- a/test/inspect/fail_stdin_not_schema.sh +++ b/test/inspect/fail_stdin_not_schema.sh @@ -14,7 +14,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" error: The schema file you provided does not represent a valid JSON Schema - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -28,7 +28,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" { "error": "The schema file you provided does not represent a valid JSON Schema", - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/lint/fail_stdin_invalid_json.sh b/test/lint/fail_stdin_invalid_json.sh index 44b05bb64..7ae4271d8 100755 --- a/test/lint/fail_stdin_invalid_json.sh +++ b/test/lint/fail_stdin_invalid_json.sh @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/lint/fail_stdin_lint.sh b/test/lint/fail_stdin_lint.sh index 0d1b22bae..a8f10943d 100755 --- a/test/lint/fail_stdin_lint.sh +++ b/test/lint/fail_stdin_lint.sh @@ -21,10 +21,10 @@ EOF test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -/dev/stdin:6:3: +tag:sourcemeta.com,2026:jsonschema/stdin:6:3: Setting `type` alongside `enum` is considered an anti-pattern, as the enumeration choices already imply their respective types (enum_with_type) at location "/enum" -/dev/stdin:5:3: +tag:sourcemeta.com,2026:jsonschema/stdin:5:3: Setting `type` alongside `enum` is considered an anti-pattern, as the enumeration choices already imply their respective types (enum_with_type) at location "/type" EOF @@ -51,7 +51,7 @@ cat << 'EOF' > "$TMP/expected.txt" "health": 0, "errors": [ { - "path": "/dev/stdin", + "path": "tag:sourcemeta.com,2026:jsonschema/stdin", "id": "enum_with_type", "message": "Setting `type` alongside `enum` is considered an anti-pattern, as the enumeration choices already imply their respective types", "description": null, @@ -59,7 +59,7 @@ cat << 'EOF' > "$TMP/expected.txt" "position": [ 5, 3, 5, 18 ] }, { - "path": "/dev/stdin", + "path": "tag:sourcemeta.com,2026:jsonschema/stdin", "id": "enum_with_type", "message": "Setting `type` alongside `enum` is considered an anti-pattern, as the enumeration choices already imply their respective types", "description": null, diff --git a/test/lint/pass_stdin_lint_verbose.sh b/test/lint/pass_stdin_lint_verbose.sh index 38c1bb47c..25e808ced 100755 --- a/test/lint/pass_stdin_lint_verbose.sh +++ b/test/lint/pass_stdin_lint_verbose.sh @@ -18,7 +18,7 @@ cat << 'EOF' | "$1" lint - --verbose > "$TMP/output.txt" 2>&1 EOF cat << 'EOF' > "$TMP/expected.txt" -Linting: /dev/stdin +Linting: tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/metaschema/fail_stdin_invalid_json.sh b/test/metaschema/fail_stdin_invalid_json.sh index 5cd20bb7a..6e92a30af 100755 --- a/test/metaschema/fail_stdin_invalid_json.sh +++ b/test/metaschema/fail_stdin_invalid_json.sh @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/metaschema/fail_stdin_metaschema.sh b/test/metaschema/fail_stdin_metaschema.sh index cc04f2533..b9b2e600e 100755 --- a/test/metaschema/fail_stdin_metaschema.sh +++ b/test/metaschema/fail_stdin_metaschema.sh @@ -18,7 +18,7 @@ EOF test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -fail: /dev/stdin +fail: tag:sourcemeta.com,2026:jsonschema/stdin error: Schema validation failure The integer value 1 was expected to equal one of the following values: "array", "boolean", "integer", "null", "number", "object", and "string" at instance location "/type" (line 3, column 3) @@ -51,7 +51,7 @@ EOF test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -/dev/stdin +tag:sourcemeta.com,2026:jsonschema/stdin { "valid": false, "errors": [ diff --git a/test/metaschema/fail_stdin_not_schema.sh b/test/metaschema/fail_stdin_not_schema.sh index 315f1de7b..459953060 100755 --- a/test/metaschema/fail_stdin_not_schema.sh +++ b/test/metaschema/fail_stdin_not_schema.sh @@ -14,7 +14,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" error: The schema file you provided does not represent a valid JSON Schema - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -28,7 +28,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" { "error": "The schema file you provided does not represent a valid JSON Schema", - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/metaschema/pass_stdin_metaschema_verbose.sh b/test/metaschema/pass_stdin_metaschema_verbose.sh index 38bb6029e..9e8cb196d 100755 --- a/test/metaschema/pass_stdin_metaschema_verbose.sh +++ b/test/metaschema/pass_stdin_metaschema_verbose.sh @@ -15,7 +15,7 @@ cat << 'EOF' | "$1" metaschema - --verbose 2> "$TMP/stderr.txt" EOF cat << 'EOF' > "$TMP/expected.txt" -ok: /dev/stdin +ok: tag:sourcemeta.com,2026:jsonschema/stdin matches https://json-schema.org/draft/2020-12/schema EOF diff --git a/test/rdf/fail_invalid_instance_stdin.sh b/test/rdf/fail_invalid_instance_stdin.sh index d7fa321eb..83c8876ab 100755 --- a/test/rdf/fail_invalid_instance_stdin.sh +++ b/test/rdf/fail_invalid_instance_stdin.sh @@ -21,7 +21,7 @@ echo '1' | "$1" rdf "$TMP/schema.json" - 2> "$TMP/stderr.txt" \ test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -fail: /dev/stdin +fail: tag:sourcemeta.com,2026:jsonschema/stdin error: Schema validation failure The value was expected to be of type string but it was of type integer at instance location "" (line 1, column 1) diff --git a/test/rdf/fail_stdin_empty.sh b/test/rdf/fail_stdin_empty.sh index 53e184357..68a1bad05 100755 --- a/test/rdf/fail_stdin_empty.sh +++ b/test/rdf/fail_stdin_empty.sh @@ -27,7 +27,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 1 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -43,7 +43,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 1, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/test/fail_stdin_invalid_json.sh b/test/test/fail_stdin_invalid_json.sh index ce9829b0c..75d472313 100755 --- a/test/test/fail_stdin_invalid_json.sh +++ b/test/test/fail_stdin_invalid_json.sh @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/test/fail_stdin_not_object.sh b/test/test/fail_stdin_not_object.sh index 11fd04caf..81595c693 100755 --- a/test/test/fail_stdin_not_object.sh +++ b/test/test/fail_stdin_not_object.sh @@ -15,11 +15,11 @@ EOF test "$EXIT_CODE" = "6" cat << 'EOF' > "$TMP/expected.txt" -/dev/stdin: +tag:sourcemeta.com,2026:jsonschema/stdin: error: The test document must be an object at line 1 at column 1 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin at location "" Learn more here: https://github.com/sourcemeta/jsonschema/blob/main/docs/test.markdown diff --git a/test/test/fail_stdin_test_failure.sh b/test/test/fail_stdin_test_failure.sh index 3e2fbbd36..b5ce47ba3 100755 --- a/test/test/fail_stdin_test_failure.sh +++ b/test/test/fail_stdin_test_failure.sh @@ -32,7 +32,7 @@ EOF test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -/dev/stdin: +tag:sourcemeta.com,2026:jsonschema/stdin: 1/1 FAIL Unexpected pass error: Passed but was expected to fail diff --git a/test/test/pass_stdin.sh b/test/test/pass_stdin.sh index 4def9b106..b36a25510 100755 --- a/test/test/pass_stdin.sh +++ b/test/test/pass_stdin.sh @@ -34,7 +34,7 @@ cat << 'EOF' | "$1" test - --resolve "$TMP/schema.json" 1> "$TMP/output.txt" 2>& EOF cat << 'EOF' > "$TMP/expected.txt" -/dev/stdin: PASS 2/2 +tag:sourcemeta.com,2026:jsonschema/stdin: PASS 2/2 EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/test/pass_stdin_data_path.sh b/test/test/pass_stdin_data_path.sh index b691c3064..f2a607ac0 100755 --- a/test/test/pass_stdin_data_path.sh +++ b/test/test/pass_stdin_data_path.sh @@ -49,7 +49,7 @@ cd "$TMP" 1> "$TMP/output.txt" 2>&1 cat << 'EOF' > "$TMP/expected.txt" -/dev/stdin: PASS 2/2 +tag:sourcemeta.com,2026:jsonschema/stdin: PASS 2/2 EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/test/pass_stdin_verbose.sh b/test/test/pass_stdin_verbose.sh index 410a203b3..4801490ab 100755 --- a/test/test/pass_stdin_verbose.sh +++ b/test/test/pass_stdin_verbose.sh @@ -35,7 +35,7 @@ EOF cat << 'EOF' > "$TMP/expected.txt" Using parallelism: 1 -/dev/stdin: +tag:sourcemeta.com,2026:jsonschema/stdin: 1/2 PASS A string 2/2 PASS Not a string EOF diff --git a/test/upgrade/fail_stdin_invalid_json.sh b/test/upgrade/fail_stdin_invalid_json.sh index 2bf8d700e..8dc08ab42 100755 --- a/test/upgrade/fail_stdin_invalid_json.sh +++ b/test/upgrade/fail_stdin_invalid_json.sh @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/validate/fail_stdin_empty.sh b/test/validate/fail_stdin_empty.sh index f2a41f5c4..b420c7fab 100755 --- a/test/validate/fail_stdin_empty.sh +++ b/test/validate/fail_stdin_empty.sh @@ -23,7 +23,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 2 at column 1 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -39,7 +39,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 2, "column": 1, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/validate/fail_stdin_instance.sh b/test/validate/fail_stdin_instance.sh index c5f619345..7bc82015f 100755 --- a/test/validate/fail_stdin_instance.sh +++ b/test/validate/fail_stdin_instance.sh @@ -20,7 +20,7 @@ echo '123' | "$1" validate "$TMP/schema.json" - > "$TMP/output.txt" 2>&1 \ test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -fail: /dev/stdin +fail: tag:sourcemeta.com,2026:jsonschema/stdin error: Schema validation failure The value was expected to be of type string but it was of type integer at instance location "" (line 1, column 1) @@ -36,7 +36,7 @@ echo '123' | "$1" validate "$TMP/schema.json" - --json > "$TMP/stdout.txt" 2>&1 test "$EXIT_CODE" = "2" cat << EOF > "$TMP/expected.txt" -/dev/stdin +tag:sourcemeta.com,2026:jsonschema/stdin { "valid": false, "errors": [ diff --git a/test/validate/fail_stdin_instance_invalid_json.sh b/test/validate/fail_stdin_instance_invalid_json.sh index 85cd5992e..709575e90 100755 --- a/test/validate/fail_stdin_instance_invalid_json.sh +++ b/test/validate/fail_stdin_instance_invalid_json.sh @@ -23,7 +23,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 9 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -39,7 +39,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 9, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/validate/fail_stdin_instance_trace.sh b/test/validate/fail_stdin_instance_trace.sh index 03c907a8d..e87500df3 100755 --- a/test/validate/fail_stdin_instance_trace.sh +++ b/test/validate/fail_stdin_instance_trace.sh @@ -42,7 +42,7 @@ echo '42' | "$1" validate "$TMP/schema.json" - --json > "$TMP/stdout.txt" 2>&1 \ test "$EXIT_CODE" = "2" cat << EOF > "$TMP/expected.json" -/dev/stdin +tag:sourcemeta.com,2026:jsonschema/stdin { "valid": false, "errors": [ diff --git a/test/validate/fail_stdin_relative_ref.sh b/test/validate/fail_stdin_relative_ref.sh index 7d16801db..328a44742 100755 --- a/test/validate/fail_stdin_relative_ref.sh +++ b/test/validate/fail_stdin_relative_ref.sh @@ -37,7 +37,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" error: Could not resolve the reference to an external schema at identifier tag:sourcemeta.com,2026:jsonschema/nested.json - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin This is likely because you forgot to import such schema using `--resolve/-r` EOF diff --git a/test/validate/fail_stdin_schema_invalid_json.sh b/test/validate/fail_stdin_schema_invalid_json.sh index 80db7805e..17aac9e5d 100755 --- a/test/validate/fail_stdin_schema_invalid_json.sh +++ b/test/validate/fail_stdin_schema_invalid_json.sh @@ -20,7 +20,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 1 at column 10 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -36,7 +36,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 1, "column": 10, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/validate/fail_stdin_schema_not_schema.sh b/test/validate/fail_stdin_schema_not_schema.sh index 50e6b4a9f..2dec07820 100755 --- a/test/validate/fail_stdin_schema_not_schema.sh +++ b/test/validate/fail_stdin_schema_not_schema.sh @@ -18,7 +18,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" error: The schema file you provided does not represent a valid JSON Schema - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -32,7 +32,7 @@ test "$EXIT_CODE" = "4" cat << 'EOF' > "$TMP/expected.txt" { "error": "The schema file you provided does not represent a valid JSON Schema", - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/validate/fail_stdin_whitespace.sh b/test/validate/fail_stdin_whitespace.sh index 70ea6867b..13dbda730 100755 --- a/test/validate/fail_stdin_whitespace.sh +++ b/test/validate/fail_stdin_whitespace.sh @@ -23,7 +23,7 @@ cat << 'EOF' > "$TMP/expected.txt" error: Failed to parse the JSON document at line 2 at column 1 - at file path /dev/stdin + at file path tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/stderr.txt" "$TMP/expected.txt" @@ -39,7 +39,7 @@ cat << 'EOF' > "$TMP/expected.txt" "error": "Failed to parse the JSON document", "line": 2, "column": 1, - "filePath": "/dev/stdin" + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" } EOF diff --git a/test/validate/fail_stdin_yaml_input.sh b/test/validate/fail_stdin_yaml_input.sh index f160b3cbd..1d7e154ee 100755 --- a/test/validate/fail_stdin_yaml_input.sh +++ b/test/validate/fail_stdin_yaml_input.sh @@ -20,7 +20,7 @@ printf 'foo: bar\nbaz: 1\n' | "$1" validate "$TMP/schema.json" - > "$TMP/output. test "$EXIT_CODE" = "2" cat << 'EOF' > "$TMP/expected.txt" -fail: /dev/stdin +fail: tag:sourcemeta.com,2026:jsonschema/stdin error: Schema validation failure The value was expected to be of type string but it was of type object at instance location "" (line 1, column 1) @@ -36,7 +36,7 @@ printf 'foo: bar\nbaz: 1\n' | "$1" validate "$TMP/schema.json" - --json > "$TMP/ test "$EXIT_CODE" = "2" cat << EOF > "$TMP/expected.txt" -/dev/stdin +tag:sourcemeta.com,2026:jsonschema/stdin { "valid": false, "errors": [ diff --git a/test/validate/pass_stdin_instance_benchmark.sh b/test/validate/pass_stdin_instance_benchmark.sh index 1bf13ab25..61d2f0153 100755 --- a/test/validate/pass_stdin_instance_benchmark.sh +++ b/test/validate/pass_stdin_instance_benchmark.sh @@ -24,7 +24,7 @@ echo '{ "foo": "bar" }' | "$1" validate "$TMP/schema.json" - --benchmark > "$TMP sed -E 's/[0-9]+\.[0-9]+//g' "$TMP/output.txt" > "$TMP/normalized.txt" cat << 'EOF' > "$TMP/expected.txt" -/dev/stdin: PASS +- us () +tag:sourcemeta.com,2026:jsonschema/stdin: PASS +- us () EOF diff "$TMP/normalized.txt" "$TMP/expected.txt" diff --git a/test/validate/pass_stdin_instance_verbose.sh b/test/validate/pass_stdin_instance_verbose.sh index 5fa31abf9..29a31d453 100755 --- a/test/validate/pass_stdin_instance_verbose.sh +++ b/test/validate/pass_stdin_instance_verbose.sh @@ -23,7 +23,7 @@ EOF echo '{ "foo": "bar" }' | "$1" validate "$TMP/schema.json" - --verbose 2> "$TMP/stderr.txt" cat << EOF > "$TMP/expected.txt" -ok: /dev/stdin +ok: tag:sourcemeta.com,2026:jsonschema/stdin matches $(realpath "$TMP")/schema.json annotation: "Test schema" at instance location "" (line 1, column 1) diff --git a/test/validate/pass_stdin_mixed_ordering.sh b/test/validate/pass_stdin_mixed_ordering.sh index f6527b5ec..d1f34add6 100755 --- a/test/validate/pass_stdin_mixed_ordering.sh +++ b/test/validate/pass_stdin_mixed_ordering.sh @@ -28,7 +28,7 @@ echo '{ "c": 3 }' | "$1" validate "$TMP/schema.json" \ cat << EOF > "$TMP/expected.txt" ok: $(realpath "$TMP")/instance1.json matches $(realpath "$TMP")/schema.json -ok: /dev/stdin +ok: tag:sourcemeta.com,2026:jsonschema/stdin matches $(realpath "$TMP")/schema.json ok: $(realpath "$TMP")/instance2.json matches $(realpath "$TMP")/schema.json diff --git a/test/validate/pass_stdin_resolve.sh b/test/validate/pass_stdin_resolve.sh index 2d9e4bafa..f96129f17 100755 --- a/test/validate/pass_stdin_resolve.sh +++ b/test/validate/pass_stdin_resolve.sh @@ -27,7 +27,7 @@ EOF cat << EOF > "$TMP/expected.txt" ok: $(realpath "$TMP")/instance.json - matches /dev/stdin + matches tag:sourcemeta.com,2026:jsonschema/stdin EOF diff "$TMP/output.txt" "$TMP/expected.txt" diff --git a/test/validate/pass_stdin_schema_verbose.sh b/test/validate/pass_stdin_schema_verbose.sh index 6fe95e278..b6529e97c 100755 --- a/test/validate/pass_stdin_schema_verbose.sh +++ b/test/validate/pass_stdin_schema_verbose.sh @@ -23,7 +23,7 @@ EOF cat << EOF > "$TMP/expected.txt" ok: $(realpath "$TMP")/instance.json - matches /dev/stdin + matches tag:sourcemeta.com,2026:jsonschema/stdin annotation: "foo" at instance location "" (line 1, column 1) at evaluate path "/properties" From 4d67b34f5a1bf86f6b3ad6d07659b6b90d712207 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Tue, 11 Aug 2026 12:22:10 -0300 Subject: [PATCH 4/4] Fix Signed-off-by: Juan Cruz Viotti --- test/validate/fail_stdin_relative_ref.sh | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/test/validate/fail_stdin_relative_ref.sh b/test/validate/fail_stdin_relative_ref.sh index 328a44742..04f9169fc 100755 --- a/test/validate/fail_stdin_relative_ref.sh +++ b/test/validate/fail_stdin_relative_ref.sh @@ -29,8 +29,8 @@ cd "$TMP" # A relative reference from standard input must fail loudly rather than # quietly resolve against whatever sits in the working directory -"$1" validate - "$TMP/instance.json" < "$TMP/schema.json" \ - > "$TMP/output.txt" 2>&1 && EXIT_CODE="$?" || EXIT_CODE="$?" +"$1" validate - "$TMP/instance.json" < "$TMP/schema.json" 2> "$TMP/stderr.txt" \ + && EXIT_CODE="$?" || EXIT_CODE="$?" # Schema input error test "$EXIT_CODE" = "4" @@ -42,4 +42,20 @@ error: Could not resolve the reference to an external schema This is likely because you forgot to import such schema using `--resolve/-r` EOF -diff "$TMP/output.txt" "$TMP/expected.txt" +diff "$TMP/stderr.txt" "$TMP/expected.txt" + +# JSON error +"$1" validate - "$TMP/instance.json" --json < "$TMP/schema.json" \ + > "$TMP/stdout.txt" && EXIT_CODE="$?" || EXIT_CODE="$?" +# Schema input error +test "$EXIT_CODE" = "4" + +cat << 'EOF' > "$TMP/expected.txt" +{ + "error": "Could not resolve the reference to an external schema", + "identifier": "tag:sourcemeta.com,2026:jsonschema/nested.json", + "filePath": "tag:sourcemeta.com,2026:jsonschema/stdin" +} +EOF + +diff "$TMP/stdout.txt" "$TMP/expected.txt"