package wf.config; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.Scope; import org.springframework.http.client.reactive.ClientHttpConnector; import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.reactive.server.WebTestClientConfigurer; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; import reactor.core.publisher.Mono; import wf.util.ConditionalOnMissingBean; /** * Ref * <a href="https://stackoverflow.com/questions/50214030/accessing-the-websession-in-webfluxtest"> * accessing-the-websession-in-webfluxtest</a> <br> * WebTestClientConfigurer enable @WebFluxTest do the following:<br> * webTestClient.mutateWith(sessionMutator(sessionBuilder().put("sessionKey", * "sessionValue").build())) */ @Configuration @ImportResource("classpath:applicationContext.xml") @Import({WebConfig.class, H2R2dbConfig.class, WsConfig.class}) public class TestContext implements WebTestClientConfigurer { static { reactor.util.Loggers.useSl4jLoggers(); } @Bean("testAuthenticationPrincipalResolver") @ConditionalOnMissingBean(AuthenticationPrincipalArgumentResolver.class) public HandlerMethodArgumentResolver authenticationPrincipalArgumentResolver(BeanFactory beanFactory) { return new TestAuthenticationPrincipalResolver(beanFactory); } /** * ObjectMapper 是 ThreadSafe,但為了效能考量 ,還是定義為PROTOTYPE * * @return */ @Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); return mapper; } private static Map<String, Object> sessionMap; public TestContext() { TestContext.sessionMap = new ConcurrentHashMap<>(); } private TestContext(final Map<String, Object> sessionMap) { TestContext.sessionMap = sessionMap; } public static TestContext sessionMutator(final Map<String, Object> sessionMap) { return new TestContext(sessionMap); } @Override public void afterConfigurerAdded(final WebTestClient.Builder builder, final WebHttpHandlerBuilder httpHandlerBuilder, final ClientHttpConnector connector) { final SessionMutatorFilter sessionMutatorFilter = new SessionMutatorFilter(); httpHandlerBuilder.filters(filters -> filters.add(0, sessionMutatorFilter)); } public static ImmutableMap.Builder<String, Object> sessionBuilder() { return new ImmutableMap.Builder<>(); } private static class SessionMutatorFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain webFilterChain) { return exchange.getSession() .doOnNext(webSession -> webSession.getAttributes().putAll(sessionMap)) .then(webFilterChain.filter(exchange)); } } }