Skip to content

Dr satyr/issue346 #769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
.project
*.iml

# Maven configuration
.mvn/maven.config

# Mobile Tools for Java (J2ME)
.mtj.tmp/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,32 @@ public static ChangedSchema getRequestBodyChangedSchema(
.map(ChangedMediaType::getSchema)
.orElse(null);
}

/**
* Get the response ChangedSchema for the given method, path, response code, and media type.
*
* @param changedOpenApi the ChangedOpenApi object
* @param method the HTTP method
* @param path the path
* @param responseCode the response code
* @param mediaType the media type
* @return the ChangedSchema object
*/
@Nullable
public static ChangedSchema getResponseBodyChangedSchema(
ChangedOpenApi changedOpenApi,
HttpMethod method,
String path,
String responseCode,
String mediaType) {
return Optional.ofNullable(getChangedOperation(changedOpenApi, method, path))
.map(ChangedOperation::getApiResponses)
.map(ChangedApiResponse::getChanged)
.map(responses -> responses.get(responseCode))
.map(ChangedResponse::getContent)
.map(ChangedContent::getChanged)
.map(changedMediaTypes -> changedMediaTypes.get(mediaType))
.map(ChangedMediaType::getSchema)
.orElse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openapitools.openapidiff.core.ChangesResolver.getRequestBodyChangedSchema;
import static org.openapitools.openapidiff.core.ChangesResolver.getResponseBodyChangedSchema;
import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardCompatible;

import java.io.ByteArrayOutputStream;
Expand All @@ -15,6 +16,8 @@
import org.junit.jupiter.api.Test;
import org.openapitools.openapidiff.core.model.ChangedOpenApi;
import org.openapitools.openapidiff.core.model.ChangedSchema;
import org.openapitools.openapidiff.core.model.DiffResult;
import org.openapitools.openapidiff.core.model.schema.ChangedRequired;
import org.openapitools.openapidiff.core.output.ConsoleRender;

public class SchemaDiffTest {
Expand Down Expand Up @@ -130,7 +133,6 @@ public void changeMultipleOfHandling() {
assertThat(props.get("field3").getMultipleOf().getRight()).isEqualTo(BigDecimal.valueOf(10));

// Check deletion of multipleOf
assertThat(props.get("field4").getMultipleOf().isCompatible()).isTrue();
assertThat(props.get("field4").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(10));
assertThat(props.get("field4").getMultipleOf().getRight()).isNull();
}
Expand Down Expand Up @@ -273,4 +275,46 @@ public void changeMinMaxPropertiesHandling() {
assertThat(props.get("field4").getMaxProperties().getOldValue()).isEqualTo(100);
assertThat(props.get("field4").getMaxProperties().getNewValue()).isEqualTo(10);
}

@Test // issue #346
public void requiredPropertyChangedTest() {
ChangedOpenApi changedOpenApi =
OpenApiCompare.fromLocations(
"schemaDiff/required-property-1.yaml", "schemaDiff/required-property-2.yaml");

// Test request body schema changes
ChangedSchema requestSchema =
getRequestBodyChangedSchema(changedOpenApi, POST, "/schema/required", "application/json");

assertNotNull(requestSchema, "Request schema should not be null");
ChangedRequired requestRequired = requestSchema.getRequired();
assertNotNull(requestRequired, "Request required changes should not be null");

// Test request property changes
assertThat(requestRequired.getIncreased()).contains("becomesRequired");
assertThat(requestRequired.getMissing()).contains("becomesOptional");
assertThat(requestRequired.getIncreased()).doesNotContain("unchanged");
assertThat(requestRequired.getMissing()).doesNotContain("unchanged");

// Verify breaking changes in request - new required properties are breaking
assertThat(requestRequired.isItemsChanged()).isEqualTo(DiffResult.INCOMPATIBLE);

// Test response schema changes
ChangedSchema responseSchema =
getResponseBodyChangedSchema(
changedOpenApi, POST, "/schema/required", "200", "application/json");

assertNotNull(responseSchema, "Response schema should not be null");
ChangedRequired responseRequired = responseSchema.getRequired();
assertNotNull(responseRequired, "Response required changes should not be null");

// Test response property changes
assertThat(responseRequired.getIncreased()).contains("becomesRequired");
assertThat(responseRequired.getMissing()).contains("becomesOptional");
assertThat(responseRequired.getIncreased()).doesNotContain("unchanged");
assertThat(responseRequired.getMissing()).doesNotContain("unchanged");

// Verify breaking changes in response - removing required properties is breaking
assertThat(responseRequired.isItemsChanged()).isEqualTo(DiffResult.INCOMPATIBLE);
}
}
39 changes: 39 additions & 0 deletions core/src/test/resources/schemaDiff/required-property-1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
openapi: 3.0.0
info:
title: Required Property Test API
version: 1.0.0
paths:
/schema/required:
post:
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [becomesOptional, unchanged]
properties:
becomesRequired:
type: string
description: Property that changes from optional to required (breaking for requests)
becomesOptional:
type: string
description: Property that changes from required to optional (breaking for responses)
unchanged:
type: string
description: Property that remains unchanged in terms of required status
responses:
'200':
description: Success response
content:
application/json:
schema:
type: object
required: [becomesOptional, unchanged]
properties:
becomesRequired:
type: string
becomesOptional:
type: string
unchanged:
type: string
39 changes: 39 additions & 0 deletions core/src/test/resources/schemaDiff/required-property-2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
openapi: 3.0.0
info:
title: Required Property Test API
version: 1.0.0
paths:
/schema/required:
post:
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [becomesRequired, unchanged]
properties:
becomesRequired:
type: string
description: Property that changes from optional to required (breaking for requests)
becomesOptional:
type: string
description: Property that changes from required to optional (breaking for responses)
unchanged:
type: string
description: Property that remains unchanged in terms of required status
responses:
'200':
description: Success response
content:
application/json:
schema:
type: object
required: [becomesRequired, unchanged]
properties:
becomesRequired:
type: string
becomesOptional:
type: string
unchanged:
type: string