Skip to content

Commit 8006bfe

Browse files
committed
feat(linter): add forbid_empty_enum rule
Signed-off-by: Vaibhav mittal <[email protected]>
1 parent ac96b5f commit 8006bfe

File tree

8 files changed

+370
-0
lines changed

8 files changed

+370
-0
lines changed

src/extension/alterschema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema
8080
linter/duplicate_examples.h
8181
linter/enum_to_const.h
8282
linter/equal_numeric_bounds_to_const.h
83+
linter/forbid_empty_enum.h
8384
linter/items_array_default.h
8485
linter/items_schema_default.h
8586
linter/multiple_of_default.h

src/extension/alterschema/alterschema.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ inline auto APPLIES_TO_POINTERS(std::vector<Pointer> &&keywords)
108108
#include "linter/duplicate_examples.h"
109109
#include "linter/enum_to_const.h"
110110
#include "linter/equal_numeric_bounds_to_const.h"
111+
#include "linter/forbid_empty_enum.h"
111112
#include "linter/items_array_default.h"
112113
#include "linter/items_schema_default.h"
113114
#include "linter/multiple_of_default.h"
@@ -222,6 +223,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void {
222223
bundle.add<UnsatisfiableMaxContains>();
223224
bundle.add<UnsatisfiableMinProperties>();
224225
bundle.add<EnumToConst>();
226+
bundle.add<ForbidEmptyEnum>();
225227
bundle.add<TopLevelTitle>();
226228
bundle.add<TopLevelDescription>();
227229
bundle.add<TopLevelExamples>();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class ForbidEmptyEnum final : public SchemaTransformRule {
2+
public:
3+
using mutates = std::false_type;
4+
using reframe_after_transform = std::false_type;
5+
ForbidEmptyEnum()
6+
: SchemaTransformRule{"forbid_empty_enum",
7+
"enum must contain at least one value"} {};
8+
9+
[[nodiscard]] auto
10+
condition(const sourcemeta::core::JSON &schema,
11+
const sourcemeta::core::JSON &,
12+
const sourcemeta::core::Vocabularies &vocabularies,
13+
const sourcemeta::core::SchemaFrame &,
14+
const sourcemeta::core::SchemaFrame::Location &,
15+
const sourcemeta::core::SchemaWalker &,
16+
const sourcemeta::core::SchemaResolver &) const
17+
-> sourcemeta::core::SchemaTransformRule::Result override {
18+
ONLY_CONTINUE_IF(vocabularies.contains_any(
19+
{Vocabularies::Known::JSON_Schema_2020_12_Validation,
20+
Vocabularies::Known::JSON_Schema_2019_09_Validation,
21+
Vocabularies::Known::JSON_Schema_Draft_7,
22+
Vocabularies::Known::JSON_Schema_Draft_6,
23+
Vocabularies::Known::JSON_Schema_Draft_4,
24+
Vocabularies::Known::JSON_Schema_Draft_3,
25+
Vocabularies::Known::JSON_Schema_Draft_2,
26+
Vocabularies::Known::JSON_Schema_Draft_1}) &&
27+
schema.is_object() && schema.defines("enum") &&
28+
schema.at("enum").is_array() && schema.at("enum").empty());
29+
return APPLIES_TO_KEYWORDS("enum");
30+
}
31+
};

test/alterschema/alterschema_lint_2019_09_test.cc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4494,3 +4494,71 @@ TEST(AlterSchema_lint_2019_09, empty_object_as_true_1) {
44944494

44954495
EXPECT_EQ(document, expected);
44964496
}
4497+
4498+
TEST(AlterSchema_lint_2019_09, forbid_empty_enum_1) {
4499+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4500+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4501+
"title": "Example",
4502+
"description": "Example schema",
4503+
"examples": [1],
4504+
"enum": []
4505+
})JSON");
4506+
4507+
LINT_WITHOUT_FIX(document, result, traces);
4508+
4509+
EXPECT_FALSE(result.first);
4510+
EXPECT_EQ(traces.size(), 1);
4511+
EXPECT_LINT_TRACE(traces, 0, "", "forbid_empty_enum",
4512+
"enum must contain at least one value", false);
4513+
}
4514+
4515+
TEST(AlterSchema_lint_2019_09, forbid_empty_enum_2) {
4516+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4517+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4518+
"title": "Example",
4519+
"description": "Example schema",
4520+
"examples": [1],
4521+
"enum": [1, 2]
4522+
})JSON");
4523+
4524+
LINT_WITHOUT_FIX(document, result, traces);
4525+
4526+
EXPECT_TRUE(result.first);
4527+
EXPECT_EQ(traces.size(), 0);
4528+
}
4529+
4530+
TEST(AlterSchema_lint_2019_09, forbid_empty_enum_3) {
4531+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4532+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4533+
"title": "Example",
4534+
"description": "Example schema",
4535+
"examples": [1],
4536+
"type": "string"
4537+
})JSON");
4538+
4539+
LINT_WITHOUT_FIX(document, result, traces);
4540+
4541+
EXPECT_TRUE(result.first);
4542+
EXPECT_EQ(traces.size(), 0);
4543+
}
4544+
4545+
TEST(AlterSchema_lint_2019_09, forbid_empty_enum_4) {
4546+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
4547+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4548+
"title": "Example",
4549+
"description": "Example schema",
4550+
"examples": [{}],
4551+
"properties": {
4552+
"foo": {
4553+
"enum": []
4554+
}
4555+
}
4556+
})JSON");
4557+
4558+
LINT_WITHOUT_FIX(document, result, traces);
4559+
4560+
EXPECT_FALSE(result.first);
4561+
EXPECT_EQ(traces.size(), 1);
4562+
EXPECT_LINT_TRACE(traces, 0, "/properties/foo", "forbid_empty_enum",
4563+
"enum must contain at least one value", false);
4564+
}

