You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/11/12 20:25:21 UTC
[isis] branch master updated: ISIS-2454: rewriting CorsFilterTest
from scratch, not using any mockery
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 4e2e835 ISIS-2454: rewriting CorsFilterTest from scratch, not using any mockery
4e2e835 is described below
commit 4e2e835697cde6bc73d196b9c8b1a9c1c64fbf04
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Nov 12 21:25:09 2020 +0100
ISIS-2454: rewriting CorsFilterTest from scratch, not using any mockery
---
.../cors/impl/IsisModuleExtCorsImpl.java | 19 +-
.../cors/impl/IsisModuleExtCorsImplTest.java | 4 +-
.../restclient/log/ClientConversationFilter.java | 2 +-
.../RestServiceSimpifiedRepresentationTest.java | 36 ++-
.../isis/testdomain/rospec/RoSpecSampler.java | 7 +-
.../isis/testdomain/rest/CorsFilterTest.java | 268 ++++++++++-----------
6 files changed, 181 insertions(+), 155 deletions(-)
diff --git a/extensions/vro/cors/impl/src/main/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImpl.java b/extensions/vro/cors/impl/src/main/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImpl.java
index 221fc3e..c050bfb 100644
--- a/extensions/vro/cors/impl/src/main/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImpl.java
+++ b/extensions/vro/cors/impl/src/main/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImpl.java
@@ -21,7 +21,6 @@ package org.apache.isis.extensions.cors.impl;
import java.util.Collections;
import java.util.Map;
-import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.Filter;
@@ -36,39 +35,37 @@ import org.springframework.web.filter.CorsFilter;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.core.config.IsisConfiguration;
-import lombok.RequiredArgsConstructor;
import lombok.val;
import lombok.extern.log4j.Log4j2;
@Configuration
@Named("isisMapCors.WebModuleServerCors")
@Qualifier("CORS")
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
@Log4j2
public class IsisModuleExtCorsImpl {
- private final IsisConfiguration configuration;
-
@Bean
- public FilterRegistrationBean<Filter> corsFilterRegistration() {
+ public FilterRegistrationBean<Filter> createCorsFilterRegistration(IsisConfiguration configuration) {
final Map<String, String> cfgMap = configuration.getAsMap();
final String resteasyBase = cfgMap.getOrDefault("resteasy.jaxrs.defaultPath", "/restful/*");
- log.info("Setting up CORS to filter resteasy-base at '{}'", resteasyBase);
+ log.info("Setting up CORS to filter resteasy-base at '{}' with {}",
+ resteasyBase,
+ configuration.getExtensions().getCors());
final FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
- filterRegistrationBean.setFilter(createCorsFilter());
+ filterRegistrationBean.setFilter(createCorsFilter(configuration));
filterRegistrationBean.setUrlPatterns(Collections.singletonList(resteasyBase));
filterRegistrationBean.setOrder(OrderPrecedence.EARLY - 100);
return filterRegistrationBean;
}
- public CorsFilter createCorsFilter() {
+ private CorsFilter createCorsFilter(IsisConfiguration configuration) {
+
+ val isisCorsConfig = configuration.getExtensions().getCors();
val corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
-
- val isisCorsConfig = configuration.getExtensions().getCors();
corsConfiguration.setAllowedHeaders(isisCorsConfig.getAllowedHeaders());
corsConfiguration.setAllowedMethods(isisCorsConfig.getAllowedMethods());
corsConfiguration.setAllowedOrigins(isisCorsConfig.getAllowedOrigins());
diff --git a/extensions/vro/cors/impl/src/test/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImplTest.java b/extensions/vro/cors/impl/src/test/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImplTest.java
index 130abc0..4d213eb 100644
--- a/extensions/vro/cors/impl/src/test/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImplTest.java
+++ b/extensions/vro/cors/impl/src/test/java/org/apache/isis/extensions/cors/impl/IsisModuleExtCorsImplTest.java
@@ -42,10 +42,10 @@ class IsisModuleExtCorsImplTest {
@Test
void defaultIsisConfiguration_shouldYieldCorsUrlPatternWithWildcard() {
// given
- val isisModuleExtCorsImpl = new IsisModuleExtCorsImpl(isisDefaultConfiguration);
+ val isisModuleExtCorsImpl = new IsisModuleExtCorsImpl();
// when
- val filterRegistration = isisModuleExtCorsImpl.corsFilterRegistration();
+ val filterRegistration = isisModuleExtCorsImpl.createCorsFilterRegistration(isisDefaultConfiguration);
// then
assertTrue(filterRegistration.getUrlPatterns().contains("/restful/*"));
diff --git a/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/log/ClientConversationFilter.java b/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/log/ClientConversationFilter.java
index dade8b1..59c0a80 100644
--- a/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/log/ClientConversationFilter.java
+++ b/mappings/restclient/applib/src/main/java/org/apache/isis/extensions/restclient/log/ClientConversationFilter.java
@@ -62,7 +62,7 @@ extends ClientRequestFilter, ClientResponseFilter {
? "Failed to parse accept header, cause: " + acceptableMediaTypeParsingFailure.getMessage()
: "OK";
- final String requestBody = requestContext.getEntity().toString();
+ final String requestBody = "" + requestContext.getEntity();
onRequest(
endpoint, method, acceptHeaderParsing,
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java
index 10172de..4f84b71 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/rest/RestServiceSimpifiedRepresentationTest.java
@@ -109,7 +109,7 @@ class RestServiceSimpifiedRepresentationTest {
System.out.println("==================================================================");
}
- // -- STRING
+ // -- VOID
@Test @Order(1)
void voidResult() {
@@ -127,6 +127,14 @@ class RestServiceSimpifiedRepresentationTest {
assertEquals(refSampler.string(), returnValue);
filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
}
+
+ @Test @Order(2)
+ void stringUsingGet() {
+ val digest = digestUsingGet("stringSafe", String.class);
+ val returnValue = digest.getEntities().getSingletonOrFail();
+ assertEquals(refSampler.stringSafe(), returnValue);
+ filter4ReprType.assertRepresentationType(RepresentationTypeSimplifiedV2.VALUE);
+ }
@Test @Order(3)
void stringNull() {
@@ -357,6 +365,32 @@ class RestServiceSimpifiedRepresentationTest {
return digest;
}
+
+ <T> ResponseDigest<T> digestUsingGet(String actionName, Class<T> entityType) {
+
+ _Probe.errOut("");
+ _Probe.errOut("=== %s", actionName);
+ _Probe.errOut("");
+
+ assertTrue(restService.getPort()>0);
+
+ val useRequestDebugLogging = false;
+ val client = restService.newClient(useRequestDebugLogging, conversationFilters);
+
+ val request = restService.newInvocationBuilder(client,
+ String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName));
+
+ filter4Reporting.next(String.format("@Action\n%s %s {\n /*...*/\n}", entityType.getSimpleName(), actionName));
+
+ val response = request.get();
+ val digest = client.digest(response, entityType);
+
+ if(!digest.isSuccess()) {
+ fail(digest.getFailureCause());
+ }
+
+ return digest;
+ }
<T> ResponseDigest<T> digestArray(
String actionName,
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/rospec/RoSpecSampler.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/rospec/RoSpecSampler.java
index 5643c40..4bd98fd 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/rospec/RoSpecSampler.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/rospec/RoSpecSampler.java
@@ -24,6 +24,7 @@ import java.util.List;
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.DomainService;
import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.commons.internal.collections._Lists;
@DomainService(
@@ -35,7 +36,6 @@ public class RoSpecSampler {
@Action
public void voidResult() {
-
}
// -- STRING
@@ -45,6 +45,11 @@ public class RoSpecSampler {
return "aString";
}
+ @Action(semantics = SemanticsOf.SAFE)
+ public String stringSafe() {
+ return "aSafeString";
+ }
+
@Action
public String stringNull() {
return null;
diff --git a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java
index 05388d5..ec3bda0 100644
--- a/regressiontests/stable/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java
+++ b/regressiontests/stable/src/test/java/org/apache/isis/testdomain/rest/CorsFilterTest.java
@@ -1,185 +1,175 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
*/
package org.apache.isis.testdomain.rest;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
+import java.util.function.UnaryOperator;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
+import javax.inject.Inject;
-import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Import;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.mock.web.MockFilterChain;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.RequestBuilder;
-import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
-import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.filter.CorsFilter;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
+import org.springframework.test.context.TestPropertySource;
+
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.junit.jupiter.api.Assertions.fail;
-import org.apache.isis.core.config.IsisConfiguration;
-import org.apache.isis.core.metamodel._testing.MetaModelContext_forTesting;
-import org.apache.isis.core.metamodel.context.MetaModelContext;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.core.config.presets.IsisPresets;
import org.apache.isis.extensions.cors.impl.IsisModuleExtCorsImpl;
+import org.apache.isis.extensions.restclient.ResponseDigest;
+import org.apache.isis.extensions.restclient.log.ClientConversationFilter;
import org.apache.isis.testdomain.conf.Configuration_headless;
import org.apache.isis.testdomain.rospec.Configuration_usingRoSpec;
+import org.apache.isis.testdomain.rospec.RoSpecSampler;
import org.apache.isis.testdomain.util.rest.RestEndpointService;
import org.apache.isis.viewer.restfulobjects.jaxrsresteasy4.IsisModuleViewerRestfulObjectsJaxrsResteasy4;
-@ActiveProfiles("test")
-@RunWith(SpringRunner.class)
+import lombok.val;
+
@SpringBootTest(
- classes = {RestEndpointService.class, IsisModuleExtCorsImpl.class},
+ classes = {
+ RestEndpointService.class
+ },
+ properties = {
+ //preparing the CORS filter with specific settings for testing
+ "isis.extensions.cors.allowedMethods=POST",
+ "isis.extensions.cors.allowedOrigins=http://www.google.com",
+ },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@TestPropertySource(IsisPresets.UseLog4j2Test)
@Import({
- Configuration_headless.class,
- Configuration_usingRoSpec.class,
- IsisModuleViewerRestfulObjectsJaxrsResteasy4.class,
+ Configuration_headless.class,
+ Configuration_usingRoSpec.class,
+ IsisModuleViewerRestfulObjectsJaxrsResteasy4.class,
+ IsisModuleExtCorsImpl.class
})
-// see: https://github.com/predix/spring-cors-filter/blob/master/src/test/java/com/ge/predix/web/cors/test/CORSFilterTest.java
-// see: https://www.baeldung.com/intercepting-filter-pattern-in-java
-@Disabled
+@TestMethodOrder(OrderAnnotation.class) // run tests in sequence, to ease debugging
class CorsFilterTest {
- @LocalServerPort
- private int port;
-
- @Test
- // FALSE_POSITIVE:
- // regardless what is set in IsisModuleExtCorsIml ("restful" vs. "restful/*)
- // a (sensible) json response is returned
- public void ensureUrlCanBeAccessed() throws Exception{
- final String url = "http://localhost:" + port + "/restful/user";
- final TestRestTemplate testRestTemplate = new TestRestTemplate();
- final ResponseEntity<String> response = testRestTemplate
- //Credentials are not checked, only occurrence of HTTP/BA field in request header
- .withBasicAuth("any", "any")
- .getForEntity(url, String.class);
- assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
- assertTrue(response.getBody().length() > 0);
- }
+ @LocalServerPort int port;
+ @Inject RestEndpointService restService;
- @Autowired
- private WebApplicationContext wac;
-
- @Test
- public void testCors() throws Exception {
- final RequestBuilder requestBuilder = MockMvcRequestBuilders
- .get("http://localhost:" + port + "/restful/user")
- .header("Access-Control-Request-Method", "GET")
- .header("Origin", "http://localhost");
- final CorsFilter corsFilter = createIsisCorsFilter();
- final DefaultMockMvcBuilder builder = MockMvcBuilders
- .webAppContextSetup(this.wac)
- .addFilter(corsFilter, "/**");
- final MockMvc mockMvc = builder.build();
- mockMvc
- .perform(requestBuilder)
- .andDo(print())
- //ServletResource not available. //Set BP in PathResourceResolver.getResource:186
- .andExpect(status().isOk())
- .andExpect(header().string("Access-Control-Allow-Methods", "GET,PUT,DELETE,POST,OPTIONS"));
- }
+ private final RoSpecSampler refSampler = new RoSpecSampler();
+ private final Can<ClientConversationFilter> conversationFilters = Can.empty();
+
+ // -- STRING
- private CorsFilter createIsisCorsFilter() {
- final MetaModelContext metaModelContext = MetaModelContext_forTesting.buildDefault();
- final ConfigurableEnvironment environment = metaModelContext.getConfiguration().getEnvironment();
- final IsisConfiguration configuration = new IsisConfiguration(environment);
- final Map<String, String> map = new HashMap<>();
+ @Test @Order(1)
+ void requestWithValidOriginAndMethod_shouldSucceed() {
+ val digest = digestUsingPost("string", String.class, builder->builder
+ .header("Origin", validOrigin()));
+ assertHttpResponse200(digest);
+ }
- configuration.setIsisSettings(map);
+ @Test @Order(2)
+ void requestWithInvalidOrigin_shouldFail() {
+ val digest = digestUsingPost("string", String.class, builder->builder
+ .header("Origin", invalidOrigin()));
+ assertHttpResponse403(digest);
+ }
+
+ @Test @Order(3)
+ void requestWithMissingOrigin_shouldSucceed() {
+ val digest = digestUsingPost("string", String.class, builder->builder);
+ assertHttpResponse200(digest);
+ }
- final IsisModuleExtCorsImpl isisExtCors = new IsisModuleExtCorsImpl(configuration);
- isisExtCors.corsFilterRegistration();
- return isisExtCors.createCorsFilter();
+ @Test @Order(4) //@Disabled("not sure why the ")
+ void requestWithInvalidMethod_shouldFail() {
+ val digest = digestUsingGet("stringSafe", String.class, builder->builder
+ .header("Origin", validOrigin()));
+ assertHttpResponse403(digest);
}
- @Test
- public void testRequestExpectStandardCorsResponse() throws ServletException, IOException {
- final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/invalid");
- request.addHeader("Origin", "*");
+ // -- HELPER
- final MockHttpServletResponse response = new MockHttpServletResponse();
- final FilterChain filterChain = new MockFilterChain();
- final Filter corsFilter = createIsisCorsFilter();
- corsFilter.doFilter(request, response, filterChain);
+ <T> ResponseDigest<T> digestUsingPost(
+ String actionName,
+ Class<T> entityType,
+ UnaryOperator<javax.ws.rs.client.Invocation.Builder> onRequestBuilder) {
- assertEquals("*", response.getHeaderValue("Access-Control-Allow-Origin"));
- }
+ assertTrue(restService.getPort()>0);
- @Test //requestUrl is not seen as invalid. Why?
- // BP -> CorsFilter.doFilterInternal:87
- public void testRequestWithForbiddenUri() throws ServletException, IOException {
- final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/invalid");
- request.addHeader("Origin", "*");
+ val useRequestDebugLogging = true;
+ val client = restService.newClient(useRequestDebugLogging, conversationFilters);
- final MockHttpServletResponse response = new MockHttpServletResponse();
- final Filter corsFilter = createIsisCorsFilter();
- final FilterChain filterChain = new FilterChainImpl(corsFilter);
- corsFilter.doFilter(request, response, filterChain);
+ val request = onRequestBuilder.apply(
+ restService.newInvocationBuilder(client,
+ String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName)));
+
+ val args = client.arguments()
+ .build();
+
+ val response = request.post(args);
+ val digest = client.digest(response, entityType);
+
+ return digest;
- assertEquals(403, response.getStatus());
}
+
+ <T> ResponseDigest<T> digestUsingGet(
+ String actionName,
+ Class<T> entityType,
+ UnaryOperator<javax.ws.rs.client.Invocation.Builder> onRequestBuilder) {
- class FilterChainImpl implements FilterChain {
- private Iterator<Filter> filters;
+ assertTrue(restService.getPort()>0);
- public FilterChainImpl(Filter... filters) {
- this.filters = Arrays.asList(filters).iterator();
- }
+ val useRequestDebugLogging = true;
+ val client = restService.newClient(useRequestDebugLogging, conversationFilters);
+
+ val request = onRequestBuilder.apply(
+ restService.newInvocationBuilder(client,
+ String.format("services/testdomain.RoSpecSampler/actions/%s/invoke", actionName)));
+
+ val response = request.get();
+ val digest = client.digest(response, entityType);
- @Override
- public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
- if (filters.hasNext()) {
- Filter filter = filters.next();
- filter.doFilter(request, response, this);
- }
+ return digest;
+ }
+
+
+ private String validOrigin() {
+ return "http://www.google.com";
+ }
+
+ private String invalidOrigin() {
+ return "http://localhost";
+ }
+
+ private void assertHttpResponse200(ResponseDigest<String> digest) {
+ if(!digest.isSuccess()) {
+ fail(digest.getFailureCause());
}
+ val returnValue = digest.getEntities().getSingletonOrFail();
+ assertEquals(refSampler.string(), returnValue);
+ }
+
+ private void assertHttpResponse403(ResponseDigest<String> digest) {
+ assertNotNull(digest.getFailureCause(), "request was expected to fail, but succeeded");
+ assertTrue(digest.getFailureCause().getMessage().contains("403"));
}
}