diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java index e55179b1..b0a7577f 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java @@ -432,6 +432,18 @@ protected String listDiff(int deepness, String name, ChangedList listDiff) { if (listDiff == null) { return ""; } + + // Check if this looks like a simple rename (1 added, 1 removed) + if (listDiff.getIncreased().size() == 1 && listDiff.getMissing().size() == 1) { + StringBuilder sb = new StringBuilder(); + Object oldValue = listDiff.getMissing().get(0); + Object newValue = listDiff.getIncreased().get(0); + sb.append(format("%sRenamed %s value:\n\n", indent(deepness), name)); + sb.append(format("%s* `%s` -> `%s`\n", indent(deepness), oldValue, newValue)); + return sb.toString(); + } + + // Fall back to current behavior for other cases return listItem(deepness, "Added " + name, listDiff.getIncreased()) + listItem(deepness, "Removed " + name, listDiff.getMissing()); } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java index fb1f24fe..e6378847 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java @@ -4,8 +4,11 @@ import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.Collections; import org.junit.jupiter.api.Test; import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.model.ChangedList; import org.openapitools.openapidiff.core.model.ChangedOpenApi; public class MarkdownRenderTest { @@ -40,4 +43,66 @@ public void renderDoesNotFailWhenHTTPStatusCodeIsRange() { render.render(diff, outputStreamWriter); assertThat(outputStream.toString()).isNotBlank(); } + + @Test + public void testEnumRenameDetection() { + MarkdownRender render = new MarkdownRender(); + + // Test 1:1 rename (should show as rename) + ChangedList enumRename = + org.openapitools.openapidiff.core.compare.ListDiff.diff( + new ChangedList.SimpleChangedList<>( + Collections.singletonList("OldValue"), Collections.singletonList("NewValue"))); + String renameResult = render.listDiff(2, "enum", enumRename); + assertThat(renameResult).contains("Renamed enum value:"); + assertThat(renameResult).contains("`OldValue` -> `NewValue`"); + + // Test multiple additions and removals (should fall back to original behavior) + ChangedList enumMultiple = + org.openapitools.openapidiff.core.compare.ListDiff.diff( + new ChangedList.SimpleChangedList<>( + Arrays.asList("OldValue1", "OldValue2"), Arrays.asList("NewValue1", "NewValue2"))); + String multipleResult = render.listDiff(2, "enum", enumMultiple); + assertThat(multipleResult).contains("Added enum values:"); + assertThat(multipleResult).contains("Removed enum values:"); + + // Test single addition (should use original behavior) + ChangedList enumAdd = + org.openapitools.openapidiff.core.compare.ListDiff.diff( + new ChangedList.SimpleChangedList<>( + Collections.emptyList(), Collections.singletonList("NewValue"))); + String addResult = render.listDiff(2, "enum", enumAdd); + assertThat(addResult).contains("Added enum value:"); + assertThat(addResult).doesNotContain("Renamed"); + + // Test single removal (should use original behavior) + ChangedList enumRemove = + org.openapitools.openapidiff.core.compare.ListDiff.diff( + new ChangedList.SimpleChangedList<>( + Collections.singletonList("OldValue"), Collections.emptyList())); + String removeResult = render.listDiff(2, "enum", enumRemove); + assertThat(removeResult).contains("Removed enum value:"); + assertThat(removeResult).doesNotContain("Renamed"); + } + + @Test + public void testOriginalIssueScenario() { + // Test to reproduce the original issue scenario + MarkdownRender render = new MarkdownRender(); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("enum_issue_old.yaml", "enum_issue_new.yaml"); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + render.render(diff, outputStreamWriter); + + String result = outputStream.toString(); + System.out.println("=== ENHANCED OUTPUT ==="); + System.out.println(result); + System.out.println("=== END OUTPUT ==="); + + // With the enhancement, this should show as a rename instead of separate add/remove + assertThat(result).contains("Renamed enum value"); + assertThat(result).contains("`StatusThree` -> `StatusNew`"); + } } diff --git a/core/src/test/resources/enum_issue_new.yaml b/core/src/test/resources/enum_issue_new.yaml new file mode 100644 index 00000000..6cab972b --- /dev/null +++ b/core/src/test/resources/enum_issue_new.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + title: Enum Issue Test + version: 1.0.0 +paths: + /test: + get: + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SomeStatus' +components: + schemas: + SomeStatus: + description: Some status description + enum: + - StatusOne + - StatusTwo + - StatusNew + type: string + x-ms-enum: + name: SomeStatus + modelAsString: false + values: + - name: StatusOne + value: StatusOne + - name: StatusTwo + value: StatusTwo + - name: StatusNew + value: StatusNew \ No newline at end of file diff --git a/core/src/test/resources/enum_issue_old.yaml b/core/src/test/resources/enum_issue_old.yaml new file mode 100644 index 00000000..f238b668 --- /dev/null +++ b/core/src/test/resources/enum_issue_old.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + title: Enum Issue Test + version: 1.0.0 +paths: + /test: + get: + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SomeStatus' +components: + schemas: + SomeStatus: + description: Some status description + enum: + - StatusOne + - StatusTwo + - StatusThree + type: string + x-ms-enum: + name: SomeStatus + modelAsString: false + values: + - name: StatusOne + value: StatusOne + - name: StatusTwo + value: StatusTwo + - name: StatusThree + value: StatusThree \ No newline at end of file