diff --git a/src/alterschema/CMakeLists.txt b/src/alterschema/CMakeLists.txt index 34376c7d..f8381a64 100644 --- a/src/alterschema/CMakeLists.txt +++ b/src/alterschema/CMakeLists.txt @@ -86,6 +86,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT blaze NAME alterschema linter/enum_to_const.h linter/equal_numeric_bounds_to_const.h linter/forbid_empty_enum.h + linter/format_type_mismatch.h linter/incoherent_min_max_contains.h linter/invalid_external_ref.h linter/items_array_default.h diff --git a/src/alterschema/alterschema.cc b/src/alterschema/alterschema.cc index 2cd0f3bf..205d37a1 100644 --- a/src/alterschema/alterschema.cc +++ b/src/alterschema/alterschema.cc @@ -124,6 +124,7 @@ inline auto APPLIES_TO_POINTERS(std::vector &&keywords) #include "linter/enum_to_const.h" #include "linter/equal_numeric_bounds_to_const.h" #include "linter/forbid_empty_enum.h" +#include "linter/format_type_mismatch.h" #include "linter/incoherent_min_max_contains.h" #include "linter/invalid_external_ref.h" #include "linter/items_array_default.h" @@ -248,6 +249,7 @@ auto add(sourcemeta::core::SchemaTransformer &bundle, bundle.add(); bundle.add(); bundle.add(); + bundle.add(); bundle.add(); bundle.add(); bundle.add(); diff --git a/src/alterschema/linter/format_type_mismatch.h b/src/alterschema/linter/format_type_mismatch.h new file mode 100644 index 00000000..b6ec7f3b --- /dev/null +++ b/src/alterschema/linter/format_type_mismatch.h @@ -0,0 +1,34 @@ +class FormatTypeMismatch final : public SchemaTransformRule { +public: + using mutates = std::false_type; + using reframe_after_transform = std::false_type; + FormatTypeMismatch() + : SchemaTransformRule{ + "format_type_mismatch", + "The `format` keyword validates string instances but `type` " + "is not `string`"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::core::JSON &schema, + const sourcemeta::core::JSON &, + const sourcemeta::core::Vocabularies &vocabularies, + const sourcemeta::core::SchemaFrame &, + const sourcemeta::core::SchemaFrame::Location &, + const sourcemeta::core::SchemaWalker &, + const sourcemeta::core::SchemaResolver &) const + -> sourcemeta::core::SchemaTransformRule::Result override { + ONLY_CONTINUE_IF(vocabularies.contains_any( + {Vocabularies::Known::JSON_Schema_2020_12_Validation, + Vocabularies::Known::JSON_Schema_2019_09_Validation, + Vocabularies::Known::JSON_Schema_Draft_7, + Vocabularies::Known::JSON_Schema_Draft_6, + Vocabularies::Known::JSON_Schema_Draft_4, + Vocabularies::Known::JSON_Schema_Draft_3}) && + schema.is_object() && schema.defines("type") && + schema.at("type").is_string() && + schema.at("type").to_string() != "string" && + schema.defines("format") && + schema.at("format").is_string()); + return APPLIES_TO_KEYWORDS("format", "type"); + } +}; diff --git a/test/alterschema/alterschema_lint_2019_09_test.cc b/test/alterschema/alterschema_lint_2019_09_test.cc index b877038f..75efe3bf 100644 --- a/test/alterschema/alterschema_lint_2019_09_test.cc +++ b/test/alterschema/alterschema_lint_2019_09_test.cc @@ -3534,6 +3534,202 @@ TEST(AlterSchema_lint_2019_09, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_2019_09, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "$ref": "#/$defs/foo", + "$defs": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "https://example.com" ], + "$ref": "#/$defs/A/properties/bar", + "$defs": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2019_09, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_2019_09, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2019-09/schema", diff --git a/test/alterschema/alterschema_lint_2020_12_test.cc b/test/alterschema/alterschema_lint_2020_12_test.cc index 99e1f2d6..b159b335 100644 --- a/test/alterschema/alterschema_lint_2020_12_test.cc +++ b/test/alterschema/alterschema_lint_2020_12_test.cc @@ -3963,6 +3963,202 @@ TEST(AlterSchema_lint_2020_12, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_2020_12, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "$ref": "#/$defs/foo", + "$defs": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ "https://example.com" ], + "$ref": "#/$defs/A/properties/bar", + "$defs": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/$defs/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_2020_12, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_2020_12, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "https://json-schema.org/draft/2020-12/schema", diff --git a/test/alterschema/alterschema_lint_draft3_test.cc b/test/alterschema/alterschema_lint_draft3_test.cc index 02dd0354..296aaf46 100644 --- a/test/alterschema/alterschema_lint_draft3_test.cc +++ b/test/alterschema/alterschema_lint_draft3_test.cc @@ -1082,6 +1082,197 @@ TEST(AlterSchema_lint_draft3, non_applicable_type_specific_keywords_1) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft3, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "additionalProperties": { + "type": "integer", + "format": "uri" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "additionalProperties": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/additionalProperties", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/additionalProperties", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft3, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "title": "Test", + "description": "A test schema", + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft3, unknown_keywords_prefix_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-03/schema#", diff --git a/test/alterschema/alterschema_lint_draft4_test.cc b/test/alterschema/alterschema_lint_draft4_test.cc index 41763b72..6f56a2da 100644 --- a/test/alterschema/alterschema_lint_draft4_test.cc +++ b/test/alterschema/alterschema_lint_draft4_test.cc @@ -1753,6 +1753,197 @@ TEST(AlterSchema_lint_draft4, non_applicable_type_specific_keywords_1) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft4, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "x": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "properties": { + "x": { "$ref": "#/definitions/A/properties/bar" } + }, + "definitions": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft4, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test", + "description": "A test schema", + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft4, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-04/schema#", diff --git a/test/alterschema/alterschema_lint_draft6_test.cc b/test/alterschema/alterschema_lint_draft6_test.cc index cc0e642c..50004b83 100644 --- a/test/alterschema/alterschema_lint_draft6_test.cc +++ b/test/alterschema/alterschema_lint_draft6_test.cc @@ -2022,6 +2022,206 @@ TEST(AlterSchema_lint_draft6, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft6, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/A/properties/bar" } + }, + "definitions": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft6, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft6, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-06/schema#", diff --git a/test/alterschema/alterschema_lint_draft7_test.cc b/test/alterschema/alterschema_lint_draft7_test.cc index 78f53883..90c8c605 100644 --- a/test/alterschema/alterschema_lint_draft7_test.cc +++ b/test/alterschema/alterschema_lint_draft7_test.cc @@ -2370,6 +2370,206 @@ TEST(AlterSchema_lint_draft7, non_applicable_type_specific_keywords_3) { EXPECT_EQ(document, expected); } +TEST(AlterSchema_lint_draft7, format_type_mismatch_1) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "", "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_2) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "type": "string", + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_3) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ "foo@bar.com" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_4) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "foo": { + "type": "integer", + "format": "email" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/properties/foo", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/properties/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_5) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": "integer" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_6) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ [] ], + "type": "array", + "items": { + "type": "number", + "format": "date-time" + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 2); + EXPECT_LINT_TRACE(traces, 0, "/items", + "non_applicable_type_specific_keywords", + "Avoid keywords that don't apply to the type or " + "types that the current subschema expects", + true); + EXPECT_LINT_TRACE(traces, 1, "/items", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_7) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": { + "type": "integer", + "format": "uri" + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/foo", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_8) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ {} ], + "properties": { + "x": { "$ref": "#/definitions/A/properties/bar" } + }, + "definitions": { + "A": { + "type": "integer", + "format": "uri", + "properties": { + "bar": { "type": "string" } + } + } + } + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_FALSE(result.first); + EXPECT_EQ(traces.size(), 1); + EXPECT_LINT_TRACE(traces, 0, "/definitions/A", "format_type_mismatch", + "The `format` keyword validates string instances but " + "`type` is not `string`", + false); +} + +TEST(AlterSchema_lint_draft7, format_type_mismatch_9) { + const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test", + "description": "A test schema", + "examples": [ 1 ], + "type": [ "integer", "string" ], + "format": "email" + })JSON"); + + LINT_WITHOUT_FIX(document, result, traces); + + EXPECT_TRUE(result.first); + EXPECT_EQ(traces.size(), 0); +} + TEST(AlterSchema_lint_draft7, not_false_1) { sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ "$schema": "http://json-schema.org/draft-07/schema#",