Remove session store type in favor of defined order

Closes gh-27756
This commit is contained in:
Madhura Bhave
2022-06-17 12:02:04 -07:00
parent 6346f21f4e
commit 7cb53b3c45
29 changed files with 208 additions and 1108 deletions

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed 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
*
* https://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.
*/
package org.springframework.boot.autoconfigure.session;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.diagnostics.FailureAnalyzer;
import org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter;
import org.springframework.session.SessionRepository;
import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository;
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link NonUniqueSessionRepositoryFailureAnalyzer}.
*
* @author Stephane Nicoll
*/
class NonUniqueSessionRepositoryFailureAnalyzerTests {
private final FailureAnalyzer analyzer = new NonUniqueSessionRepositoryFailureAnalyzer();
@Test
void failureAnalysisWithMultipleCandidates() {
FailureAnalysis analysis = analyzeFailure(
createFailure(JdbcIndexedSessionRepository.class, HazelcastIndexedSessionRepository.class));
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains(JdbcIndexedSessionRepository.class.getName(),
HazelcastIndexedSessionRepository.class.getName());
assertThat(analysis.getAction()).contains("spring.session.store-type");
}
@SafeVarargs
@SuppressWarnings("varargs")
private final Exception createFailure(Class<? extends SessionRepository<?>>... candidates) {
return new NonUniqueSessionRepositoryException(Arrays.asList(candidates));
}
private FailureAnalysis analyzeFailure(Exception failure) {
FailureAnalysis analysis = this.analyzer.analyze(failure);
if (analysis != null) {
new LoggingFailureAnalysisReporter().report(analysis);
}
return analysis;
}
}

View File

