Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -98,7 +99,7 @@ public List<OpenApiViolation> 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);
Expand Down Expand Up @@ -145,7 +146,7 @@ public List<OpenApiViolation> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -51,49 +55,178 @@ 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<>());
@Nested
@DisplayName("validateRequestObject")
public 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 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(createRequest(), 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 violationIgnored = createViolation(LogLevel.IGNORE);
var violationError = createViolation(LogLevel.ERROR);
mockRequestValidation(List.of(violationIgnored, violationError));

var result = openApiRequestValidator.validateRequestObject(createRequest(), null);

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 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(createRequest(), null);

assertSingleViolationReturned(result, violationError);
}

@Test
@DisplayName("When all violations are ignored then empty list is returned")
public void testWhenAllRequestViolationsAreIgnoredThenEmptyListIsReturned() {
var violation1 = createViolation(LogLevel.IGNORE);
var violation2 = createViolation(LogLevel.IGNORE);
mockRequestValidation(List.of(violation1, violation2));

var result = openApiRequestValidator.validateRequestObject(createRequest(), null);

assertNoViolationsReturned(result);
}
}

@Nested
@DisplayName("validateResponseObject")
public class ValidateResponseObjectTests {

@Test
@DisplayName("When violation has log level IGNORE then it should not be returned")
public void testWhenResponseViolationHasLogLevelIgnoreThenItShouldNotBeReturned() {
var violationIgnored = createViolation(LogLevel.IGNORE);
var violationWarn = createViolation(LogLevel.WARN);
mockResponseValidation(List.of(violationIgnored, violationWarn));

var result = executeValidateResponseObject();

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 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 = executeValidateResponseObject();

assertSingleViolationReturned(result, violationError);
}

@Test
@DisplayName("When all violations are ignored then empty list is returned")
public void testWhenAllResponseViolationsAreIgnoredThenEmptyListIsReturned() {
var violation1 = createViolation(LogLevel.IGNORE);
var violation2 = createViolation(LogLevel.IGNORE);
mockResponseValidation(List.of(violation1, violation2));

var result = executeValidateResponseObject();

assertNoViolationsReturned(result);
}

private List<OpenApiViolation> executeValidateResponseObject() {
var request = createRequest();
var response = createResponse();
return openApiRequestValidator.validateResponseObject(request, response, null);
}
}

openApiRequestValidator.validateRequestObject(request, null);
private void verifyQueryParamValueEquals(
ArgumentCaptor<SimpleRequest> simpleRequestArgumentCaptor,
String name,
String expected
) {
var ids = simpleRequestArgumentCaptor.getValue().getQueryParameterValues(name).iterator().next();
assertEquals(expected, ids);
}

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");
private OpenApiViolation createViolation(LogLevel level) {
var violation = mock(OpenApiViolation.class);
when(violation.getLevel()).thenReturn(level);
return violation;
}

@Test
public void testWhenViolationIsExcludedThenItShouldNotBeReturned() {
private RequestMetaData createRequest() {
var uri = URI.create("https://api.example.com/path");
var request = new RequestMetaData("GET", uri, new HashMap<>());
return new RequestMetaData("GET", uri, new HashMap<>());
}

private ResponseMetaData createResponse() {
return new ResponseMetaData(200, "application/json", new HashMap<>());
}

private void mockRequestValidation(List<OpenApiViolation> violations) {
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);
private void mockResponseValidation(List<OpenApiViolation> 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<OpenApiViolation> result, OpenApiViolation expected) {
assertEquals(1, result.size());
assertEquals(violations.get(1), result.getFirst());
assertEquals(expected, result.getFirst());
}

private void verifyQueryParamValueEquals(
ArgumentCaptor<SimpleRequest> simpleRequestArgumentCaptor,
String name,
String expected
) {
var ids = simpleRequestArgumentCaptor.getValue().getQueryParameterValues(name).iterator().next();
assertEquals(expected, ids);
private void assertNoViolationsReturned(List<OpenApiViolation> result) {
assertEquals(0, result.size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -39,7 +39,7 @@ public class FailOnViolationIntegrationTest {
@Autowired
private TestViolationLogger openApiViolationLogger;

@SpyBean
@MockitoSpyBean
private DefaultRestController defaultRestController;

@BeforeEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -33,7 +33,7 @@ public class FailOnViolationIntegrationTest {
@Autowired
private TestViolationLogger openApiViolationLogger;

@SpyBean
@MockitoSpyBean
private DefaultRestController defaultRestController;

@BeforeEach
Expand Down
Loading