Extract spring-session-data-redis

Issue gh-806
This commit is contained in:
Rob Winch
2017-06-16 15:25:31 -05:00
parent f1319483ee
commit 972cf66d7e
34 changed files with 6 additions and 4 deletions

View File

@@ -0,0 +1,725 @@
/*
* Copyright 2014-2017 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
*
* 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.
*/
package org.springframework.session.data.redis;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.connection.DefaultMessage;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.data.redis.RedisOperationsSessionRepository.PrincipalNameResolver;
import org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession;
import org.springframework.session.events.AbstractSessionEvent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings({ "unchecked", "rawtypes" })
public class RedisOperationsSessionRepositoryTests {
static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
@Mock
RedisConnectionFactory factory;
@Mock
RedisConnection connection;
@Mock
RedisOperations<Object, Object> redisOperations;
@Mock
BoundValueOperations<Object, Object> boundValueOperations;
@Mock
BoundHashOperations<Object, Object, Object> boundHashOperations;
@Mock
BoundSetOperations<Object, Object> boundSetOperations;
@Mock
ApplicationEventPublisher publisher;
@Mock
RedisSerializer<Object> defaultSerializer;
@Captor
ArgumentCaptor<AbstractSessionEvent> event;
@Captor
ArgumentCaptor<Map<String, Object>> delta;
private MapSession cached;
private RedisOperationsSessionRepository redisRepository;
@Before
public void setup() {
this.redisRepository = new RedisOperationsSessionRepository(this.redisOperations);
this.redisRepository.setDefaultSerializer(this.defaultSerializer);
this.cached = new MapSession();
this.cached.setId("session-id");
this.cached.setCreationTime(Instant.ofEpochMilli(1404360000000L));
this.cached.setLastAccessedTime(Instant.ofEpochMilli(1404360000000L));
}
@Test(expected = IllegalArgumentException.class)
public void constructorNullConnectionFactory() {
new RedisOperationsSessionRepository((RedisConnectionFactory) null);
}
@Test(expected = IllegalArgumentException.class)
public void setApplicationEventPublisherNull() {
this.redisRepository.setApplicationEventPublisher(null);
}
// gh-61
@Test
public void constructorConnectionFactory() {
this.redisRepository = new RedisOperationsSessionRepository(this.factory);
RedisSession session = this.redisRepository.createSession();
given(this.factory.getConnection()).willReturn(this.connection);
this.redisRepository.save(session);
}
@Test
public void createSessionDefaultMaxInactiveInterval() throws Exception {
Session session = this.redisRepository.createSession();
assertThat(session.getMaxInactiveInterval())
.isEqualTo(new MapSession().getMaxInactiveInterval());
}
@Test
public void createSessionCustomMaxInactiveInterval() throws Exception {
int interval = 1;
this.redisRepository.setDefaultMaxInactiveInterval(interval);
Session session = this.redisRepository.createSession();
assertThat(session.getMaxInactiveInterval())
.isEqualTo(Duration.ofSeconds(interval));
}
@Test
public void saveNewSession() {
RedisSession session = this.redisRepository.createSession();
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
Map<String, Object> delta = getDelta();
assertThat(delta.size()).isEqualTo(3);
Object creationTime = delta
.get(RedisOperationsSessionRepository.CREATION_TIME_ATTR);
assertThat(creationTime).isEqualTo(session.getCreationTime().toEpochMilli());
assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR))
.isEqualTo((int) Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS).getSeconds());
assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR))
.isEqualTo(session.getCreationTime().toEpochMilli());
}
// gh-467
@Test
public void saveSessionNothingChanged() {
RedisSession session = this.redisRepository.new RedisSession(this.cached);
this.redisRepository.save(session);
verifyZeroInteractions(this.redisOperations);
}
@Test
public void saveJavadocSummary() {
RedisSession session = this.redisRepository.createSession();
String sessionKey = "spring:session:sessions:" + session.getId();
String backgroundExpireKey = "spring:session:expirations:"
+ RedisSessionExpirationPolicy.roundUpToNextMinute(
RedisSessionExpirationPolicy.expiresInMillis(session));
String destroyedTriggerKey = "spring:session:sessions:expires:" + session.getId();
given(this.redisOperations.boundHashOps(sessionKey))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(backgroundExpireKey))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(destroyedTriggerKey))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
// the actual data in the session expires 5 minutes after expiration so the data
// can be accessed in expiration events
// if the session is retrieved and expired it will not be returned since
// getSession checks if it is expired
long fiveMinutesAfterExpires = session.getMaxInactiveInterval().plusMinutes(5)
.getSeconds();
verify(this.boundHashOperations).expire(fiveMinutesAfterExpires,
TimeUnit.SECONDS);
verify(this.boundSetOperations).expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
verify(this.boundSetOperations).add("expires:" + session.getId());
verify(this.boundValueOperations).expire(1800L, TimeUnit.SECONDS);
verify(this.boundValueOperations).append("");
}
@Test
public void saveJavadoc() {
RedisSession session = this.redisRepository.new RedisSession(this.cached);
session.setLastAccessedTime(session.getLastAccessedTime());
given(this.redisOperations.boundHashOps("spring:session:sessions:session-id"))
.willReturn(this.boundHashOperations);
given(this.redisOperations
.boundSetOps("spring:session:expirations:1404361860000"))
.willReturn(this.boundSetOperations);
given(this.redisOperations
.boundValueOps("spring:session:sessions:expires:session-id"))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
// the actual data in the session expires 5 minutes after expiration so the data
// can be accessed in expiration events
// if the session is retrieved and expired it will not be returned since
// getSession checks if it is expired
verify(this.boundHashOperations).expire(
session.getMaxInactiveInterval().plusMinutes(5).getSeconds(),
TimeUnit.SECONDS);
}
@Test
public void saveLastAccessChanged() {
RedisSession session = this.redisRepository.new RedisSession(
new MapSession(this.cached));
session.setLastAccessedTime(Instant.ofEpochMilli(12345678L));
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
assertThat(getDelta())
.isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
session.getLastAccessedTime().toEpochMilli()));
}
@Test
public void saveSetAttribute() {
String attrName = "attrName";
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
session.setAttribute(attrName, "attrValue");
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
assertThat(getDelta()).isEqualTo(
map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
session.getAttribute(attrName).orElse(null)));
}
@Test
public void saveRemoveAttribute() {
String attrName = "attrName";
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
session.removeAttribute(attrName);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
assertThat(getDelta()).isEqualTo(map(
RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), null));
}
@Test
public void saveExpired() {
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
session.setMaxInactiveInterval(Duration.ZERO);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
this.redisRepository.save(session);
String id = session.getId();
verify(this.redisOperations, atLeastOnce()).delete(getKey("expires:" + id));
verify(this.redisOperations, never()).boundValueOps(getKey("expires:" + id));
}
@Test
public void redisSessionGetAttributes() {
String attrName = "attrName";
RedisSession session = this.redisRepository.new RedisSession();
assertThat(session.getAttributeNames()).isEmpty();
session.setAttribute(attrName, "attrValue");
assertThat(session.getAttributeNames()).containsOnly(attrName);
session.removeAttribute(attrName);
assertThat(session.getAttributeNames()).isEmpty();
}
@Test
public void delete() {
String attrName = "attrName";
MapSession expected = new MapSession();
expected.setLastAccessedTime(Instant.now().minusSeconds(60));
expected.setAttribute(attrName, "attrValue");
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
Map map = map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
expected.getAttribute(attrName).orElse(null),
RedisOperationsSessionRepository.CREATION_TIME_ATTR,
expected.getCreationTime().toEpochMilli(),
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR,
(int) expected.getMaxInactiveInterval().getSeconds(),
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
expected.getLastAccessedTime().toEpochMilli());
given(this.boundHashOperations.entries()).willReturn(map);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
String id = expected.getId();
this.redisRepository.delete(id);
assertThat(getDelta().get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR))
.isEqualTo(0);
verify(this.redisOperations, atLeastOnce()).delete(getKey("expires:" + id));
verify(this.redisOperations, never()).boundValueOps(getKey("expires:" + id));
}
@Test
public void deleteNullSession() {
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
String id = "abc";
this.redisRepository.delete(id);
verify(this.redisOperations, times(0)).delete(anyString());
verify(this.redisOperations, times(0)).delete(anyString());
}
@Test
public void getSessionNotFound() {
String id = "abc";
given(this.redisOperations.boundHashOps(getKey(id)))
.willReturn(this.boundHashOperations);
given(this.boundHashOperations.entries()).willReturn(map());
assertThat(this.redisRepository.getSession(id)).isNull();
}
@Test
public void getSessionFound() {
String attrName = "attrName";
MapSession expected = new MapSession();
expected.setLastAccessedTime(Instant.now().minusSeconds(60));
expected.setAttribute(attrName, "attrValue");
given(this.redisOperations.boundHashOps(getKey(expected.getId())))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
expected.getAttribute(attrName).orElse(null),
RedisOperationsSessionRepository.CREATION_TIME_ATTR,
expected.getCreationTime().toEpochMilli(),
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR,
(int) expected.getMaxInactiveInterval().getSeconds(),
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
expected.getLastAccessedTime().toEpochMilli());
given(this.boundHashOperations.entries()).willReturn(map);
RedisSession session = this.redisRepository.getSession(expected.getId());
assertThat(session.getId()).isEqualTo(expected.getId());
assertThat(session.getAttributeNames()).isEqualTo(expected.getAttributeNames());
assertThat(session.<String>getAttribute(attrName))
.isEqualTo(expected.getAttribute(attrName));
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
assertThat(session.getMaxInactiveInterval())
.isEqualTo(expected.getMaxInactiveInterval());
assertThat(session.getLastAccessedTime())
.isEqualTo(expected.getLastAccessedTime());
}
@Test
public void getSessionExpired() {
String expiredId = "expired-id";
given(this.redisOperations.boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1,
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli());
given(this.boundHashOperations.entries()).willReturn(map);
assertThat(this.redisRepository.getSession(expiredId)).isNull();
}
@Test
public void findByPrincipalNameExpired() {
String expiredId = "expired-id";
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.boundSetOperations.members())
.willReturn(Collections.<Object>singleton(expiredId));
given(this.redisOperations.boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1,
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
Instant.now().minus(5, ChronoUnit.MINUTES).toEpochMilli());
given(this.boundHashOperations.entries()).willReturn(map);
assertThat(this.redisRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "principal"))
.isEmpty();
}
@Test
public void findByPrincipalName() {
Instant lastAccessed = Instant.now().minusMillis(10);
Instant createdTime = lastAccessed.minusMillis(10);
Duration maxInactive = Duration.ofHours(1);
String sessionId = "some-id";
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.boundSetOperations.members())
.willReturn(Collections.<Object>singleton(sessionId));
given(this.redisOperations.boundHashOps(getKey(sessionId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.CREATION_TIME_ATTR, createdTime.toEpochMilli(),
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, (int) maxInactive.getSeconds(),
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, lastAccessed.toEpochMilli());
given(this.boundHashOperations.entries()).willReturn(map);
Map<String, RedisSession> sessionIdToSessions = this.redisRepository
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
"principal");
assertThat(sessionIdToSessions).hasSize(1);
RedisSession session = sessionIdToSessions.get(sessionId);
assertThat(session).isNotNull();
assertThat(session.getId()).isEqualTo(sessionId);
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessed);
assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactive);
assertThat(session.getCreationTime()).isEqualTo(createdTime);
}
@Test
public void cleanupExpiredSessions() {
String expiredId = "expired-id";
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
Set<Object> expiredIds = new HashSet<>(
Arrays.asList("expired-key1", "expired-key2"));
given(this.boundSetOperations.members()).willReturn(expiredIds);
this.redisRepository.cleanupExpiredSessions();
for (Object id : expiredIds) {
String expiredKey = "spring:session:sessions:" + id;
// https://github.com/spring-projects/spring-session/issues/93
verify(this.redisOperations).hasKey(expiredKey);
}
}
@Test
public void onMessageCreated() throws Exception {
MapSession session = this.cached;
byte[] pattern = "".getBytes("UTF-8");
String channel = "spring:session:event:created:" + session.getId();
JdkSerializationRedisSerializer defaultSerailizer = new JdkSerializationRedisSerializer();
this.redisRepository.setDefaultSerializer(defaultSerailizer);
byte[] body = defaultSerailizer.serialize(new HashMap());
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
this.redisRepository.setApplicationEventPublisher(this.publisher);
this.redisRepository.onMessage(message, pattern);
verify(this.publisher).publishEvent(this.event.capture());
assertThat(this.event.getValue().getSessionId()).isEqualTo(session.getId());
}
// gh-309
@Test
public void onMessageCreatedCustomSerializer() throws Exception {
MapSession session = this.cached;
byte[] pattern = "".getBytes("UTF-8");
byte[] body = new byte[0];
String channel = "spring:session:event:created:" + session.getId();
given(this.defaultSerializer.deserialize(body))
.willReturn(new HashMap<String, Object>());
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
this.redisRepository.setApplicationEventPublisher(this.publisher);
this.redisRepository.onMessage(message, pattern);
verify(this.publisher).publishEvent(this.event.capture());
assertThat(this.event.getValue().getSessionId()).isEqualTo(session.getId());
verify(this.defaultSerializer).deserialize(body);
}
@Test
public void resolvePrincipalIndex() {
PrincipalNameResolver resolver = RedisOperationsSessionRepository.PRINCIPAL_NAME_RESOLVER;
String username = "username";
RedisSession session = this.redisRepository.createSession();
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
username);
assertThat(resolver.resolvePrincipal(session)).isEqualTo(username);
}
@Test
public void resolveIndexOnSecurityContext() {
String principal = "resolveIndexOnSecurityContext";
Authentication authentication = new UsernamePasswordAuthenticationToken(principal,
"notused", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext context = new SecurityContextImpl();
context.setAuthentication(authentication);
PrincipalNameResolver resolver = RedisOperationsSessionRepository.PRINCIPAL_NAME_RESOLVER;
RedisSession session = this.redisRepository.createSession();
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
assertThat(resolver.resolvePrincipal(session)).isEqualTo(principal);
}
@Test
public void flushModeOnSaveCreate() {
this.redisRepository.createSession();
verifyZeroInteractions(this.boundHashOperations);
}
@Test
public void flushModeOnSaveSetAttribute() {
RedisSession session = this.redisRepository.createSession();
session.setAttribute("something", "here");
verifyZeroInteractions(this.boundHashOperations);
}
@Test
public void flushModeOnSaveRemoveAttribute() {
RedisSession session = this.redisRepository.createSession();
session.removeAttribute("remove");
verifyZeroInteractions(this.boundHashOperations);
}
@Test
public void flushModeOnSaveSetLastAccessedTime() {
RedisSession session = this.redisRepository.createSession();
session.setLastAccessedTime(Instant.ofEpochMilli(1L));
verifyZeroInteractions(this.boundHashOperations);
}
@Test
public void flushModeOnSaveSetMaxInactiveIntervalInSeconds() {
RedisSession session = this.redisRepository.createSession();
session.setMaxInactiveInterval(Duration.ofSeconds(1));
verifyZeroInteractions(this.boundHashOperations);
}
@Test
public void flushModeImmediateCreate() {
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
Map<String, Object> delta = getDelta();
assertThat(delta.size()).isEqualTo(3);
Object creationTime = delta
.get(RedisOperationsSessionRepository.CREATION_TIME_ATTR);
assertThat(creationTime).isEqualTo(session.getCreationTime().toEpochMilli());
assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR))
.isEqualTo((int) Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS).getSeconds());
assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR))
.isEqualTo(session.getCreationTime().toEpochMilli());
}
@Test
public void flushModeImmediateSetAttribute() {
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
String attrName = "someAttribute";
session.setAttribute(attrName, "someValue");
Map<String, Object> delta = getDelta(2);
assertThat(delta.size()).isEqualTo(1);
assertThat(delta).isEqualTo(
map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
session.getAttribute(attrName).orElse(null)));
}
@Test
public void flushModeImmediateRemoveAttribute() {
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
String attrName = "someAttribute";
session.removeAttribute(attrName);
Map<String, Object> delta = getDelta(2);
assertThat(delta.size()).isEqualTo(1);
assertThat(delta).isEqualTo(
map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
session.getAttribute(attrName).orElse(null)));
}
@Test
public void flushModeSetMaxInactiveIntervalInSeconds() {
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
reset(this.boundHashOperations);
session.setMaxInactiveInterval(Duration.ofSeconds(1));
verify(this.boundHashOperations).expire(anyLong(), any(TimeUnit.class));
}
@Test
public void flushModeSetLastAccessedTime() {
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
session.setLastAccessedTime(Instant.now());
Map<String, Object> delta = getDelta(2);
assertThat(delta.size()).isEqualTo(1);
assertThat(delta)
.isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
session.getLastAccessedTime().toEpochMilli()));
}
@Test(expected = IllegalArgumentException.class)
public void setRedisFlushModeNull() {
this.redisRepository.setRedisFlushMode(null);
}
private String getKey(String id) {
return "spring:session:sessions:" + id;
}
private Map map(Object... objects) {
Map<String, Object> result = new HashMap<>();
if (objects == null) {
return result;
}
for (int i = 0; i < objects.length; i += 2) {
result.put((String) objects[i], objects[i + 1]);
}
return result;
}
private Map<String, Object> getDelta() {
return getDelta(1);
}
private Map<String, Object> getDelta(int times) {
verify(this.boundHashOperations, times(times)).putAll(this.delta.capture());
return this.delta.getAllValues().get(times - 1);
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright 2014-2017 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
*
* 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.
*/
package org.springframework.session.data.redis;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.session.MapSession;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* @author Rob Winch
*/
@RunWith(MockitoJUnitRunner.class)
public class RedisSessionExpirationPolicyTests {
// Wed Apr 15 10:28:32 CDT 2015
final static Long NOW = 1429111712346L;
// Wed Apr 15 10:27:32 CDT 2015
final static Long ONE_MINUTE_AGO = 1429111652346L;
@Mock
RedisOperations<Object, Object> sessionRedisOperations;
@Mock
BoundSetOperations<Object, Object> setOperations;
@Mock
BoundHashOperations<Object, Object, Object> hashOperations;
@Mock
BoundValueOperations<Object, Object> valueOperations;
RedisSessionExpirationPolicy policy;
private MapSession session;
@Before
public void setup() {
RedisOperationsSessionRepository repository = new RedisOperationsSessionRepository(
this.sessionRedisOperations);
this.policy = new RedisSessionExpirationPolicy(this.sessionRedisOperations,
repository);
this.session = new MapSession();
this.session.setLastAccessedTime(Instant.ofEpochMilli(1429116694675L));
this.session.setId("12345");
given(this.sessionRedisOperations.boundSetOps(anyString()))
.willReturn(this.setOperations);
given(this.sessionRedisOperations.boundHashOps(anyString()))
.willReturn(this.hashOperations);
given(this.sessionRedisOperations.boundValueOps(anyString()))
.willReturn(this.valueOperations);
}
// gh-169
@Test
public void onExpirationUpdatedRemovesOriginalExpirationTimeRoundedUp()
throws Exception {
long originalExpirationTimeInMs = ONE_MINUTE_AGO;
long originalRoundedToNextMinInMs = RedisSessionExpirationPolicy
.roundUpToNextMinute(originalExpirationTimeInMs);
String originalExpireKey = this.policy
.getExpirationKey(originalRoundedToNextMinInMs);
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
// verify the original is removed
verify(this.sessionRedisOperations).boundSetOps(originalExpireKey);
verify(this.setOperations).remove("expires:" + this.session.getId());
}
@Test
public void onExpirationUpdatedDoNotSendDeleteWhenExpirationTimeDoesNotChange()
throws Exception {
long originalExpirationTimeInMs = RedisSessionExpirationPolicy
.expiresInMillis(this.session) - 10;
long originalRoundedToNextMinInMs = RedisSessionExpirationPolicy
.roundUpToNextMinute(originalExpirationTimeInMs);
String originalExpireKey = this.policy
.getExpirationKey(originalRoundedToNextMinInMs);
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
// verify the original is not removed
verify(this.sessionRedisOperations).boundSetOps(originalExpireKey);
verify(this.setOperations, never()).remove("expires:" + this.session.getId());
}
@Test
public void onExpirationUpdatedAddsExpirationTimeRoundedUp() throws Exception {
long expirationTimeInMs = RedisSessionExpirationPolicy
.expiresInMillis(this.session);
long expirationRoundedUpInMs = RedisSessionExpirationPolicy
.roundUpToNextMinute(expirationTimeInMs);
String expectedExpireKey = this.policy.getExpirationKey(expirationRoundedUpInMs);
this.policy.onExpirationUpdated(null, this.session);
verify(this.sessionRedisOperations).boundSetOps(expectedExpireKey);
verify(this.setOperations).add("expires:" + this.session.getId());
verify(this.setOperations).expire(
this.session.getMaxInactiveInterval().plusMinutes(5).getSeconds(),
TimeUnit.SECONDS);
}
@Test
public void onExpirationUpdatedSetExpireSession() throws Exception {
String sessionKey = this.policy.getSessionKey(this.session.getId());
this.policy.onExpirationUpdated(null, this.session);
verify(this.sessionRedisOperations).boundHashOps(sessionKey);
verify(this.hashOperations).expire(
this.session.getMaxInactiveInterval().plusMinutes(5).getSeconds(),
TimeUnit.SECONDS);
}
@Test
public void onExpirationUpdatedDeleteOnZero() throws Exception {
String sessionKey = this.policy.getSessionKey("expires:" + this.session.getId());
long originalExpirationTimeInMs = ONE_MINUTE_AGO;
this.session.setMaxInactiveInterval(Duration.ZERO);
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
// verify the original is removed
verify(this.setOperations).remove("expires:" + this.session.getId());
verify(this.setOperations).add("expires:" + this.session.getId());
verify(this.sessionRedisOperations).delete(sessionKey);
verify(this.setOperations).expire(
this.session.getMaxInactiveInterval().plusMinutes(5).getSeconds(),
TimeUnit.SECONDS);
}
@Test
public void onExpirationUpdatedPersistOnNegativeExpiration() throws Exception {
long originalExpirationTimeInMs = ONE_MINUTE_AGO;
this.session.setMaxInactiveInterval(Duration.ofSeconds(-1));
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
verify(this.setOperations).remove("expires:" + this.session.getId());
verify(this.valueOperations).append("");
verify(this.valueOperations).persist();
verify(this.hashOperations).persist();
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2014-2017 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class EnableRedisKeyspaceNotificationsInitializerTests {
static final String CONFIG_NOTIFY_KEYSPACE_EVENTS = "notify-keyspace-events";
@Mock
RedisConnectionFactory connectionFactory;
@Mock
RedisConnection connection;
@Captor
ArgumentCaptor<String> options;
RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer initializer;
@Before
public void setup() {
given(this.connectionFactory.getConnection()).willReturn(this.connection);
this.initializer = new RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer(
this.connectionFactory, new ConfigureNotifyKeyspaceEventsAction());
}
@Test
public void afterPropertiesSetUnset() throws Exception {
setConfigNotification("");
this.initializer.afterPropertiesSet();
assertOptionsContains("E", "g", "x");
}
@Test
public void afterPropertiesSetA() throws Exception {
setConfigNotification("A");
this.initializer.afterPropertiesSet();
assertOptionsContains("A", "E");
}
@Test
public void afterPropertiesSetE() throws Exception {
setConfigNotification("E");
this.initializer.afterPropertiesSet();
assertOptionsContains("E", "g", "x");
}
@Test
public void afterPropertiesSetK() throws Exception {
setConfigNotification("K");
this.initializer.afterPropertiesSet();
assertOptionsContains("K", "E", "g", "x");
}
@Test
public void afterPropertiesSetAE() throws Exception {
setConfigNotification("AE");
this.initializer.afterPropertiesSet();
verify(this.connection, never()).setConfig(anyString(), anyString());
}
@Test
public void afterPropertiesSetAK() throws Exception {
setConfigNotification("AK");
this.initializer.afterPropertiesSet();
assertOptionsContains("A", "K", "E");
}
@Test
public void afterPropertiesSetEK() throws Exception {
setConfigNotification("EK");
this.initializer.afterPropertiesSet();
assertOptionsContains("E", "K", "g", "x");
}
@Test
public void afterPropertiesSetEg() throws Exception {
setConfigNotification("Eg");
this.initializer.afterPropertiesSet();
assertOptionsContains("E", "g", "x");
}
@Test
public void afterPropertiesSetE$() throws Exception {
setConfigNotification("E$");
this.initializer.afterPropertiesSet();
assertOptionsContains("E", "$", "g", "x");
}
@Test
public void afterPropertiesSetKg() throws Exception {
setConfigNotification("Kg");
this.initializer.afterPropertiesSet();
assertOptionsContains("K", "g", "E", "x");
}
@Test
public void afterPropertiesSetAEK() throws Exception {
setConfigNotification("AEK");
this.initializer.afterPropertiesSet();
verify(this.connection, never()).setConfig(anyString(), anyString());
}
private void assertOptionsContains(String... expectedValues) {
verify(this.connection).setConfig(eq(CONFIG_NOTIFY_KEYSPACE_EVENTS),
this.options.capture());
for (String expectedValue : expectedValues) {
assertThat(this.options.getValue()).contains(expectedValue);
}
assertThat(this.options.getValue().length()).isEqualTo(expectedValues.length);
}
private void setConfigNotification(String value) {
given(this.connection.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS))
.willReturn(Arrays.asList(CONFIG_NOTIFY_KEYSPACE_EVENTS, value));
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2014-2016 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class RedisHttpSessionConfigurationClassPathXmlApplicationContextTests {
// gh-318
@Test
public void contextLoads() {
}
static RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2014-2017 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
*
*/
public class RedisHttpSessionConfigurationCustomCronTests {
AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
}
@After
public void closeContext() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void overrideCron() {
this.context.register(Config.class);
assertThatThrownBy(() ->
RedisHttpSessionConfigurationCustomCronTests.this.context.refresh())
.hasStackTraceContaining(
"Encountered invalid @Scheduled method 'cleanupExpiredSessions': Cron expression must consist of 6 fields (found 1 in \"oops\")");
}
@EnableRedisHttpSession
@Configuration
@PropertySource("classpath:spring-session-cleanup-cron-expression-oops.properties")
static class Config {
@Bean
public RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2014-2017 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
public class RedisHttpSessionConfigurationMockTests {
@Mock
RedisConnectionFactory factory;
@Mock
RedisConnection connection;
@Before
public void setup() {
given(this.factory.getConnection()).willReturn(this.connection);
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenNoOpThenNoInteractionWithConnectionFactory()
throws Exception {
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, ConfigureRedisAction.NO_OP);
init.afterPropertiesSet();
verifyZeroInteractions(this.factory);
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenExceptionThenCloseConnection()
throws Exception {
ConfigureRedisAction action = mock(ConfigureRedisAction.class);
willThrow(new RuntimeException()).given(action).configure(this.connection);
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, action);
try {
init.afterPropertiesSet();
failBecauseExceptionWasNotThrown(Throwable.class);
}
catch (Throwable success) {
}
verify(this.connection).close();
}
@Test
public void enableRedisKeyspaceNotificationsInitializerAfterPropertiesSetWhenNoExceptionThenCloseConnection()
throws Exception {
ConfigureRedisAction action = mock(ConfigureRedisAction.class);
EnableRedisKeyspaceNotificationsInitializer init = new EnableRedisKeyspaceNotificationsInitializer(
this.factory, action);
init.afterPropertiesSet();
verify(this.connection).close();
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2014-2016 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RedisHttpSessionConfigurationNoOpConfigureRedisActionTests {
@Test
public void redisConnectionFactoryNotUsedSinceNoValidation() {
}
@EnableRedisHttpSession
@Configuration
static class Config {
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return mock(RedisConnectionFactory.class);
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2014-2016 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RedisHttpSessionConfigurationOverrideDefaultSerializerTests {
@Autowired
RedisTemplate<Object, Object> template;
@Autowired
RedisSerializer<Object> defaultRedisSerializer;
@Test
public void overrideDefaultRedisTemplate() {
assertThat(this.template.getDefaultSerializer())
.isSameAs(this.defaultRedisSerializer);
}
@EnableRedisHttpSession
@Configuration
static class Config {
@Bean
@SuppressWarnings("unchecked")
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return mock(RedisSerializer.class);
}
@Bean
public RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2014-2017 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import java.util.concurrent.Executor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.scheduling.SchedulingAwareRunnable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Vladimir Tsanev
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RedisHttpSessionConfigurationOverrideSessionTaskExecutor {
@Autowired
RedisMessageListenerContainer redisMessageListenerContainer;
@Autowired
Executor springSessionRedisTaskExecutor;
@Test
public void overrideSessionTaskExecutor() {
verify(this.springSessionRedisTaskExecutor, times(1))
.execute(any(SchedulingAwareRunnable.class));
}
@EnableRedisHttpSession
@Configuration
static class Config {
@Bean
public Executor springSessionRedisTaskExecutor() {
return mock(Executor.class);
}
@Bean
public RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2014-2017 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import java.util.concurrent.Executor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.scheduling.SchedulingAwareRunnable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Vladimir Tsanev
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RedisHttpSessionConfigurationOverrideSessionTaskExecutors {
@Autowired
RedisMessageListenerContainer redisMessageListenerContainer;
@Autowired
Executor springSessionRedisTaskExecutor;
@Autowired
Executor springSessionRedisSubscriptionExecutor;
@Test
public void overrideSessionTaskExecutors() {
verify(this.springSessionRedisSubscriptionExecutor, times(1))
.execute(any(SchedulingAwareRunnable.class));
verify(this.springSessionRedisTaskExecutor, never()).execute(any(Runnable.class));
}
@EnableRedisHttpSession
@Configuration
static class Config {
@Bean
public Executor springSessionRedisTaskExecutor() {
return mock(Executor.class);
}
@Bean
public Executor springSessionRedisSubscriptionExecutor() {
return mock(Executor.class);
}
@Bean
public RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2014-2016 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* @author Eddú Meléndez
*/
public class RedisHttpSessionConfigurationTests {
private AnnotationConfigApplicationContext context;
@Before
public void before() {
this.context = new AnnotationConfigApplicationContext();
}
@After
public void after() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void resolveValue() {
registerAndRefresh(RedisConfig.class, CustomRedisHttpSessionConfiguration.class);
RedisHttpSessionConfiguration configuration = this.context.getBean(RedisHttpSessionConfiguration.class);
assertThat(ReflectionTestUtils.getField(configuration, "redisNamespace")).isEqualTo("myRedisNamespace");
}
@Test
public void resolveValueByPlaceholder() {
this.context.setEnvironment(new MockEnvironment().withProperty("session.redis.namespace", "customRedisNamespace"));
registerAndRefresh(RedisConfig.class, PropertySourceConfiguration.class, CustomRedisHttpSessionConfiguration2.class);
RedisHttpSessionConfiguration configuration = this.context.getBean(RedisHttpSessionConfiguration.class);
assertThat(ReflectionTestUtils.getField(configuration, "redisNamespace")).isEqualTo("customRedisNamespace");
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
}
@Configuration
static class PropertySourceConfiguration {
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
@Configuration
static class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisConnectionFactory connectionFactory = mock(RedisConnectionFactory.class);
given(connectionFactory.getConnection()).willReturn(mock(RedisConnection.class));
return connectionFactory;
}
}
@Configuration
@EnableRedisHttpSession(redisNamespace = "myRedisNamespace")
static class CustomRedisHttpSessionConfiguration {
}
@Configuration
@EnableRedisHttpSession(redisNamespace = "${session.redis.namespace}")
static class CustomRedisHttpSessionConfiguration2 {
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2014-2016 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RedisHttpSessionConfigurationXmlCustomExpireTests {
@Test
public void contextLoads() {
}
static RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2014-2016 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RedisHttpSessionConfigurationXmlTests {
@Test
public void contextLoads() {
}
static RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2014-2016 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
*
* 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.
*/
package org.springframework.session.data.redis.config.annotation.web.http.gh109;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* This test must be in a different package than RedisHttpSessionConfiguration.
*
* @author Rob Winch
* @since 1.0.2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class Gh109Tests {
@Test
public void loads() {
}
@Configuration
static class Config extends RedisHttpSessionConfiguration {
int sessionTimeout = 100;
/**
* override sessionRepository construction to set the custom session-timeout
*/
@Bean
@Override
public RedisOperationsSessionRepository sessionRepository(
RedisOperations<Object, Object> sessionRedisTemplate,
ApplicationEventPublisher applicationEventPublisher) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
sessionRedisTemplate);
sessionRepository.setDefaultMaxInactiveInterval(this.sessionTimeout);
return sessionRepository;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisConnectionFactory factory = mock(RedisConnectionFactory.class);
RedisConnection connection = mock(RedisConnection.class);
given(factory.getConnection()).willReturn(connection);
return factory;
}
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfigurationClassPathXmlApplicationContextTests"
factory-method="connectionFactory"/>
</beans>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"
p:maxInactiveIntervalInSeconds="3600"/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfigurationXmlCustomExpireTests"
factory-method="connectionFactory"/>
</beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfigurationXmlTests"
factory-method="connectionFactory"/>
</beans>

View File

@@ -0,0 +1 @@
spring.session.cleanup.cron.expression=oops