@@ -55,35 +55,21 @@ class ReactiveSessionAutoConfigurationMongoTests extends AbstractSessionAutoConf
.withStartupTimeout(Duration.ofMinutes(5));
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
.withClassLoader(new FilteredClassLoader(ReactiveRedisSessionRepository.class))
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class, MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class, MongoReactiveAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class));
@Test
void defaultConfig() {
this.contextRunner
.withPropertyValues("spring.session.store-type=mongodb",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl())
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class))
.run(validateSpringSessionUsesMongo("sessions"));
}
@Test
void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner.withClassLoader(new FilteredClassLoader(ReactiveRedisSessionRepository.class))
.withPropertyValues("spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl())
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class))
this.contextRunner.withPropertyValues("spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl())
.run(validateSpringSessionUsesMongo("sessions"));
}
@Test
void defaultConfigWithCustomTimeout() {
this.contextRunner
.withPropertyValues("spring.session.store-type=mongodb", "spring.session.timeout=1m",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl())
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class))
.run((context) -> {
this.contextRunner.withPropertyValues("spring.session.timeout=1m",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl()).run((context) -> {
ReactiveMongoSessionRepository repository = validateSessionRepository(context,
ReactiveMongoSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("maxInactiveIntervalInSeconds", 60);
@@ -92,12 +78,8 @@ class ReactiveSessionAutoConfigurationMongoTests extends AbstractSessionAutoConf
@Test
void defaultConfigWithCustomSessionTimeout() {
this.contextRunner
.withPropertyValues("spring.session.store-type=mongodb", "server.reactive.session.timeout=1m",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl())
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class))
.run((context) -> {
this.contextRunner.withPropertyValues("server.reactive.session.timeout=1m",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl()).run((context) -> {
ReactiveMongoSessionRepository repository = validateSessionRepository(context,
ReactiveMongoSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("maxInactiveIntervalInSeconds", 60);
@@ -107,24 +89,26 @@ class ReactiveSessionAutoConfigurationMongoTests extends AbstractSessionAutoConf
@Test
void mongoSessionStoreWithCustomizations() {
this.contextRunner
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=mongodb", "spring.session.mongodb.collection-name=foo",
.withPropertyValues("spring.session.mongodb.collection-name=foo",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl())
.run(validateSpringSessionUsesMongo("foo"));
}
@Test
void sessionCookieConfigurationIsAppliedToAutoConfiguredWebSessionIdResolver() {
AutoConfigurations autoConfigurations = AutoConfigurations.of(MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class, MongoReactiveAutoConfiguration.class,
AutoConfigurations autoConfigurations = AutoConfigurations.of(SessionAutoConfiguration.class,
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class, MongoReactiveAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class, WebSessionIdResolverAutoConfiguration.class);
this.contextRunner.withConfiguration(autoConfigurations).withUserConfiguration(Config.class).withPropertyValues(
"spring.session.store-type=mongodb", "server.reactive.session.cookie.name:JSESSIONID",
"server.reactive.session.cookie.domain:.example.com", "server.reactive.session.cookie.path:/example",
"server.reactive.session.cookie.max-age:60", "server.reactive.session.cookie.http-only:false",
"server.reactive.session.cookie.secure:false", "server.reactive.session.cookie.same-site:strict",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl()).run(assertExchangeWithSession((exchange) -> {
new ReactiveWebApplicationContextRunner().withConfiguration(autoConfigurations)
.withUserConfiguration(Config.class)
.withClassLoader(new FilteredClassLoader(ReactiveRedisSessionRepository.class))
.withPropertyValues("server.reactive.session.cookie.name:JSESSIONID",
"server.reactive.session.cookie.domain:.example.com",
"server.reactive.session.cookie.path:/example", "server.reactive.session.cookie.max-age:60",
"server.reactive.session.cookie.http-only:false", "server.reactive.session.cookie.secure:false",
"server.reactive.session.cookie.same-site:strict",
"spring.data.mongodb.uri=" + mongoDb.getReplicaSetUrl())
.run(assertExchangeWithSession((exchange) -> {
List<ResponseCookie> cookies = exchange.getResponse().getCookies().get("JSESSIONID");
assertThat(cookies).isNotEmpty();
assertThat(cookies).allMatch((cookie) -> cookie.getDomain().equals(".example.com"));

View File

@@ -56,73 +56,57 @@ class ReactiveSessionAutoConfigurationRedisTests extends AbstractSessionAutoConf
.withStartupTimeout(Duration.ofMinutes(10));
protected final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(SessionAutoConfiguration.class, WebSessionIdResolverAutoConfiguration.class));
.withClassLoader(new FilteredClassLoader(ReactiveMongoSessionRepository.class)).withConfiguration(
AutoConfigurations.of(SessionAutoConfiguration.class, WebSessionIdResolverAutoConfiguration.class,
RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class));
@Test
void defaultConfig() {
this.contextRunner.withPropertyValues("spring.session.store-type=redis")
.withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE));
this.contextRunner.run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE));
}
@Test
void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner.withClassLoader(new FilteredClassLoader(ReactiveMongoSessionRepository.class))
.withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE));
void redisTakesPrecedenceMultipleImplementations() {
ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner().withConfiguration(
AutoConfigurations.of(SessionAutoConfiguration.class, WebSessionIdResolverAutoConfiguration.class,
RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class));
contextRunner.run(validateSpringSessionUsesRedis("spring:session:", SaveMode.ON_SET_ATTRIBUTE));
}
@Test
void defaultConfigWithCustomTimeout() {
this.contextRunner.withPropertyValues("spring.session.store-type=redis", "spring.session.timeout=1m")
.withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.run((context) -> {
ReactiveRedisSessionRepository repository = validateSessionRepository(context,
ReactiveRedisSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
this.contextRunner.withPropertyValues("spring.session.timeout=1m").run((context) -> {
ReactiveRedisSessionRepository repository = validateSessionRepository(context,
ReactiveRedisSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
}
@Test
void defaultConfigWithCustomWebFluxTimeout() {
this.contextRunner.withPropertyValues("spring.session.store-type=redis", "server.reactive.session.timeout=1m")
.withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.run((context) -> {
ReactiveRedisSessionRepository repository = validateSessionRepository(context,
ReactiveRedisSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
this.contextRunner.withPropertyValues("server.reactive.session.timeout=1m").run((context) -> {
ReactiveRedisSessionRepository repository = validateSessionRepository(context,
ReactiveRedisSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
}
@Test
void redisSessionStoreWithCustomizations() {
this.contextRunner
.withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo",
.withPropertyValues("spring.session.redis.namespace=foo",
"spring.session.redis.save-mode=on-get-attribute")
.run(validateSpringSessionUsesRedis("foo:", SaveMode.ON_GET_ATTRIBUTE));
}
@Test
void sessionCookieConfigurationIsAppliedToAutoConfiguredWebSessionIdResolver() {
this.contextRunner
.withConfiguration(
AutoConfigurations.of(RedisAutoConfiguration.class, RedisReactiveAutoConfiguration.class))
.withUserConfiguration(Config.class)
.withPropertyValues("spring.session.store-type=redis", "spring.redis.host=" + redis.getHost(),
"spring.redis.port=" + redis.getFirstMappedPort(), "spring.session.store-type=redis",
"server.reactive.session.cookie.name:JSESSIONID",
"server.reactive.session.cookie.domain:.example.com",
"server.reactive.session.cookie.path:/example", "server.reactive.session.cookie.max-age:60",
"server.reactive.session.cookie.http-only:false", "server.reactive.session.cookie.secure:false",
"server.reactive.session.cookie.same-site:strict")
.run(assertExchangeWithSession((exchange) -> {
this.contextRunner.withUserConfiguration(Config.class).withPropertyValues(
"spring.redis.host=" + redis.getHost(), "spring.redis.port=" + redis.getFirstMappedPort(),
"server.reactive.session.cookie.name:JSESSIONID", "server.reactive.session.cookie.domain:.example.com",
"server.reactive.session.cookie.path:/example", "server.reactive.session.cookie.max-age:60",
"server.reactive.session.cookie.http-only:false", "server.reactive.session.cookie.secure:false",
"server.reactive.session.cookie.same-site:strict").run(assertExchangeWithSession((exchange) -> {
List<ResponseCookie> cookies = exchange.getResponse().getCookies().get("JSESSIONID");
assertThat(cookies).isNotEmpty();
assertThat(cookies).allMatch((cookie) -> cookie.getDomain().equals(".example.com"));

View File

@@ -47,30 +47,30 @@ import static org.mockito.Mockito.mock;
class SessionAutoConfigurationHazelcastTests extends AbstractSessionAutoConfigurationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withClassLoader(new FilteredClassLoader(JdbcIndexedSessionRepository.class,
RedisIndexedSessionRepository.class, MongoIndexedSessionRepository.class))
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class))
.withUserConfiguration(HazelcastConfiguration.class);
@Test
void defaultConfig() {
this.contextRunner.withPropertyValues("spring.session.store-type=hazelcast").run(this::validateDefaultConfig);
this.contextRunner.run(this::validateDefaultConfig);
}
@Test
void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(JdbcIndexedSessionRepository.class,
RedisIndexedSessionRepository.class, MongoIndexedSessionRepository.class))
void hazelcastTakesPrecedenceOverJdbc() {
this.contextRunner.withClassLoader(
new FilteredClassLoader(RedisIndexedSessionRepository.class, MongoIndexedSessionRepository.class))
.run(this::validateDefaultConfig);
}
@Test
void defaultConfigWithCustomTimeout() {
this.contextRunner.withPropertyValues("spring.session.store-type=hazelcast", "spring.session.timeout=1m")
.run((context) -> {
HazelcastIndexedSessionRepository repository = validateSessionRepository(context,
HazelcastIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
this.contextRunner.withPropertyValues("spring.session.timeout=1m").run((context) -> {
HazelcastIndexedSessionRepository repository = validateSessionRepository(context,
HazelcastIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
}
private void validateDefaultConfig(AssertableWebApplicationContext context) {
@@ -84,32 +84,29 @@ class SessionAutoConfigurationHazelcastTests extends AbstractSessionAutoConfigur
@Test
void customMapName() {
this.contextRunner.withPropertyValues("spring.session.store-type=hazelcast",
"spring.session.hazelcast.map-name=foo:bar:biz").run((context) -> {
validateSessionRepository(context, HazelcastIndexedSessionRepository.class);
HazelcastInstance hazelcastInstance = context.getBean(HazelcastInstance.class);
then(hazelcastInstance).should().getMap("foo:bar:biz");
});
this.contextRunner.withPropertyValues("spring.session.hazelcast.map-name=foo:bar:biz").run((context) -> {
validateSessionRepository(context, HazelcastIndexedSessionRepository.class);
HazelcastInstance hazelcastInstance = context.getBean(HazelcastInstance.class);
then(hazelcastInstance).should().getMap("foo:bar:biz");
});
}
@Test
void customFlushMode() {
this.contextRunner.withPropertyValues("spring.session.store-type=hazelcast",
"spring.session.hazelcast.flush-mode=immediate").run((context) -> {
HazelcastIndexedSessionRepository repository = validateSessionRepository(context,
HazelcastIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("flushMode", FlushMode.IMMEDIATE);
});
this.contextRunner.withPropertyValues("spring.session.hazelcast.flush-mode=immediate").run((context) -> {
HazelcastIndexedSessionRepository repository = validateSessionRepository(context,
HazelcastIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("flushMode", FlushMode.IMMEDIATE);
});
}
@Test
void customSaveMode() {
this.contextRunner.withPropertyValues("spring.session.store-type=hazelcast",
"spring.session.hazelcast.save-mode=on-get-attribute").run((context) -> {
HazelcastIndexedSessionRepository repository = validateSessionRepository(context,
HazelcastIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("saveMode", SaveMode.ON_GET_ATTRIBUTE);
});
this.contextRunner.withPropertyValues("spring.session.hazelcast.save-mode=on-get-attribute").run((context) -> {
HazelcastIndexedSessionRepository repository = validateSessionRepository(context,
HazelcastIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("saveMode", SaveMode.ON_GET_ATTRIBUTE);
});
}
@Configuration(proxyBeanMethods = false)

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed 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
*
* https://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.
*/
package org.springframework.boot.autoconfigure.session;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Integration tests for {@link SessionAutoConfiguration}.
*
* @author Stephane Nicoll
*/
class SessionAutoConfigurationIntegrationTests extends AbstractSessionAutoConfigurationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, SessionAutoConfiguration.class))
.withPropertyValues("spring.datasource.generate-unique-name=true");
@Test
void severalCandidatesWithNoSessionStore() {
this.contextRunner.withUserConfiguration(HazelcastConfiguration.class).run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure().rootCause().isInstanceOf(NonUniqueSessionRepositoryException.class)
.hasMessageContaining("Multiple session repository candidates are available")
.hasMessageContaining("set the 'spring.session.store-type' property accordingly");
});
}
@Test
void severalCandidatesWithWrongSessionStore() {
this.contextRunner.withUserConfiguration(HazelcastConfiguration.class)
.withPropertyValues("spring.session.store-type=redis").run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure().hasCauseInstanceOf(SessionRepositoryUnavailableException.class);
assertThat(context).getFailure()
.hasMessageContaining("No session repository could be auto-configured");
assertThat(context).getFailure().hasMessageContaining("session store type is 'redis'");
});
}
@Test
void severalCandidatesWithValidSessionStore() {
this.contextRunner.withUserConfiguration(HazelcastConfiguration.class)
.withPropertyValues("spring.session.store-type=jdbc")
.run((context) -> validateSessionRepository(context, JdbcIndexedSessionRepository.class));
}
@Configuration(proxyBeanMethods = false)
static class HazelcastConfiguration {
@Bean
@SuppressWarnings("unchecked")
HazelcastInstance hazelcastInstance() {
IMap<Object, Object> map = mock(IMap.class);
HazelcastInstance mock = mock(HazelcastInstance.class);
given(mock.getMap("spring:session:sessions")).willReturn(map);
given(mock.getMap("foo:bar:biz")).willReturn(map);
return mock;
}
}
}

View File

@@ -62,6 +62,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfigurationTests {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withClassLoader(new FilteredClassLoader(HazelcastIndexedSessionRepository.class,
MongoIndexedSessionRepository.class, RedisIndexedSessionRepository.class))
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class,
SessionAutoConfiguration.class))
@@ -69,15 +71,7 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
@Test
void defaultConfig() {
this.contextRunner.withPropertyValues("spring.session.store-type=jdbc").run(this::validateDefaultConfig);
}
@Test
void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(HazelcastIndexedSessionRepository.class,
MongoIndexedSessionRepository.class, RedisIndexedSessionRepository.class))
.run(this::validateDefaultConfig);
this.contextRunner.run(this::validateDefaultConfig);
}
private void validateDefaultConfig(AssertableWebApplicationContext context) {
@@ -96,46 +90,39 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
@Test
void filterOrderCanBeCustomized() {
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.servlet.filter-order=123")
.run((context) -> {
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
assertThat(registration.getOrder()).isEqualTo(123);
});
this.contextRunner.withPropertyValues("spring.session.servlet.filter-order=123").run((context) -> {
FilterRegistrationBean<?> registration = context.getBean(FilterRegistrationBean.class);
assertThat(registration.getOrder()).isEqualTo(123);
});
}
@Test
void disableDataSourceInitializer() {
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.initialize-schema=never")
.run((context) -> {
assertThat(context).doesNotHaveBean(JdbcSessionDataSourceScriptDatabaseInitializer.class);
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("tableName", "SPRING_SESSION");
assertThat(context.getBean(JdbcSessionProperties.class).getInitializeSchema())
.isEqualTo(DatabaseInitializationMode.NEVER);
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(
() -> context.getBean(JdbcOperations.class).queryForList("select * from SPRING_SESSION"));
});
this.contextRunner.withPropertyValues("spring.session.jdbc.initialize-schema=never").run((context) -> {
assertThat(context).doesNotHaveBean(JdbcSessionDataSourceScriptDatabaseInitializer.class);
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("tableName", "SPRING_SESSION");
assertThat(context.getBean(JdbcSessionProperties.class).getInitializeSchema())
.isEqualTo(DatabaseInitializationMode.NEVER);
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(
() -> context.getBean(JdbcOperations.class).queryForList("select * from SPRING_SESSION"));
});
}
@Test
void customTimeout() {
this.contextRunner.withPropertyValues("spring.session.store-type=jdbc", "spring.session.timeout=1m")
.run((context) -> {
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
this.contextRunner.withPropertyValues("spring.session.timeout=1m").run((context) -> {
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("defaultMaxInactiveInterval", 60);
});
}
@Test
void customTableName() {
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.table-name=FOO_BAR",
"spring.session.jdbc.schema=classpath:session/custom-schema-h2.sql")
.run((context) -> {
this.contextRunner.withPropertyValues("spring.session.jdbc.table-name=FOO_BAR",
"spring.session.jdbc.schema=classpath:session/custom-schema-h2.sql").run((context) -> {
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
assertThat(repository).hasFieldOrPropertyWithValue("tableName", "FOO_BAR");
@@ -147,60 +134,51 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
@Test
void customCleanupCron() {
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.cleanup-cron=0 0 12 * * *")
.run((context) -> {
assertThat(context.getBean(JdbcSessionProperties.class).getCleanupCron()).isEqualTo("0 0 12 * * *");
SpringBootJdbcHttpSessionConfiguration configuration = context
.getBean(SpringBootJdbcHttpSessionConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("cleanupCron", "0 0 12 * * *");
});
this.contextRunner.withPropertyValues("spring.session.jdbc.cleanup-cron=0 0 12 * * *").run((context) -> {
assertThat(context.getBean(JdbcSessionProperties.class).getCleanupCron()).isEqualTo("0 0 12 * * *");
SpringBootJdbcHttpSessionConfiguration configuration = context
.getBean(SpringBootJdbcHttpSessionConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("cleanupCron", "0 0 12 * * *");
});
}
@Test
void customFlushMode() {
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.flush-mode=immediate")
.run((context) -> {
assertThat(context.getBean(JdbcSessionProperties.class).getFlushMode())
.isEqualTo(FlushMode.IMMEDIATE);
SpringBootJdbcHttpSessionConfiguration configuration = context
.getBean(SpringBootJdbcHttpSessionConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("flushMode", FlushMode.IMMEDIATE);
});
this.contextRunner.withPropertyValues("spring.session.jdbc.flush-mode=immediate").run((context) -> {
assertThat(context.getBean(JdbcSessionProperties.class).getFlushMode()).isEqualTo(FlushMode.IMMEDIATE);
SpringBootJdbcHttpSessionConfiguration configuration = context
.getBean(SpringBootJdbcHttpSessionConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("flushMode", FlushMode.IMMEDIATE);
});
}
@Test
void customSaveMode() {
this.contextRunner
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.save-mode=on-get-attribute")
.run((context) -> {
assertThat(context.getBean(JdbcSessionProperties.class).getSaveMode())
.isEqualTo(SaveMode.ON_GET_ATTRIBUTE);
SpringBootJdbcHttpSessionConfiguration configuration = context
.getBean(SpringBootJdbcHttpSessionConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("saveMode", SaveMode.ON_GET_ATTRIBUTE);
});
this.contextRunner.withPropertyValues("spring.session.jdbc.save-mode=on-get-attribute").run((context) -> {
assertThat(context.getBean(JdbcSessionProperties.class).getSaveMode()).isEqualTo(SaveMode.ON_GET_ATTRIBUTE);
SpringBootJdbcHttpSessionConfiguration configuration = context
.getBean(SpringBootJdbcHttpSessionConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("saveMode", SaveMode.ON_GET_ATTRIBUTE);
});
}
@Test
void sessionDataSourceIsUsedWhenAvailable() {
this.contextRunner.withUserConfiguration(SessionDataSourceConfiguration.class)
.withPropertyValues("spring.session.store-type=jdbc").run((context) -> {
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
DataSource sessionDataSource = context.getBean("sessionDataSource", DataSource.class);
assertThat(repository).extracting("jdbcOperations.dataSource").isEqualTo(sessionDataSource);
assertThat(context.getBean(JdbcSessionDataSourceScriptDatabaseInitializer.class))
.hasFieldOrPropertyWithValue("dataSource", sessionDataSource);
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(
() -> context.getBean(JdbcOperations.class).queryForList("select * from SPRING_SESSION"));
});
this.contextRunner.withUserConfiguration(SessionDataSourceConfiguration.class).run((context) -> {
JdbcIndexedSessionRepository repository = validateSessionRepository(context,
JdbcIndexedSessionRepository.class);
DataSource sessionDataSource = context.getBean("sessionDataSource", DataSource.class);
assertThat(repository).extracting("jdbcOperations.dataSource").isEqualTo(sessionDataSource);
assertThat(context.getBean(JdbcSessionDataSourceScriptDatabaseInitializer.class))
.hasFieldOrPropertyWithValue("dataSource", sessionDataSource);
assertThatExceptionOfType(BadSqlGrammarException.class).isThrownBy(
() -> context.getBean(JdbcOperations.class).queryForList("select * from SPRING_SESSION"));
});
}
@Test
void sessionRepositoryBeansDependOnJdbcSessionDataSourceInitializer() {
this.contextRunner.withPropertyValues("spring.session.store-type=jdbc").run((context) -> {
this.contextRunner.run((context) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] sessionRepositoryNames = beanFactory.getBeanNamesForType(JdbcIndexedSessionRepository.class);
assertThat(sessionRepositoryNames).isNotEmpty();
@@ -214,8 +192,7 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
@Test
void sessionRepositoryBeansDependOnFlyway() {
this.contextRunner.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.initialize-schema=never")
.run((context) -> {
.withPropertyValues("spring.session.jdbc.initialize-schema=never").run((context) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] sessionRepositoryNames = beanFactory
.getBeanNamesForType(JdbcIndexedSessionRepository.class);
@@ -230,8 +207,7 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
@Test
void sessionRepositoryBeansDependOnLiquibase() {
this.contextRunner.withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=jdbc", "spring.session.jdbc.initialize-schema=never")
.run((context) -> {
.withPropertyValues("spring.session.jdbc.initialize-schema=never").run((context) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] sessionRepositoryNames = beanFactory
.getBeanNamesForType(JdbcIndexedSessionRepository.class);
@@ -248,7 +224,6 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
this.contextRunner.withUserConfiguration(CustomJdbcSessionDatabaseInitializerConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=jdbc")
.run((context) -> assertThat(context)
.hasSingleBean(JdbcSessionDataSourceScriptDatabaseInitializer.class)
.doesNotHaveBean("jdbcSessionDataSourceScriptDatabaseInitializer")
@@ -260,7 +235,6 @@ class SessionAutoConfigurationJdbcTests extends AbstractSessionAutoConfiguration
this.contextRunner.withUserConfiguration(CustomDatabaseInitializerConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=jdbc")
.run((context) -> assertThat(context)
.hasSingleBean(JdbcSessionDataSourceScriptDatabaseInitializer.class)
.hasBean("customInitializer"));

View File

@@ -52,34 +52,32 @@ class SessionAutoConfigurationMongoTests extends AbstractSessionAutoConfiguratio
.withStartupTimeout(Duration.ofMinutes(5));
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withClassLoader(new FilteredClassLoader(HazelcastIndexedSessionRepository.class,
JdbcIndexedSessionRepository.class, RedisIndexedSessionRepository.class))
.withConfiguration(AutoConfigurations.of(MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
SessionAutoConfiguration.class))
.withPropertyValues("spring.data.mongodb.uri=" + mongoDB.getReplicaSetUrl());
@Test
void defaultConfig() {
this.contextRunner.withPropertyValues("spring.session.store-type=mongodb")
.run(validateSpringSessionUsesMongo("sessions"));
this.contextRunner.run(validateSpringSessionUsesMongo("sessions"));
}
@Test
void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(HazelcastIndexedSessionRepository.class,
JdbcIndexedSessionRepository.class, RedisIndexedSessionRepository.class))
void mongoTakesPrecedenceOverJdbcAndHazelcast() {
this.contextRunner.withClassLoader(new FilteredClassLoader(RedisIndexedSessionRepository.class))
.run(validateSpringSessionUsesMongo("sessions"));
}
@Test
void defaultConfigWithCustomTimeout() {
this.contextRunner.withPropertyValues("spring.session.store-type=mongodb", "spring.session.timeout=1m")
this.contextRunner.withPropertyValues("spring.session.timeout=1m")
.run(validateSpringSessionUsesMongo("sessions", Duration.ofMinutes(1)));
}
@Test
void mongoSessionStoreWithCustomizations() {
this.contextRunner
.withPropertyValues("spring.session.store-type=mongodb", "spring.session.mongodb.collection-name=foo")
this.contextRunner.withPropertyValues("spring.session.mongodb.collection-name=foo")
.run(validateSpringSessionUsesMongo("foo"));
}

View File

@@ -60,12 +60,14 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
.withStartupTimeout(Duration.ofMinutes(10));
protected final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withClassLoader(new FilteredClassLoader(HazelcastIndexedSessionRepository.class,
JdbcIndexedSessionRepository.class, MongoIndexedSessionRepository.class))
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
@Test
void defaultConfig() {
this.contextRunner
.withPropertyValues("spring.session.store-type=redis", "spring.redis.host=" + redis.getHost(),
.withPropertyValues("spring.redis.host=" + redis.getHost(),
"spring.redis.port=" + redis.getFirstMappedPort())
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE,
@@ -73,11 +75,8 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
}
@Test
void defaultConfigWithUniqueStoreImplementation() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(HazelcastIndexedSessionRepository.class,
JdbcIndexedSessionRepository.class, MongoIndexedSessionRepository.class))
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
void redisTakesPrecedenceMultipleImplementations() {
this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withPropertyValues("spring.redis.host=" + redis.getHost(),
"spring.redis.port=" + redis.getFirstMappedPort())
.run(validateSpringSessionUsesRedis("spring:session:event:0:created:", FlushMode.ON_SAVE,
@@ -87,7 +86,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
@Test
void defaultConfigWithCustomTimeout() {
this.contextRunner
.withPropertyValues("spring.session.store-type=redis", "spring.redis.host=" + redis.getHost(),
.withPropertyValues("spring.redis.host=" + redis.getHost(),
"spring.redis.port=" + redis.getFirstMappedPort(), "spring.session.timeout=1m")
.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)).run((context) -> {
RedisIndexedSessionRepository repository = validateSessionRepository(context,
@@ -99,8 +98,8 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
@Test
void redisSessionStoreWithCustomizations() {
this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo",
"spring.session.redis.flush-mode=immediate", "spring.session.redis.save-mode=on-get-attribute",
.withPropertyValues("spring.session.redis.namespace=foo", "spring.session.redis.flush-mode=immediate",
"spring.session.redis.save-mode=on-get-attribute",
"spring.session.redis.cleanup-cron=0 0 12 * * *", "spring.redis.host=" + redis.getHost(),
"spring.redis.port=" + redis.getFirstMappedPort())
.run(validateSpringSessionUsesRedis("foo:event:0:created:", FlushMode.IMMEDIATE,
@@ -110,7 +109,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
@Test
void redisSessionWithConfigureActionNone() {
this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=redis", "spring.session.redis.configure-action=none",
.withPropertyValues("spring.session.redis.configure-action=none",
"spring.redis.host=" + redis.getHost(), "spring.redis.port=" + redis.getFirstMappedPort())
.run(validateStrategy(ConfigureRedisAction.NO_OP.getClass()));
}
@@ -118,7 +117,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
@Test
void redisSessionWithDefaultConfigureActionNone() {
this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withPropertyValues("spring.session.store-type=redis", "spring.redis.host=" + redis.getHost(),
.withPropertyValues("spring.redis.host=" + redis.getHost(),
"spring.redis.port=" + redis.getFirstMappedPort())
.run(validateStrategy(ConfigureNotifyKeyspaceEventsAction.class,
entry("notify-keyspace-events", "gxE")));
@@ -128,7 +127,7 @@ class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfiguratio
void redisSessionWithCustomConfigureRedisActionBean() {
this.contextRunner.withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class))
.withUserConfiguration(MaxEntriesRedisAction.class)
.withPropertyValues("spring.session.store-type=redis", "spring.redis.host=" + redis.getHost(),
.withPropertyValues("spring.redis.host=" + redis.getHost(),
"spring.redis.port=" + redis.getFirstMappedPort())
.run(validateStrategy(MaxEntriesRedisAction.class, entry("set-max-intset-entries", "1024")));

View File

@@ -26,14 +26,22 @@ import org.mockito.InOrder;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.ReactiveMapSessionRepository;
import org.springframework.session.SessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
import org.springframework.session.data.mongo.MongoIndexedSessionRepository;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository;
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
import org.springframework.session.web.http.CookieHttpSessionIdResolver;
import org.springframework.session.web.http.DefaultCookieSerializer;
@@ -60,37 +68,31 @@ class SessionAutoConfigurationTests extends AbstractSessionAutoConfigurationTest
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
@Test
void contextFailsIfMultipleStoresAreAvailable() {
this.contextRunner.run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure().rootCause().isInstanceOf(NonUniqueSessionRepositoryException.class)
.hasMessageContaining("Multiple session repository candidates are available");
});
}
@Test
void contextFailsIfStoreTypeNotAvailable() {
this.contextRunner.withPropertyValues("spring.session.store-type=jdbc").run((context) -> {
assertThat(context).hasFailed();
assertThat(context).getFailure().hasCauseInstanceOf(SessionRepositoryUnavailableException.class);
assertThat(context).getFailure().hasMessageContaining("No session repository could be auto-configured");
assertThat(context).getFailure().hasMessageContaining("session store type is 'jdbc'");
});
}
@Test
void autoConfigurationDisabledIfStoreTypeSetToNone() {
this.contextRunner.withPropertyValues("spring.session.store-type=none")
void autoConfigurationDisabledIfNoImplementationMatches() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(RedisIndexedSessionRepository.class,
HazelcastIndexedSessionRepository.class, JdbcIndexedSessionRepository.class,
MongoIndexedSessionRepository.class))
.run((context) -> assertThat(context).doesNotHaveBean(SessionRepository.class));
}
@Test
void backOffIfSessionRepositoryIsPresent() {
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
.withPropertyValues("spring.session.store-type=redis").run((context) -> {
MapSessionRepository repository = validateSessionRepository(context, MapSessionRepository.class);
assertThat(context).getBean("mySessionRepository").isSameAs(repository);
});
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class).run((context) -> {
MapSessionRepository repository = validateSessionRepository(context, MapSessionRepository.class);
assertThat(context).getBean("mySessionRepository").isSameAs(repository);
});
}
@Test
void backOffIfReactiveSessionRepositoryIsPresent() {
ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class));
contextRunner.withUserConfiguration(ReactiveSessionRepositoryConfiguration.class).run((context) -> {
ReactiveMapSessionRepository repository = validateSessionRepository(context,
ReactiveMapSessionRepository.class);
assertThat(context).getBean("mySessionRepository").isSameAs(repository);
});
}
@Test
@@ -220,6 +222,17 @@ class SessionAutoConfigurationTests extends AbstractSessionAutoConfigurationTest
}
@Configuration(proxyBeanMethods = false)
@EnableSpringWebSession
static class ReactiveSessionRepositoryConfiguration {
@Bean
ReactiveMapSessionRepository mySessionRepository() {
return new ReactiveMapSessionRepository(Collections.emptyMap());
}
}
@EnableConfigurationProperties(ServerProperties.class)
static class ServerPropertiesConfiguration {