This is an automated email from the ASF dual-hosted git repository.
apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 0430e3c6530 IGNITE-25831 Add enums support to OpenAPI test (#6287)
0430e3c6530 is described below
commit 0430e3c6530c319449f79f596500b59b319328e7
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Mon Jul 21 22:29:18 2025 +0300
IGNITE-25831 Add enums support to OpenAPI test (#6287)
---
.../org/apache/ignite/internal/OpenApiMatcher.java | 114 +++++++++++++++------
.../apache/ignite/internal/OpenApiMatcherTest.java | 67 ++++++++++--
2 files changed, 139 insertions(+), 42 deletions(-)
diff --git
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcher.java
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcher.java
index cb4be431f63..5d5e327fe08 100644
---
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcher.java
+++
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcher.java
@@ -29,6 +29,7 @@ import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
@@ -330,6 +331,11 @@ public class OpenApiMatcher extends
TypeSafeDiagnosingMatcher<OpenAPI> {
return false;
}
+ if (!Objects.equals(baseSchema.getFormat(),
currentSchema.getFormat())) {
+ mismatchDescription.appendText("Schema
").appendValue(path).appendText(" has different formats");
+ return false;
+ }
+
String baseRef = baseSchema.get$ref();
if (!Objects.equals(baseRef, currentSchema.get$ref())) {
mismatchDescription.appendText("Schema
").appendValue(path).appendText(" has different reference");
@@ -363,53 +369,93 @@ public class OpenApiMatcher extends
TypeSafeDiagnosingMatcher<OpenAPI> {
}
if ("object".equals(baseType)) {
- // Detect new required properties only for responses
- if (isRequest) {
- Set<String> baseRequired = new
HashSet<>(ofNullable(baseSchema.getRequired()).orElse(List.of()));
+ return compareObjectSchema(path, mismatchDescription,
baseSchema, currentSchema);
+ }
- Set<String> newRequired =
ofNullable(currentSchema.getRequired()).orElse(List.of()).stream()
- .filter(Predicate.not(baseRequired::contains))
- .collect(toSet());
+ if ("string".equals(baseType)) {
+ return compareStringSchema(path, mismatchDescription,
baseSchema, currentSchema);
+ }
- if (!newRequired.isEmpty()) {
- mismatchDescription.appendText("Schema
").appendValue(path)
- .appendText(" has new required properties
").appendValue(newRequired);
- return false;
- }
- } else {
- Set<String> currentRequired = new
HashSet<>(ofNullable(currentSchema.getRequired()).orElse(List.of()));
+ return true;
+ }
- Set<String> newOptional =
ofNullable(baseSchema.getRequired()).orElse(List.of()).stream()
- .filter(Predicate.not(currentRequired::contains))
- .collect(toSet());
+ private boolean compareObjectSchema(String path, Description
mismatchDescription, Schema<?> baseSchema, Schema<?> currentSchema) {
+ if (isRequest) {
+ Set<String> baseRequired = new
HashSet<>(ofNullable(baseSchema.getRequired()).orElse(List.of()));
- if (!newOptional.isEmpty()) {
- mismatchDescription.appendText("Schema
").appendValue(path)
- .appendText(" has new optional properties
").appendValue(newOptional);
- return false;
- }
- }
+ Set<String> newRequired =
ofNullable(currentSchema.getRequired()).orElse(List.of()).stream()
+ .filter(Predicate.not(baseRequired::contains))
+ .collect(toSet());
- Map<String, Schema> baseProperties =
ofNullable(baseSchema.getProperties()).orElse(Map.of());
- Map<String, Schema> currentProperties =
ofNullable(currentSchema.getProperties()).orElse(Map.of());
- if (!compareProperties(path + "/object", mismatchDescription,
baseProperties, currentProperties)) {
+ if (!newRequired.isEmpty()) {
+ mismatchDescription.appendText("Schema ").appendValue(path)
+ .appendText(" has new required properties
").appendValue(newRequired);
return false;
}
+ } else {
+ Set<String> currentRequired = new
HashSet<>(ofNullable(currentSchema.getRequired()).orElse(List.of()));
- Object baseAdditionalProperties =
baseSchema.getAdditionalProperties();
- Object currentAdditionalProperties =
currentSchema.getAdditionalProperties();
- if (baseAdditionalProperties instanceof Schema &&
currentAdditionalProperties instanceof Schema) {
- return compareSchema(
- path + "/additionalProperties",
- mismatchDescription,
- ((Schema) baseAdditionalProperties),
- ((Schema) currentAdditionalProperties)
- );
+ Set<String> newOptional =
ofNullable(baseSchema.getRequired()).orElse(List.of()).stream()
+ .filter(Predicate.not(currentRequired::contains))
+ .collect(toSet());
+
+ if (!newOptional.isEmpty()) {
+ mismatchDescription.appendText("Schema ").appendValue(path)
+ .appendText(" has new optional properties
").appendValue(newOptional);
+ return false;
}
+ }
+
+ Map<String, Schema> baseProperties =
ofNullable(baseSchema.getProperties()).orElse(Map.of());
+ Map<String, Schema> currentProperties =
ofNullable(currentSchema.getProperties()).orElse(Map.of());
+ if (!compareProperties(path + "/object", mismatchDescription,
baseProperties, currentProperties)) {
+ return false;
+ }
+
+ Object baseAdditionalProperties =
baseSchema.getAdditionalProperties();
+ Object currentAdditionalProperties =
currentSchema.getAdditionalProperties();
+ if (baseAdditionalProperties instanceof Schema &&
currentAdditionalProperties instanceof Schema) {
+ return compareSchema(
+ path + "/additionalProperties",
+ mismatchDescription,
+ ((Schema) baseAdditionalProperties),
+ ((Schema) currentAdditionalProperties)
+ );
+ }
+ return true;
+ }
+
+ private boolean compareStringSchema(String path, Description
mismatchDescription, Schema<?> baseSchema, Schema<?> currentSchema) {
+ if (!(baseSchema instanceof StringSchema) || !(currentSchema
instanceof StringSchema)) {
return true;
}
+ if (isRequest) {
+ Set<String> currentEnum = new
HashSet<>(ofNullable(((StringSchema)
currentSchema).getEnum()).orElse(List.of()));
+
+ Set<String> missingValues = ofNullable(((StringSchema)
baseSchema).getEnum()).orElse(List.of()).stream()
+ .filter(Predicate.not(currentEnum::contains))
+ .collect(toSet());
+
+ if (!missingValues.isEmpty()) {
+ mismatchDescription.appendText("Schema ").appendValue(path)
+ .appendText(" has missing enum values
").appendValue(missingValues);
+ return false;
+ }
+ } else {
+ Set<String> baseEnum = new
HashSet<>(ofNullable(((StringSchema) baseSchema).getEnum()).orElse(List.of()));
+
+ Set<String> newValues = ofNullable(((StringSchema)
currentSchema).getEnum()).orElse(List.of()).stream()
+ .filter(Predicate.not(baseEnum::contains))
+ .collect(toSet());
+
+ if (!newValues.isEmpty()) {
+ mismatchDescription.appendText("Schema ").appendValue(path)
+ .appendText(" has new enum values
").appendValue(newValues);
+ return false;
+ }
+ }
return true;
}
diff --git
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcherTest.java
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcherTest.java
index 6d4fd0ede64..33934e8a4c5 100644
---
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcherTest.java
+++
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/OpenApiMatcherTest.java
@@ -28,6 +28,7 @@ import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
@@ -151,6 +152,17 @@ class OpenApiMatcherTest {
+ "of parameter \"param\" : Schema \"test/param\" has
different type");
}
+ @Test
+ void differentParameterSchemaFormat() {
+ OpenAPI baseApi = new OpenAPI().path("test", new PathItem().get(new
Operation()
+ .addParametersItem(new Parameter().name("param").schema(new
Schema().type("integer").format("int32")))));
+ OpenAPI currentApi = new OpenAPI().path("test", new PathItem().get(new
Operation()
+ .addParametersItem(new Parameter().name("param").schema(new
Schema().type("integer").format("int64")))));
+
+ assertThatMismatchedWithDescription(baseApi, currentApi, "operation
<GET> at path \"test\" has incompatible schemas "
+ + "of parameter \"param\" : Schema \"test/param\" has
different formats");
+ }
+
@Test
void differentParameterSchemaRef() {
OpenAPI baseApi = new OpenAPI().path("test", new PathItem().get(new
Operation()
@@ -164,8 +176,7 @@ class OpenApiMatcherTest {
@Test
void differentComponentsSchemaType() {
- PathItem pathItem = new PathItem().get(new
Operation().addParametersItem(new Parameter().name("param")
- .schema(new
Schema().$ref("#/components/schemas/testSchema"))));
+ PathItem pathItem = createPathItem();
OpenAPI baseApi = new OpenAPI().path("test", pathItem)
.components(new Components().addSchemas("testSchema", new
Schema().type("object")));
OpenAPI currentApi = new OpenAPI().path("test", pathItem)
@@ -177,8 +188,7 @@ class OpenApiMatcherTest {
@Test
void differentComponentsSchemaAdditionalType() {
- PathItem pathItem = new PathItem().get(new
Operation().addParametersItem(new Parameter().name("param")
- .schema(new
Schema().$ref("#/components/schemas/testSchema"))));
+ PathItem pathItem = createPathItem();
OpenAPI baseApi = new OpenAPI().path("test", pathItem)
.components(new Components().addSchemas("testSchema", new
Schema().type("object")
.additionalProperties(new Schema().type("object"))));
@@ -192,8 +202,7 @@ class OpenApiMatcherTest {
@Test
void differentComponentsSchemaInnerType() {
- PathItem pathItem = new PathItem().get(new
Operation().addParametersItem(new Parameter().name("param")
- .schema(new
Schema().$ref("#/components/schemas/testSchema"))));
+ PathItem pathItem = createPathItem();
OpenAPI baseApi = new OpenAPI().path("test", pathItem)
.components(new Components().addSchemas("testSchema", new
Schema().type("object")
.addProperty("value", new Schema().type("object"))));
@@ -207,8 +216,7 @@ class OpenApiMatcherTest {
@Test
void newRequiredParameters() {
- PathItem pathItem = new PathItem().get(new
Operation().addParametersItem(new Parameter().name("param")
- .schema(new
Schema().$ref("#/components/schemas/testSchema"))));
+ PathItem pathItem = createPathItem();
OpenAPI baseApi = new OpenAPI().path("test", pathItem)
.components(new Components().addSchemas("testSchema", new
Schema().type("object")
.addProperty("prop", new Schema())));
@@ -242,6 +250,44 @@ class OpenApiMatcherTest {
+ "incompatible content: Schema
\"#/components/schemas/testSchema\" has new optional properties <[prop]>");
}
+ @Test
+ void newEnumValuesInRequest() {
+ PathItem pathItem = createPathItem();
+ StringSchema baseSchema = new StringSchema().type("string");
+ baseSchema.addEnumItemObject("FIRST");
+ baseSchema.addEnumItemObject("SECOND");
+ OpenAPI baseApi = new OpenAPI().path("test", pathItem)
+ .components(new Components().addSchemas("testSchema",
baseSchema));
+ StringSchema currentSchema = new StringSchema().type("string");
+ currentSchema.addEnumItemObject("FIRST");
+ OpenAPI currentApi = new OpenAPI().path("test", pathItem)
+ .components(new Components().addSchemas("testSchema",
currentSchema));
+
+ assertThatMismatchedWithDescription(baseApi, currentApi, "operation
<GET> at path \"test\" has incompatible schemas "
+ + "of parameter \"param\" : Schema
\"#/components/schemas/testSchema\" has missing enum values <[SECOND]>");
+ }
+
+ @Test
+ void newEnumValuesInResponse() {
+ PathItem pathItem = new PathItem().get(new Operation()
+ .responses(new ApiResponses().addApiResponse("200", new
ApiResponse().content(new Content()
+ .addMediaType("application/json", new MediaType()
+ .schema(new
Schema().$ref("#/components/schemas/testSchema")))))));
+ StringSchema baseSchema = new StringSchema().type("string");
+ baseSchema.addEnumItemObject("FIRST");
+ OpenAPI baseApi = new OpenAPI().path("test", pathItem)
+ .components(new Components().addSchemas("testSchema",
baseSchema));
+ StringSchema currentSchema = new StringSchema().type("string");
+ currentSchema.addEnumItemObject("FIRST");
+ currentSchema.addEnumItemObject("SECOND");
+ OpenAPI currentApi = new OpenAPI().path("test", pathItem)
+ .components(new Components().addSchemas("testSchema",
currentSchema));
+
+ assertThatMismatchedWithDescription(baseApi, currentApi,
+ "operation <GET> at path \"test\" response \"200\" has
incompatible content: "
+ + "Schema \"#/components/schemas/testSchema\" has new enum
values <[SECOND]>");
+ }
+
private static void assertThatMismatchedWithDescription(OpenAPI baseApi,
OpenAPI currentApi, String description) {
Matcher<OpenAPI> matcher = isCompatibleWith(baseApi);
assertThat(matcher.matches(currentApi), is(false));
@@ -250,4 +296,9 @@ class OpenApiMatcherTest {
matcher.describeMismatch(currentApi, stringDescription);
assertThat(stringDescription.toString(), is(description));
}
+
+ private static PathItem createPathItem() {
+ return new PathItem().get(new Operation().addParametersItem(new
Parameter().name("param")
+ .schema(new
Schema().$ref("#/components/schemas/testSchema"))));
+ }
}