diff --git a/src/extension/alterschema/CMakeLists.txt b/src/extension/alterschema/CMakeLists.txt index d17efb350..76ac6ea44 100644 --- a/src/extension/alterschema/CMakeLists.txt +++ b/src/extension/alterschema/CMakeLists.txt @@ -85,6 +85,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core 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/extension/alterschema/alterschema.cc b/src/extension/alterschema/alterschema.cc index d20eff011..cd24c259b 100644 --- a/src/extension/alterschema/alterschema.cc +++ b/src/extension/alterschema/alterschema.cc @@ -114,6 +114,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" @@ -236,6 +237,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void { bundle.add(); bundle.add(); bundle.add(); + bundle.add(); bundle.add(); bundle.add(); bundle.add(); diff --git a/src/extension/alterschema/linter/format_type_mismatch.h b/src/extension/alterschema/linter/format_type_mismatch.h new file mode 100644 index 000000000..b6ec7f3bd --- /dev/null +++ b/src/extension/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 e3ef2ef9c..3fa10d1cb 100644 --- a/test/alterschema/alterschema_lint_2019_09_test.cc +++ b/test/alterschema/alterschema_lint_2019_09_test.cc @@ -5092,3 +5092,199 @@ TEST(AlterSchema_lint_2019_09, 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": [ {} ], + "$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": [ {} ], + "$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); +} diff --git a/test/alterschema/alterschema_lint_2020_12_test.cc b/test/alterschema/alterschema_lint_2020_12_test.cc index b303726c3..539683314 100644 --- a/test/alterschema/alterschema_lint_2020_12_test.cc +++ b/test/alterschema/alterschema_lint_2020_12_test.cc @@ -10654,3 +10654,199 @@ TEST(AlterSchema_lint_2020_12, 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": [ {} ], + "$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": [ {} ], + "$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); +} diff --git a/test/alterschema/alterschema_lint_draft3_test.cc b/test/alterschema/alterschema_lint_draft3_test.cc index 02dd03541..94ebb2454 100644 --- a/test/alterschema/alterschema_lint_draft3_test.cc +++ b/test/alterschema/alterschema_lint_draft3_test.cc @@ -1328,3 +1328,194 @@ TEST(AlterSchema_lint_draft3, draft_official_dialect_with_https_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); +} diff --git a/test/alterschema/alterschema_lint_draft4_test.cc b/test/alterschema/alterschema_lint_draft4_test.cc index 8c333cb3d..ff13b77ad 100644 --- a/test/alterschema/alterschema_lint_draft4_test.cc +++ b/test/alterschema/alterschema_lint_draft4_test.cc @@ -2788,3 +2788,194 @@ TEST(AlterSchema_lint_draft4, draft_official_dialect_with_https_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); +} diff --git a/test/alterschema/alterschema_lint_draft6_test.cc b/test/alterschema/alterschema_lint_draft6_test.cc index 601772ca3..e1ffaf97d 100644 --- a/test/alterschema/alterschema_lint_draft6_test.cc +++ b/test/alterschema/alterschema_lint_draft6_test.cc @@ -3165,3 +3165,203 @@ TEST(AlterSchema_lint_draft6, draft_official_dialect_with_https_1) { 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); +} diff --git a/test/alterschema/alterschema_lint_draft7_test.cc b/test/alterschema/alterschema_lint_draft7_test.cc index d16640583..6c9690219 100644 --- a/test/alterschema/alterschema_lint_draft7_test.cc +++ b/test/alterschema/alterschema_lint_draft7_test.cc @@ -3841,3 +3841,203 @@ TEST(AlterSchema_lint_draft7, 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); +}