test/alterschema/alterschema_lint_2020_12_test.cc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9512,3 +9512,71 @@ TEST(AlterSchema_lint_2020_12, empty_object_as_true_3) {
95129512

95139513
EXPECT_EQ(document, expected);
95149514
}
9515+
9516+
TEST(AlterSchema_lint_2020_12, forbid_empty_enum_1) {
9517+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
9518+
"$schema": "https://json-schema.org/draft/2020-12/schema",
9519+
"title": "Example",
9520+
"description": "Example schema",
9521+
"examples": [1],
9522+
"enum": []
9523+
})JSON");
9524+
9525+
LINT_WITHOUT_FIX(document, result, traces);
9526+
9527+
EXPECT_FALSE(result.first);
9528+
EXPECT_EQ(traces.size(), 1);
9529+
EXPECT_LINT_TRACE(traces, 0, "", "forbid_empty_enum",
9530+
"enum must contain at least one value", false);
9531+
}
9532+
9533+
TEST(AlterSchema_lint_2020_12, forbid_empty_enum_2) {
9534+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
9535+
"$schema": "https://json-schema.org/draft/2020-12/schema",
9536+
"title": "Example",
9537+
"description": "Example schema",
9538+
"examples": [1],
9539+
"enum": [1, 2]
9540+
})JSON");
9541+
9542+
LINT_WITHOUT_FIX(document, result, traces);
9543+
9544+
EXPECT_TRUE(result.first);
9545+
EXPECT_EQ(traces.size(), 0);
9546+
}
9547+
9548+
TEST(AlterSchema_lint_2020_12, forbid_empty_enum_3) {
9549+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
9550+
"$schema": "https://json-schema.org/draft/2020-12/schema",
9551+
"title": "Example",
9552+
"description": "Example schema",
9553+
"examples": [1],
9554+
"type": "string"
9555+
})JSON");
9556+
9557+
LINT_WITHOUT_FIX(document, result, traces);
9558+
9559+
EXPECT_TRUE(result.first);
9560+
EXPECT_EQ(traces.size(), 0);
9561+
}
9562+
9563+
TEST(AlterSchema_lint_2020_12, forbid_empty_enum_4) {
9564+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
9565+
"$schema": "https://json-schema.org/draft/2020-12/schema",
9566+
"title": "Example",
9567+
"description": "Example schema",
9568+
"examples": [{}],
9569+
"properties": {
9570+
"foo": {
9571+
"enum": []
9572+
}
9573+
}
9574+
})JSON");
9575+
9576+
LINT_WITHOUT_FIX(document, result, traces);
9577+
9578+
EXPECT_FALSE(result.first);
9579+
EXPECT_EQ(traces.size(), 1);
9580+
EXPECT_LINT_TRACE(traces, 0, "/properties/foo", "forbid_empty_enum",
9581+
"enum must contain at least one value", false);
9582+
}

