Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/extension/alterschema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/extension/alterschema/alterschema.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ inline auto APPLIES_TO_POINTERS(std::vector<Pointer> &&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"
Expand Down Expand Up @@ -236,6 +237,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
bundle.add<UnsatisfiableMinProperties>();
bundle.add<EnumToConst>();
bundle.add<ForbidEmptyEnum>();
bundle.add<FormatTypeMismatch>();
bundle.add<TopLevelTitle>();
bundle.add<TopLevelDescription>();
bundle.add<TopLevelExamples>();
Expand Down
34 changes: 34 additions & 0 deletions src/extension/alterschema/linter/format_type_mismatch.h
Original file line number Diff line number Diff line change
@@ -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(
Comment thread
Vaibhav701161 marked this conversation as resolved.
Outdated
{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");
Comment thread
Vaibhav701161 marked this conversation as resolved.
}
};
196 changes: 196 additions & 0 deletions test/alterschema/alterschema_lint_2019_09_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Loading
Loading