From f3b33727cc9dcd7a8dc78fa00e5b3e9b3be49e6b Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 27 Oct 2025 17:57:51 +0100 Subject: [PATCH 1/7] use MockitoSpyBean instead of deprecated SpyBean --- .../integration/FailOnViolationIntegrationTest.java | 4 ++-- .../integration/FailOnViolationIntegrationTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-boot-starter/spring-boot-starter-web/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java b/spring-boot-starter/spring-boot-starter-web/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java index f29c4369..05043e9f 100644 --- a/spring-boot-starter/spring-boot-starter-web/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java +++ b/spring-boot-starter/spring-boot-starter-web/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java @@ -20,8 +20,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; @@ -39,7 +39,7 @@ public class FailOnViolationIntegrationTest { @Autowired private TestViolationLogger openApiViolationLogger; - @SpyBean + @MockitoSpyBean private DefaultRestController defaultRestController; @BeforeEach diff --git a/spring-boot-starter/spring-boot-starter-webflux/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java b/spring-boot-starter/spring-boot-starter-webflux/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java index 602236b3..f110c512 100644 --- a/spring-boot-starter/spring-boot-starter-webflux/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java +++ b/spring-boot-starter/spring-boot-starter-webflux/src/test/java/com/getyourguide/openapi/validation/integration/FailOnViolationIntegrationTest.java @@ -14,8 +14,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.reactive.server.WebTestClient; @@ -33,7 +33,7 @@ public class FailOnViolationIntegrationTest { @Autowired private TestViolationLogger openApiViolationLogger; - @SpyBean + @MockitoSpyBean private DefaultRestController defaultRestController; @BeforeEach From 8c40d4ee5fc4092460cec717e11b5c83314dc25c Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 27 Oct 2025 17:58:13 +0100 Subject: [PATCH 2/7] fix: do not return violations that should be ignored --- .../openapi/validation/core/OpenApiRequestValidator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java index 41800da9..882fe8cc 100644 --- a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java @@ -3,6 +3,7 @@ import com.atlassian.oai.validator.model.Request; import com.atlassian.oai.validator.model.SimpleRequest; import com.atlassian.oai.validator.model.SimpleResponse; +import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.log.OpenApiViolationHandler; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; import com.getyourguide.openapi.validation.api.model.Direction; @@ -98,7 +99,7 @@ public List validateRequestObject( var result = validator.validateRequest(simpleRequest); var violations = mapper.map(result, request, response, Direction.REQUEST, requestBody); return violations.stream() - .filter(violation -> !violationExclusions.isExcluded(violation)) + .filter(violation -> !LogLevel.IGNORE.equals(violation.getLevel()) && !violationExclusions.isExcluded(violation)) .toList(); } catch (Exception e) { log.error("[OpenAPI Validation] Could not validate request", e); @@ -145,7 +146,7 @@ public List validateResponseObject( ); var violations = mapper.map(result, request, response, Direction.RESPONSE, responseBody); return violations.stream() - .filter(violation -> !violationExclusions.isExcluded(violation)) + .filter(violation -> !LogLevel.IGNORE.equals(violation.getLevel()) && !violationExclusions.isExcluded(violation)) .toList(); } catch (Exception e) { log.error("[OpenAPI Validation] Could not validate response", e); From 30bb227ac07c968c351d59759f61bdc52150891f Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 27 Oct 2025 18:31:48 +0100 Subject: [PATCH 3/7] test: add tests for LogLevel.IGNORE filtering and organize test structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive tests for filtering violations with LogLevel.IGNORE in both request and response validation - Organize tests using @Nested and @DisplayName for better readability - Create createViolation helper method to reduce test code duplication - Tests verify that violations with LogLevel.IGNORE are not returned to prevent 400 errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../core/OpenApiRequestValidatorTest.java | 202 +++++++++++++++--- 1 file changed, 175 insertions(+), 27 deletions(-) diff --git a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java index 9b2e4882..71d37291 100644 --- a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java +++ b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java @@ -8,8 +8,10 @@ import com.atlassian.oai.validator.model.SimpleRequest; import com.atlassian.oai.validator.report.ValidationReport; +import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.model.OpenApiViolation; import com.getyourguide.openapi.validation.api.model.RequestMetaData; +import com.getyourguide.openapi.validation.api.model.ResponseMetaData; import com.getyourguide.openapi.validation.core.exclusions.InternalViolationExclusions; import com.getyourguide.openapi.validation.core.mapper.ValidationReportToOpenApiViolationsMapper; import com.getyourguide.openapi.validation.core.validator.OpenApiInteractionValidatorWrapper; @@ -19,6 +21,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -51,41 +55,179 @@ public void setup() { } @Test + @DisplayName("When thread pool executor rejects execution then it should not throw") public void testWhenThreadPoolExecutorRejectsExecutionThenItShouldNotThrow() { Mockito.doThrow(new RejectedExecutionException()).when(executor).execute(any()); openApiRequestValidator.validateRequestObjectAsync(mock(), null, null, mock()); } - @Test - public void testWhenEncodedQueryParamIsPassedThenValidationShouldHappenWithQueryParamDecoded() { - var uri = URI.create("https://api.example.com?ids=1%2C2%2C3&text=e%3Dmc2%20%26%20more&spaces=this+is+a+sparta"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - - openApiRequestValidator.validateRequestObject(request, null); - - var simpleRequestArgumentCaptor = ArgumentCaptor.forClass(SimpleRequest.class); - verify(validator).validateRequest(simpleRequestArgumentCaptor.capture()); - verifyQueryParamValueEquals(simpleRequestArgumentCaptor, "ids", "1,2,3"); - verifyQueryParamValueEquals(simpleRequestArgumentCaptor, "text", "e=mc2 & more"); - verifyQueryParamValueEquals(simpleRequestArgumentCaptor, "spaces", "this is a sparta"); + @Nested + @DisplayName("validateRequestObject") + class ValidateRequestObjectTests { + + @Test + @DisplayName("When encoded query param is passed then validation should happen with query param decoded") + public void testWhenEncodedQueryParamIsPassedThenValidationShouldHappenWithQueryParamDecoded() { + var uri = URI.create("https://api.example.com?ids=1%2C2%2C3&text=e%3Dmc2%20%26%20more&spaces=this+is+a+sparta"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + + openApiRequestValidator.validateRequestObject(request, null); + + var simpleRequestArgumentCaptor = ArgumentCaptor.forClass(SimpleRequest.class); + verify(validator).validateRequest(simpleRequestArgumentCaptor.capture()); + verifyQueryParamValueEquals(simpleRequestArgumentCaptor, "ids", "1,2,3"); + verifyQueryParamValueEquals(simpleRequestArgumentCaptor, "text", "e=mc2 & more"); + verifyQueryParamValueEquals(simpleRequestArgumentCaptor, "spaces", "this is a sparta"); + } + + @Test + @DisplayName("When violation is excluded then it should not be returned") + public void testWhenViolationIsExcludedThenItShouldNotBeReturned() { + var uri = URI.create("https://api.example.com/path"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + var validationReport = mock(ValidationReport.class); + when(validator.validateRequest(any())).thenReturn(validationReport); + var violationExcluded = mock(OpenApiViolation.class); + var violations = List.of(violationExcluded, mock(OpenApiViolation.class)); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); + + var result = openApiRequestValidator.validateRequestObject(request, null); + + assertEquals(1, result.size()); + assertEquals(violations.get(1), result.getFirst()); + } + + @Test + @DisplayName("When violation has log level IGNORE then it should not be returned") + public void testWhenRequestViolationHasLogLevelIgnoreThenItShouldNotBeReturned() { + var uri = URI.create("https://api.example.com/path"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + var validationReport = mock(ValidationReport.class); + when(validator.validateRequest(any())).thenReturn(validationReport); + + var violationIgnored = createViolation(LogLevel.IGNORE); + var violationError = createViolation(LogLevel.ERROR); + + var violations = List.of(violationIgnored, violationError); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + + var result = openApiRequestValidator.validateRequestObject(request, null); + + assertEquals(1, result.size()); + assertEquals(violationError, result.getFirst()); + } + + @Test + @DisplayName("When violation has log level IGNORE and another is excluded then both should not be returned") + public void testWhenRequestViolationHasLogLevelIgnoreAndIsExcludedThenItShouldNotBeReturned() { + var uri = URI.create("https://api.example.com/path"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + var validationReport = mock(ValidationReport.class); + when(validator.validateRequest(any())).thenReturn(validationReport); + + var violationIgnored = createViolation(LogLevel.IGNORE); + var violationExcluded = createViolation(LogLevel.WARN); + when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); + var violationError = createViolation(LogLevel.ERROR); + + var violations = List.of(violationIgnored, violationExcluded, violationError); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + + var result = openApiRequestValidator.validateRequestObject(request, null); + + assertEquals(1, result.size()); + assertEquals(violationError, result.getFirst()); + } + + @Test + @DisplayName("When all violations are ignored then empty list is returned") + public void testWhenAllRequestViolationsAreIgnoredThenEmptyListIsReturned() { + var uri = URI.create("https://api.example.com/path"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + var validationReport = mock(ValidationReport.class); + when(validator.validateRequest(any())).thenReturn(validationReport); + + var violation1 = createViolation(LogLevel.IGNORE); + var violation2 = createViolation(LogLevel.IGNORE); + + var violations = List.of(violation1, violation2); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + + var result = openApiRequestValidator.validateRequestObject(request, null); + + assertEquals(0, result.size()); + } } - @Test - public void testWhenViolationIsExcludedThenItShouldNotBeReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - var validationReport = mock(ValidationReport.class); - when(validator.validateRequest(any())).thenReturn(validationReport); - var violationExcluded = mock(OpenApiViolation.class); - var violations = List.of(violationExcluded, mock(OpenApiViolation.class)); - when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); - when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); - - var result = openApiRequestValidator.validateRequestObject(request, null); - - assertEquals(1, result.size()); - assertEquals(violations.get(1), result.getFirst()); + @Nested + @DisplayName("validateResponseObject") + class ValidateResponseObjectTests { + + @Test + @DisplayName("When violation has log level IGNORE then it should not be returned") + public void testWhenResponseViolationHasLogLevelIgnoreThenItShouldNotBeReturned() { + var uri = URI.create("https://api.example.com/path"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + var response = new ResponseMetaData(200, "application/json", new HashMap<>()); + var validationReport = mock(ValidationReport.class); + when(validator.validateResponse(any(), any(), any())).thenReturn(validationReport); + + var violationIgnored = createViolation(LogLevel.IGNORE); + var violationWarn = createViolation(LogLevel.WARN); + + var violations = List.of(violationIgnored, violationWarn); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + + var result = openApiRequestValidator.validateResponseObject(request, response, null); + + assertEquals(1, result.size()); + assertEquals(violationWarn, result.getFirst()); + } + + @Test + @DisplayName("When violation has log level IGNORE and another is excluded then both should not be returned") + public void testWhenResponseViolationHasLogLevelIgnoreAndIsExcludedThenItShouldNotBeReturned() { + var uri = URI.create("https://api.example.com/path"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + var response = new ResponseMetaData(200, "application/json", new HashMap<>()); + var validationReport = mock(ValidationReport.class); + when(validator.validateResponse(any(), any(), any())).thenReturn(validationReport); + + var violationIgnored = createViolation(LogLevel.IGNORE); + var violationExcluded = createViolation(LogLevel.INFO); + when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); + var violationError = createViolation(LogLevel.ERROR); + + var violations = List.of(violationIgnored, violationExcluded, violationError); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + + var result = openApiRequestValidator.validateResponseObject(request, response, null); + + assertEquals(1, result.size()); + assertEquals(violationError, result.getFirst()); + } + + @Test + @DisplayName("When all violations are ignored then empty list is returned") + public void testWhenAllResponseViolationsAreIgnoredThenEmptyListIsReturned() { + var uri = URI.create("https://api.example.com/path"); + var request = new RequestMetaData("GET", uri, new HashMap<>()); + var response = new ResponseMetaData(200, "application/json", new HashMap<>()); + var validationReport = mock(ValidationReport.class); + when(validator.validateResponse(any(), any(), any())).thenReturn(validationReport); + + var violation1 = createViolation(LogLevel.IGNORE); + var violation2 = createViolation(LogLevel.IGNORE); + + var violations = List.of(violation1, violation2); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + + var result = openApiRequestValidator.validateResponseObject(request, response, null); + + assertEquals(0, result.size()); + } } private void verifyQueryParamValueEquals( @@ -96,4 +238,10 @@ private void verifyQueryParamValueEquals( var ids = simpleRequestArgumentCaptor.getValue().getQueryParameterValues(name).iterator().next(); assertEquals(expected, ids); } + + private OpenApiViolation createViolation(LogLevel level) { + var violation = mock(OpenApiViolation.class); + when(violation.getLevel()).thenReturn(level); + return violation; + } } From b511080331eac9c4fd7798d92d975f613746a203 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 27 Oct 2025 18:35:17 +0100 Subject: [PATCH 4/7] refactor: extract helper methods in OpenApiRequestValidatorTest for better readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add createRequest() and createResponse() helper methods to reduce test setup duplication - Add mockRequestValidation() and mockResponseValidation() to simplify mock configuration - Add assertSingleViolationReturned() and assertNoViolationsReturned() assertion helpers - Simplify test methods by reducing boilerplate and improving focus on test intent 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../core/OpenApiRequestValidatorTest.java | 112 ++++++++---------- 1 file changed, 51 insertions(+), 61 deletions(-) diff --git a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java index 71d37291..2d1cef3b 100644 --- a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java +++ b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java @@ -102,62 +102,42 @@ public void testWhenViolationIsExcludedThenItShouldNotBeReturned() { @Test @DisplayName("When violation has log level IGNORE then it should not be returned") public void testWhenRequestViolationHasLogLevelIgnoreThenItShouldNotBeReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - var validationReport = mock(ValidationReport.class); - when(validator.validateRequest(any())).thenReturn(validationReport); - + var request = createRequest("/path"); var violationIgnored = createViolation(LogLevel.IGNORE); var violationError = createViolation(LogLevel.ERROR); - - var violations = List.of(violationIgnored, violationError); - when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + mockRequestValidation(List.of(violationIgnored, violationError)); var result = openApiRequestValidator.validateRequestObject(request, null); - assertEquals(1, result.size()); - assertEquals(violationError, result.getFirst()); + assertSingleViolationReturned(result, violationError); } @Test @DisplayName("When violation has log level IGNORE and another is excluded then both should not be returned") public void testWhenRequestViolationHasLogLevelIgnoreAndIsExcludedThenItShouldNotBeReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - var validationReport = mock(ValidationReport.class); - when(validator.validateRequest(any())).thenReturn(validationReport); - + var request = createRequest("/path"); var violationIgnored = createViolation(LogLevel.IGNORE); var violationExcluded = createViolation(LogLevel.WARN); when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); var violationError = createViolation(LogLevel.ERROR); - - var violations = List.of(violationIgnored, violationExcluded, violationError); - when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + mockRequestValidation(List.of(violationIgnored, violationExcluded, violationError)); var result = openApiRequestValidator.validateRequestObject(request, null); - assertEquals(1, result.size()); - assertEquals(violationError, result.getFirst()); + assertSingleViolationReturned(result, violationError); } @Test @DisplayName("When all violations are ignored then empty list is returned") public void testWhenAllRequestViolationsAreIgnoredThenEmptyListIsReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - var validationReport = mock(ValidationReport.class); - when(validator.validateRequest(any())).thenReturn(validationReport); - + var request = createRequest("/path"); var violation1 = createViolation(LogLevel.IGNORE); var violation2 = createViolation(LogLevel.IGNORE); - - var violations = List.of(violation1, violation2); - when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + mockRequestValidation(List.of(violation1, violation2)); var result = openApiRequestValidator.validateRequestObject(request, null); - assertEquals(0, result.size()); + assertNoViolationsReturned(result); } } @@ -168,65 +148,45 @@ class ValidateResponseObjectTests { @Test @DisplayName("When violation has log level IGNORE then it should not be returned") public void testWhenResponseViolationHasLogLevelIgnoreThenItShouldNotBeReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - var response = new ResponseMetaData(200, "application/json", new HashMap<>()); - var validationReport = mock(ValidationReport.class); - when(validator.validateResponse(any(), any(), any())).thenReturn(validationReport); - + var request = createRequest("/path"); + var response = createResponse(); var violationIgnored = createViolation(LogLevel.IGNORE); var violationWarn = createViolation(LogLevel.WARN); - - var violations = List.of(violationIgnored, violationWarn); - when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + mockResponseValidation(List.of(violationIgnored, violationWarn)); var result = openApiRequestValidator.validateResponseObject(request, response, null); - assertEquals(1, result.size()); - assertEquals(violationWarn, result.getFirst()); + assertSingleViolationReturned(result, violationWarn); } @Test @DisplayName("When violation has log level IGNORE and another is excluded then both should not be returned") public void testWhenResponseViolationHasLogLevelIgnoreAndIsExcludedThenItShouldNotBeReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - var response = new ResponseMetaData(200, "application/json", new HashMap<>()); - var validationReport = mock(ValidationReport.class); - when(validator.validateResponse(any(), any(), any())).thenReturn(validationReport); - + var request = createRequest("/path"); + var response = createResponse(); var violationIgnored = createViolation(LogLevel.IGNORE); var violationExcluded = createViolation(LogLevel.INFO); when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); var violationError = createViolation(LogLevel.ERROR); - - var violations = List.of(violationIgnored, violationExcluded, violationError); - when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + mockResponseValidation(List.of(violationIgnored, violationExcluded, violationError)); var result = openApiRequestValidator.validateResponseObject(request, response, null); - assertEquals(1, result.size()); - assertEquals(violationError, result.getFirst()); + assertSingleViolationReturned(result, violationError); } @Test @DisplayName("When all violations are ignored then empty list is returned") public void testWhenAllResponseViolationsAreIgnoredThenEmptyListIsReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); - var response = new ResponseMetaData(200, "application/json", new HashMap<>()); - var validationReport = mock(ValidationReport.class); - when(validator.validateResponse(any(), any(), any())).thenReturn(validationReport); - + var request = createRequest("/path"); + var response = createResponse(); var violation1 = createViolation(LogLevel.IGNORE); var violation2 = createViolation(LogLevel.IGNORE); - - var violations = List.of(violation1, violation2); - when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + mockResponseValidation(List.of(violation1, violation2)); var result = openApiRequestValidator.validateResponseObject(request, response, null); - assertEquals(0, result.size()); + assertNoViolationsReturned(result); } } @@ -244,4 +204,34 @@ private OpenApiViolation createViolation(LogLevel level) { when(violation.getLevel()).thenReturn(level); return violation; } + + private RequestMetaData createRequest(String path) { + var uri = URI.create("https://api.example.com" + path); + return new RequestMetaData("GET", uri, new HashMap<>()); + } + + private ResponseMetaData createResponse() { + return new ResponseMetaData(200, "application/json", new HashMap<>()); + } + + private void mockRequestValidation(List violations) { + var validationReport = mock(ValidationReport.class); + when(validator.validateRequest(any())).thenReturn(validationReport); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + } + + private void mockResponseValidation(List violations) { + var validationReport = mock(ValidationReport.class); + when(validator.validateResponse(any(), any(), any())).thenReturn(validationReport); + when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); + } + + private void assertSingleViolationReturned(List result, OpenApiViolation expected) { + assertEquals(1, result.size()); + assertEquals(expected, result.getFirst()); + } + + private void assertNoViolationsReturned(List result) { + assertEquals(0, result.size()); + } } From 113f37caba8c39b3d0ae27bce6271a4c4ee8a355 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 27 Oct 2025 18:40:21 +0100 Subject: [PATCH 5/7] simplify test code by extracting methods --- .../core/OpenApiRequestValidatorTest.java | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java index 2d1cef3b..eaef0472 100644 --- a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java +++ b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java @@ -84,8 +84,7 @@ public void testWhenEncodedQueryParamIsPassedThenValidationShouldHappenWithQuery @Test @DisplayName("When violation is excluded then it should not be returned") public void testWhenViolationIsExcludedThenItShouldNotBeReturned() { - var uri = URI.create("https://api.example.com/path"); - var request = new RequestMetaData("GET", uri, new HashMap<>()); + ; var validationReport = mock(ValidationReport.class); when(validator.validateRequest(any())).thenReturn(validationReport); var violationExcluded = mock(OpenApiViolation.class); @@ -93,7 +92,7 @@ public void testWhenViolationIsExcludedThenItShouldNotBeReturned() { when(mapper.map(any(), any(), any(), any(), any())).thenReturn(violations); when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); - var result = openApiRequestValidator.validateRequestObject(request, null); + var result = openApiRequestValidator.validateRequestObject(createRequest(), null); assertEquals(1, result.size()); assertEquals(violations.get(1), result.getFirst()); @@ -102,12 +101,11 @@ public void testWhenViolationIsExcludedThenItShouldNotBeReturned() { @Test @DisplayName("When violation has log level IGNORE then it should not be returned") public void testWhenRequestViolationHasLogLevelIgnoreThenItShouldNotBeReturned() { - var request = createRequest("/path"); var violationIgnored = createViolation(LogLevel.IGNORE); var violationError = createViolation(LogLevel.ERROR); mockRequestValidation(List.of(violationIgnored, violationError)); - var result = openApiRequestValidator.validateRequestObject(request, null); + var result = openApiRequestValidator.validateRequestObject(createRequest(), null); assertSingleViolationReturned(result, violationError); } @@ -115,14 +113,13 @@ public void testWhenRequestViolationHasLogLevelIgnoreThenItShouldNotBeReturned() @Test @DisplayName("When violation has log level IGNORE and another is excluded then both should not be returned") public void testWhenRequestViolationHasLogLevelIgnoreAndIsExcludedThenItShouldNotBeReturned() { - var request = createRequest("/path"); var violationIgnored = createViolation(LogLevel.IGNORE); var violationExcluded = createViolation(LogLevel.WARN); when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); var violationError = createViolation(LogLevel.ERROR); mockRequestValidation(List.of(violationIgnored, violationExcluded, violationError)); - var result = openApiRequestValidator.validateRequestObject(request, null); + var result = openApiRequestValidator.validateRequestObject(createRequest(), null); assertSingleViolationReturned(result, violationError); } @@ -130,12 +127,11 @@ public void testWhenRequestViolationHasLogLevelIgnoreAndIsExcludedThenItShouldNo @Test @DisplayName("When all violations are ignored then empty list is returned") public void testWhenAllRequestViolationsAreIgnoredThenEmptyListIsReturned() { - var request = createRequest("/path"); var violation1 = createViolation(LogLevel.IGNORE); var violation2 = createViolation(LogLevel.IGNORE); mockRequestValidation(List.of(violation1, violation2)); - var result = openApiRequestValidator.validateRequestObject(request, null); + var result = openApiRequestValidator.validateRequestObject(createRequest(), null); assertNoViolationsReturned(result); } @@ -148,13 +144,11 @@ class ValidateResponseObjectTests { @Test @DisplayName("When violation has log level IGNORE then it should not be returned") public void testWhenResponseViolationHasLogLevelIgnoreThenItShouldNotBeReturned() { - var request = createRequest("/path"); - var response = createResponse(); var violationIgnored = createViolation(LogLevel.IGNORE); var violationWarn = createViolation(LogLevel.WARN); mockResponseValidation(List.of(violationIgnored, violationWarn)); - var result = openApiRequestValidator.validateResponseObject(request, response, null); + var result = executeValidateResponseObject(); assertSingleViolationReturned(result, violationWarn); } @@ -162,15 +156,13 @@ public void testWhenResponseViolationHasLogLevelIgnoreThenItShouldNotBeReturned( @Test @DisplayName("When violation has log level IGNORE and another is excluded then both should not be returned") public void testWhenResponseViolationHasLogLevelIgnoreAndIsExcludedThenItShouldNotBeReturned() { - var request = createRequest("/path"); - var response = createResponse(); var violationIgnored = createViolation(LogLevel.IGNORE); var violationExcluded = createViolation(LogLevel.INFO); when(internalViolationExclusions.isExcluded(violationExcluded)).thenReturn(true); var violationError = createViolation(LogLevel.ERROR); mockResponseValidation(List.of(violationIgnored, violationExcluded, violationError)); - var result = openApiRequestValidator.validateResponseObject(request, response, null); + var result = executeValidateResponseObject(); assertSingleViolationReturned(result, violationError); } @@ -178,16 +170,20 @@ public void testWhenResponseViolationHasLogLevelIgnoreAndIsExcludedThenItShouldN @Test @DisplayName("When all violations are ignored then empty list is returned") public void testWhenAllResponseViolationsAreIgnoredThenEmptyListIsReturned() { - var request = createRequest("/path"); - var response = createResponse(); var violation1 = createViolation(LogLevel.IGNORE); var violation2 = createViolation(LogLevel.IGNORE); mockResponseValidation(List.of(violation1, violation2)); - var result = openApiRequestValidator.validateResponseObject(request, response, null); + var result = executeValidateResponseObject(); assertNoViolationsReturned(result); } + + private List executeValidateResponseObject() { + var request = createRequest(); + var response = createResponse(); + return openApiRequestValidator.validateResponseObject(request, response, null); + } } private void verifyQueryParamValueEquals( @@ -205,8 +201,8 @@ private OpenApiViolation createViolation(LogLevel level) { return violation; } - private RequestMetaData createRequest(String path) { - var uri = URI.create("https://api.example.com" + path); + private RequestMetaData createRequest() { + var uri = URI.create("https://api.example.com/path"); return new RequestMetaData("GET", uri, new HashMap<>()); } From 8e6d120c61b5e5d0d1c6006e29e6fb5fe53a10fe Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 27 Oct 2025 18:46:06 +0100 Subject: [PATCH 6/7] required changes surfaced by PMD --- .../openapi/validation/core/OpenApiRequestValidatorTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java index eaef0472..652a5c2c 100644 --- a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java +++ b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidatorTest.java @@ -64,7 +64,7 @@ public void testWhenThreadPoolExecutorRejectsExecutionThenItShouldNotThrow() { @Nested @DisplayName("validateRequestObject") - class ValidateRequestObjectTests { + public class ValidateRequestObjectTests { @Test @DisplayName("When encoded query param is passed then validation should happen with query param decoded") @@ -84,7 +84,6 @@ public void testWhenEncodedQueryParamIsPassedThenValidationShouldHappenWithQuery @Test @DisplayName("When violation is excluded then it should not be returned") public void testWhenViolationIsExcludedThenItShouldNotBeReturned() { - ; var validationReport = mock(ValidationReport.class); when(validator.validateRequest(any())).thenReturn(validationReport); var violationExcluded = mock(OpenApiViolation.class); @@ -139,7 +138,7 @@ public void testWhenAllRequestViolationsAreIgnoredThenEmptyListIsReturned() { @Nested @DisplayName("validateResponseObject") - class ValidateResponseObjectTests { + public class ValidateResponseObjectTests { @Test @DisplayName("When violation has log level IGNORE then it should not be returned") From a824063aac4014a5be7246d530537b6e465a20c8 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 27 Oct 2025 18:56:27 +0100 Subject: [PATCH 7/7] extract method --- .../openapi/validation/core/OpenApiRequestValidator.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java index 882fe8cc..ac730ba8 100644 --- a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java @@ -99,7 +99,7 @@ public List validateRequestObject( var result = validator.validateRequest(simpleRequest); var violations = mapper.map(result, request, response, Direction.REQUEST, requestBody); return violations.stream() - .filter(violation -> !LogLevel.IGNORE.equals(violation.getLevel()) && !violationExclusions.isExcluded(violation)) + .filter(this::isNonExcludedViolation) .toList(); } catch (Exception e) { log.error("[OpenAPI Validation] Could not validate request", e); @@ -146,11 +146,15 @@ public List validateResponseObject( ); var violations = mapper.map(result, request, response, Direction.RESPONSE, responseBody); return violations.stream() - .filter(violation -> !LogLevel.IGNORE.equals(violation.getLevel()) && !violationExclusions.isExcluded(violation)) + .filter(this::isNonExcludedViolation) .toList(); } catch (Exception e) { log.error("[OpenAPI Validation] Could not validate response", e); return List.of(); } } + + private boolean isNonExcludedViolation(OpenApiViolation violation) { + return !LogLevel.IGNORE.equals(violation.getLevel()) && !violationExclusions.isExcluded(violation); + } }