From 7920875456789eba60a18181e2fde547511f09aa Mon Sep 17 00:00:00 2001 From: Inf1mum Date: Tue, 10 Nov 2026 19:57:21 +0300 Subject: [PATCH 1/2] fix: render changed operation description in HTML and Markdown reports When an operation's description changes, the diff core detects the change (ChangedOperation#description, DiffResult.METADATA), but HtmlRender never emitted it: the operation was listed as changed while the report showed no detail at all. Add a Description section to the operation detail list in HtmlRender, mirroring the existing j2html pattern used for other operation-level changes (Operation ID, Parameters, Request, Response): the old description is struck through and the new one shown next to it, like the changed parameter notes. MarkdownRender had the same gap and now renders the changed description through the existing metadata helper, respecting the showChangedMetadata flag like the summary does. Fixes #857. --- .../openapidiff/core/output/HtmlRender.java | 17 +++ .../core/output/MarkdownRender.java | 4 + .../core/output/HtmlRenderTest.java | 28 +++++ .../core/output/MarkdownRenderTest.java | 15 +++ .../issue-857-operation-description-1.json | 108 ++++++++++++++++++ .../issue-857-operation-description-2.json | 108 ++++++++++++++++++ 6 files changed, 280 insertions(+) create mode 100644 core/src/test/resources/issue-857-operation-description-1.json create mode 100644 core/src/test/resources/issue-857-operation-description-2.json diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java index 441d1c9d..c1b26253 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java @@ -203,6 +203,10 @@ private OlTag ol_changed(List changedOperations) { .orElse(""); UlTag ul_detail = ul().withClass("detail"); + if (result(changedOperation.getDescription()).isDifferent()) { + ul_detail.with( + li().with(h3("Description")).with(ul_description(changedOperation.getDescription()))); + } if (result(changedOperation.getOperationId()).isDifferent()) { ul_detail.with( li().with(h3("Operation ID")).with(ul_operation_id(changedOperation.getOperationId()))); @@ -576,4 +580,17 @@ private UlTag ul_operation_id(ChangedOperationId changedOperationId) { + " to " + Optional.ofNullable(changedOperationId.getRight()).orElse(""))); } + + private UlTag ul_description(ChangedMetadata changedMetadata) { + return ul().withClass("change description") + .with( + li().withText("Changed description: ") + .with( + del(null == changedMetadata.getLeft() ? "" : changedMetadata.getLeft()) + .withClass(COMMENT)) + .withText(" change into ") + .with( + span(null == changedMetadata.getRight() ? "" : changedMetadata.getRight()) + .withClass(COMMENT))); + } } 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 531fd380..30c9b387 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 @@ -133,6 +133,10 @@ protected void listEndpoints( operation.getHttpMethod().toString(), operation.getPathUrl(), operation.getSummary())); + if (result(operation.getDescription()).isDifferent()) { + safelyAppend(outputStreamWriter, titleH5("Description:")); + safelyAppend(outputStreamWriter, metadata("description", operation.getDescription())); + } if (result(operation.getOperationId()).isDifferent()) { safelyAppend(outputStreamWriter, titleH5("Operation ID:")); safelyAppend(outputStreamWriter, operationId(operation.getOperationId())); diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java index 2d770e00..dc0d26a1 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java @@ -43,4 +43,32 @@ public void issue865_renderWithShowAllChangesDoesNotFailWhenSchemaIsNullButExamp render.render(diff, outputStreamWriter); assertThat(outputStream.toString()).isNotBlank(); } + + @Test + public void issue857_renderShowsChangedOperationDescription() { + HtmlRender render = new HtmlRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations( + "issue-857-operation-description-1.json", "issue-857-operation-description-2.json"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).contains("Changed description:"); + assertThat(outputStream.toString()).contains("Sample description"); + assertThat(outputStream.toString()) + .contains("Sample description changed"); + } + + @Test + public void issue857_renderWithShowAllChangesShowsChangedOperationDescription() { + HtmlRender render = new HtmlRender(true); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations( + "issue-857-operation-description-1.json", "issue-857-operation-description-2.json"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).contains("Changed description:"); + assertThat(outputStream.toString()).contains("Sample description changed"); + } } 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 59788210..1ebea992 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 @@ -20,6 +20,21 @@ public void renderDoesNotFailWhenPropertyHasBeenRemoved() { assertThat(outputStream.toString()).isNotBlank(); } + @Test + public void issue857_renderShowsChangedOperationDescription() { + MarkdownRender render = new MarkdownRender(); + render.setShowChangedMetadata(true); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations( + "issue-857-operation-description-1.json", "issue-857-operation-description-2.json"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).contains("Changed description:"); + assertThat(outputStream.toString()).contains("Sample description"); + assertThat(outputStream.toString()).contains("Sample description changed"); + } + @Test public void renderDoesNotCauseStackOverflowWithRecursiveDefinitions() { MarkdownRender render = new MarkdownRender(); diff --git a/core/src/test/resources/issue-857-operation-description-1.json b/core/src/test/resources/issue-857-operation-description-1.json new file mode 100644 index 00000000..0ccef0d1 --- /dev/null +++ b/core/src/test/resources/issue-857-operation-description-1.json @@ -0,0 +1,108 @@ +{ + "components": { + "schemas": { + }, + "securitySchemes": { + } + }, + "info": { + "description": "This is the swagger documentation for sample-api. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). ", + "title": "sample-api", + "version": "3.47.0" + }, + "openapi": "3.1.0", + "paths": { + "/sample-api/export": { + "get": { + "description": "Export into MS Excel", + "operationId": "exportItems_1", + "parameters": [ + { + "in": "query", + "name": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "sort", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation" + } + }, + "summary": "Export list in excel format", + "tags": [ + "Search" + ] + } + }, + "/sample-api/{id}/actions/CAN": { + "post": { + "description": "Sample description", + "operationId": "cancel_1", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "201": { + "description": "Cancel executed" + } + }, + "summary": "Cancel", + "tags": [ + "Workflow actions" + ] + } + }, + "/sample-api/{id}/actions/DEL": { + "post": { + "description": "This method should be used when a user wants to delete an entity from the system", + "operationId": "delete_1", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "201": { + "description": "Delete executed" + } + }, + "summary": "Delete", + "tags": [ + "Workflow actions" + ] + } + } + }, + "security": [ + ], + "servers": [ + { + "url": "http://sample-server" + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/issue-857-operation-description-2.json b/core/src/test/resources/issue-857-operation-description-2.json new file mode 100644 index 00000000..690b832e --- /dev/null +++ b/core/src/test/resources/issue-857-operation-description-2.json @@ -0,0 +1,108 @@ +{ + "components": { + "schemas": { + }, + "securitySchemes": { + } + }, + "info": { + "description": "This is the swagger documentation for sample-api. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). ", + "title": "sample-api", + "version": "3.48.0" + }, + "openapi": "3.1.0", + "paths": { + "/sample-api/export": { + "get": { + "description": "Export into MS Excel", + "operationId": "exportItems_1", + "parameters": [ + { + "in": "query", + "name": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "sort", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation" + } + }, + "summary": "Export list in excel format", + "tags": [ + "Search" + ] + } + }, + "/sample-api/{id}/actions/CAN": { + "post": { + "description": "Sample description changed", + "operationId": "cancel_1", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "201": { + "description": "Cancel executed" + } + }, + "summary": "Cancel", + "tags": [ + "Workflow actions" + ] + } + }, + "/sample-api/{id}/actions/DEL": { + "post": { + "description": "This method should be used when a user wants to delete an entity from the system", + "operationId": "delete_1", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "201": { + "description": "Delete executed" + } + }, + "summary": "Delete", + "tags": [ + "Workflow actions" + ] + } + } + }, + "security": [ + ], + "servers": [ + { + "url": "http://sample-server" + } + ] +} \ No newline at end of file From d007987bba63001bbb57cc499c1b0c016bff463a Mon Sep 17 00:00:00 2001 From: Inf1mum Date: Tue, 10 Nov 2026 23:53:05 +0300 Subject: [PATCH 2/2] test: strengthen issue-857 description render assertions MarkdownRenderTest: assert the complete old-to-new metadata sequence emitted by MarkdownRender (blockquote old, 'to:' separator, blockquote new) instead of matching either description substring, which the new description satisfied on its own. HtmlRenderTest (showAllChanges mode): assert the element carries the old description and the element carries the new one. --- .../openapidiff/core/output/HtmlRenderTest.java | 4 +++- .../openapidiff/core/output/MarkdownRenderTest.java | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java index dc0d26a1..cde79e5d 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java @@ -69,6 +69,8 @@ public void issue857_renderWithShowAllChangesShowsChangedOperationDescription() "issue-857-operation-description-1.json", "issue-857-operation-description-2.json"); render.render(diff, outputStreamWriter); assertThat(outputStream.toString()).contains("Changed description:"); - assertThat(outputStream.toString()).contains("Sample description changed"); + assertThat(outputStream.toString()).contains("Sample description"); + assertThat(outputStream.toString()) + .contains("Sample description changed"); } } 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 1ebea992..367ad698 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 @@ -30,9 +30,9 @@ public void issue857_renderShowsChangedOperationDescription() { OpenApiCompare.fromLocations( "issue-857-operation-description-1.json", "issue-857-operation-description-2.json"); render.render(diff, outputStreamWriter); - assertThat(outputStream.toString()).contains("Changed description:"); - assertThat(outputStream.toString()).contains("Sample description"); - assertThat(outputStream.toString()).contains("Sample description changed"); + assertThat(outputStream.toString()) + .contains( + "Changed description:\n> Sample description\n\n\nto:\n> Sample description changed"); } @Test