test/alterschema/alterschema_lint_draft4_test.cc

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,3 +2604,67 @@ TEST(AlterSchema_lint_draft4,
26042604

26052605
EXPECT_EQ(document, expected);
26062606
}
2607+
2608+
TEST(AlterSchema_lint_draft4, forbid_empty_enum_1) {
2609+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2610+
"$schema": "http://json-schema.org/draft-04/schema#",
2611+
"title": "Example",
2612+
"description": "Example schema",
2613+
"enum": []
2614+
})JSON");
2615+
2616+
LINT_WITHOUT_FIX(document, result, traces);
2617+
2618+
EXPECT_FALSE(result.first);
2619+
EXPECT_EQ(traces.size(), 1);
2620+
EXPECT_LINT_TRACE(traces, 0, "", "forbid_empty_enum",
2621+
"enum must contain at least one value", false);
2622+
}
2623+
2624+
TEST(AlterSchema_lint_draft4, forbid_empty_enum_2) {
2625+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2626+
"$schema": "http://json-schema.org/draft-04/schema#",
2627+
"title": "Example",
2628+
"description": "Example schema",
2629+
"enum": [1, 2]
2630+
})JSON");
2631+
2632+
LINT_WITHOUT_FIX(document, result, traces);
2633+
2634+
EXPECT_TRUE(result.first);
2635+
EXPECT_EQ(traces.size(), 0);
2636+
}
2637+
2638+
TEST(AlterSchema_lint_draft4, forbid_empty_enum_3) {
2639+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2640+
"$schema": "http://json-schema.org/draft-04/schema#",
2641+
"title": "Example",
2642+
"description": "Example schema",
2643+
"type": "string"
2644+
})JSON");
2645+
2646+
LINT_WITHOUT_FIX(document, result, traces);
2647+
2648+
EXPECT_TRUE(result.first);
2649+
EXPECT_EQ(traces.size(), 0);
2650+
}
2651+
2652+
TEST(AlterSchema_lint_draft4, forbid_empty_enum_4) {
2653+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2654+
"$schema": "http://json-schema.org/draft-04/schema#",
2655+
"title": "Example",
2656+
"description": "Example schema",
2657+
"properties": {
2658+
"foo": {
2659+
"enum": []
2660+
}
2661+
}
2662+
})JSON");
2663+
2664+
LINT_WITHOUT_FIX(document, result, traces);
2665+
2666+
EXPECT_FALSE(result.first);
2667+
EXPECT_EQ(traces.size(), 1);
2668+
EXPECT_LINT_TRACE(traces, 0, "/properties/foo", "forbid_empty_enum",
2669+
"enum must contain at least one value", false);
2670+
}

test/alterschema/alterschema_lint_draft6_test.cc

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,3 +2929,71 @@ TEST(AlterSchema_lint_draft6, empty_object_as_true_1) {
29292929

29302930
EXPECT_EQ(document, expected);
29312931
}
2932+
2933+
TEST(AlterSchema_lint_draft6, forbid_empty_enum_1) {
2934+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2935+
"$schema": "http://json-schema.org/draft-06/schema#",
2936+
"title": "Example",
2937+
"description": "Example schema",
2938+
"examples": [1],
2939+
"enum": []
2940+
})JSON");
2941+
2942+
LINT_WITHOUT_FIX(document, result, traces);
2943+
2944+
EXPECT_FALSE(result.first);
2945+
EXPECT_EQ(traces.size(), 1);
2946+
EXPECT_LINT_TRACE(traces, 0, "", "forbid_empty_enum",
2947+
"enum must contain at least one value", false);
2948+
}
2949+
2950+
TEST(AlterSchema_lint_draft6, forbid_empty_enum_2) {
2951+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2952+
"$schema": "http://json-schema.org/draft-06/schema#",
2953+
"title": "Example",
2954+
"description": "Example schema",
2955+
"examples": [1],
2956+
"enum": [1, 2]
2957+
})JSON");
2958+
2959+
LINT_WITHOUT_FIX(document, result, traces);
2960+
2961+
EXPECT_TRUE(result.first);
2962+
EXPECT_EQ(traces.size(), 0);
2963+
}
2964+
2965+
TEST(AlterSchema_lint_draft6, forbid_empty_enum_3) {
2966+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2967+
"$schema": "http://json-schema.org/draft-06/schema#",
2968+
"title": "Example",
2969+
"description": "Example schema",
2970+
"examples": [1],
2971+
"type": "string"
2972+
})JSON");
2973+
2974+
LINT_WITHOUT_FIX(document, result, traces);
2975+
2976+
EXPECT_TRUE(result.first);
2977+
EXPECT_EQ(traces.size(), 0);
2978+
}
2979+
2980+
TEST(AlterSchema_lint_draft6, forbid_empty_enum_4) {
2981+
const sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2982+
"$schema": "http://json-schema.org/draft-06/schema#",
2983+
"title": "Example",
2984+
"description": "Example schema",
2985+
"examples": [{}],
2986+
"properties": {
2987+
"foo": {
2988+
"enum": []
2989+
}
2990+
}
2991+
})JSON");
2992+
2993+
LINT_WITHOUT_FIX(document, result, traces);
2994+
2995+
EXPECT_FALSE(result.first);
2996+
EXPECT_EQ(traces.size(), 1);
2997+
EXPECT_LINT_TRACE(traces, 0, "/properties/foo", "forbid_empty_enum",
2998+
"enum must contain at least one value", false);
2999+
}

0 commit comments

Comments
 (0)