blob: 76cc57222c7f6f487e9f6d070a83304afd101609 [file] [log] [blame]
package org.apache.ignite.spring.sessions;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.cache.expiry.TouchedExpiryPolicy;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.configuration.CacheConfiguration;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.FlushMode;
import org.springframework.session.MapSession;
import org.apache.ignite.spring.sessions.IgniteIndexedSessionRepository.IgniteSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Tests for {@link IgniteIndexedSessionRepository}.
*/
class IgniteIndexedSessionRepositoryTests {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private final Ignite ignite = mock(Ignite.class);
@SuppressWarnings("unchecked")
private final IgniteCache<String, IgniteSession> sessions = mock(IgniteCache.class);
private IgniteIndexedSessionRepository repository;
@BeforeEach
void setUp() {
given(this.ignite.<String, IgniteSession>getOrCreateCache(
ArgumentMatchers.<CacheConfiguration<String, IgniteSession>>any())).willReturn(this.sessions);
given(this.sessions.withExpiryPolicy(ArgumentMatchers.any())).willReturn(this.sessions);
this.repository = new IgniteIndexedSessionRepository(this.ignite);
this.repository.init();
}
@Test
void constructorNullIgnite() {
assertThatIllegalArgumentException().isThrownBy(() -> new IgniteIndexedSessionRepository(null))
.withMessage("Ignite must not be null");
}
@Test
void setSaveModeNull() {
assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setSaveMode(null))
.withMessage("saveMode must not be null");
}
@Test
void createSessionDefaultMaxInactiveInterval() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession session = this.repository.createSession();
assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval());
verifyNoMoreInteractions(this.sessions);
}
@Test
void createSessionCustomMaxInactiveInterval() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
int interval = 1;
this.repository.setDefaultMaxInactiveInterval(interval);
IgniteSession session = this.repository.createSession();
assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(interval));
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveNewFlushModeOnSave() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession session = this.repository.createSession();
verifyNoMoreInteractions(this.sessions);
this.repository.save(session);
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveNewFlushModeImmediate() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
this.repository.setFlushMode(FlushMode.IMMEDIATE);
IgniteSession session = this.repository.createSession();
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUpdatedAttributeFlushModeOnSave() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession session = this.repository.createSession();
session.setAttribute("testName", "testValue");
verifyNoMoreInteractions(this.sessions);
this.repository.save(session);
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUpdatedAttributeFlushModeImmediate() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
this.repository.setFlushMode(FlushMode.IMMEDIATE);
IgniteSession session = this.repository.createSession();
session.setAttribute("testName", "testValue");
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).replace(eq(session.getId()), eq(session));
this.repository.save(session);
verifyNoMoreInteractions(this.sessions);
}
@Test
void removeAttributeFlushModeOnSave() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession session = this.repository.createSession();
session.removeAttribute("testName");
verifyNoMoreInteractions(this.sessions);
this.repository.save(session);
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
verifyNoMoreInteractions(this.sessions);
}
@Test
void removeAttributeFlushModeImmediate() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
this.repository.setFlushMode(FlushMode.IMMEDIATE);
IgniteSession session = this.repository.createSession();
session.removeAttribute("testName");
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).replace(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
this.repository.save(session);
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUpdatedLastAccessedTimeFlushModeOnSave() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession session = this.repository.createSession();
session.setLastAccessedTime(Instant.now());
verifyNoMoreInteractions(this.sessions);
this.repository.save(session);
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUpdatedLastAccessedTimeFlushModeImmediate() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
this.repository.setFlushMode(FlushMode.IMMEDIATE);
IgniteSession session = this.repository.createSession();
session.setLastAccessedTime(Instant.now());
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).replace(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
this.repository.save(session);
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUpdatedMaxInactiveIntervalInSecondsFlushModeOnSave() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession session = this.repository.createSession();
session.setMaxInactiveInterval(Duration.ofSeconds(1));
verifyNoMoreInteractions(this.sessions);
this.repository.save(session);
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUpdatedMaxInactiveIntervalInSecondsFlushModeImmediate() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
this.repository.setFlushMode(FlushMode.IMMEDIATE);
IgniteSession session = this.repository.createSession();
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
String sessionId = session.getId();
session.setMaxInactiveInterval(Duration.ofSeconds(1));
verify(this.sessions, times(1)).put(eq(sessionId), eq(session));
verify(this.sessions, times(1)).replace(eq(sessionId), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
this.repository.save(session);
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUnchangedFlushModeOnSave() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession session = this.repository.createSession();
this.repository.save(session);
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
this.repository.save(session);
verifyNoMoreInteractions(this.sessions);
}
@Test
void saveUnchangedFlushModeImmediate() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
this.repository.setFlushMode(FlushMode.IMMEDIATE);
IgniteSession session = this.repository.createSession();
verify(this.sessions, times(1)).put(eq(session.getId()), eq(session));
verify(this.sessions, times(1)).withExpiryPolicy(eq(createExpiryPolicy(session)));
this.repository.save(session);
verifyNoMoreInteractions(this.sessions);
}
@Test
void getSessionNotFound() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
String sessionId = "testSessionId";
IgniteSession session = this.repository.findById(sessionId);
assertThat(session).isNull();
verify(this.sessions, times(1)).get(eq(sessionId));
verifyNoMoreInteractions(this.sessions);
}
@Test
void getSessionExpired() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession expired = this.repository.new IgniteSession(new MapSession(), true);
expired.setLastAccessedTime(Instant.now().minusSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1));
given(this.sessions.get(eq(expired.getId()))).willReturn(expired);
IgniteSession session = this.repository.findById(expired.getId());
assertThat(session).isNull();
verify(this.sessions, times(1)).get(eq(expired.getId()));
verify(this.sessions, times(1)).remove(eq(expired.getId()));
verifyNoMoreInteractions(this.sessions);
}
@Test
void getSessionFound() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
IgniteSession saved = this.repository.new IgniteSession(new MapSession(), true);
saved.setAttribute("savedName", "savedValue");
given(this.sessions.get(eq(saved.getId()))).willReturn(saved);
IgniteSession session = this.repository.findById(saved.getId());
assertThat(session.getId()).isEqualTo(saved.getId());
assertThat(session.<String>getAttribute("savedName")).isEqualTo("savedValue");
verify(this.sessions, times(1)).get(eq(saved.getId()));
verifyNoMoreInteractions(this.sessions);
}
@Test
void delete() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
String sessionId = "testSessionId";
this.repository.deleteById(sessionId);
verify(this.sessions, times(1)).remove(eq(sessionId));
verifyNoMoreInteractions(this.sessions);
}
@Test
void findByIndexNameAndIndexValueUnknownIndexName() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
String indexValue = "testIndexValue";
Map<String, IgniteSession> sessions = this.repository.findByIndexNameAndIndexValue("testIndexName", indexValue);
assertThat(sessions).isEmpty();
verifyNoMoreInteractions(this.sessions);
}
@Test
void findByIndexNameAndIndexValuePrincipalIndexNameNotFound() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
String principal = "username";
Map<String, IgniteSession> sessions = this.repository
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
verify(this.sessions, times(1)).query(ArgumentMatchers
.argThat((argument) -> ("SELECT * FROM IgniteSession WHERE principal='" + principal + "'")
.equals(argument.getSql())));
assertThat(sessions).isEmpty();
verifyNoMoreInteractions(this.sessions);
}
@Test
void findByIndexNameAndIndexValuePrincipalIndexNameFound() {
verify(this.sessions, times(1)).registerCacheEntryListener(ArgumentMatchers.any());
String principal = "username";
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, "notused",
AuthorityUtils.createAuthorityList("ROLE_USER"));
List<Object> saved = new ArrayList<>(2);
final MapSession ses1 = new MapSession();
ses1.setAttribute(SPRING_SECURITY_CONTEXT, authentication);
IgniteSession saved1 = this.repository.new IgniteSession(ses1, true);
saved.add(Arrays.asList(ses1, authentication.getPrincipal()));
final MapSession ses2 = new MapSession();
ses2.setAttribute(SPRING_SECURITY_CONTEXT, authentication);
IgniteSession saved2 = this.repository.new IgniteSession(ses2, true);
saved.add(Arrays.asList(ses2, authentication.getPrincipal()));
given(this.sessions.query(ArgumentMatchers.any())).willReturn(new FieldsQueryCursor<List<?>>() {
@Override
public String getFieldName(int idx) {
return null;
}
@Override
public int getColumnsCount() {
return 2;
}
@Override
public List<List<?>> getAll() {
return (List) saved;
}
@Override
public void close() {
}
@NotNull
@Override
public Iterator<List<?>> iterator() {
return (Iterator) saved.iterator();
}
});
Map<String, IgniteSession> sessions = this.repository
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
assertThat(sessions).hasSize(2);
verify(this.sessions, times(1)).query(any());
verifyNoMoreInteractions(this.sessions);
}
@Test
void getAttributeNamesAndRemove() {
IgniteSession session = this.repository.createSession();
session.setAttribute("attribute1", "value1");
session.setAttribute("attribute2", "value2");
for (String attributeName : session.getAttributeNames()) {
session.removeAttribute(attributeName);
}
assertThat(session.getAttributeNames()).isEmpty();
}
private static TouchedExpiryPolicy createExpiryPolicy(IgniteSession session) {
return new TouchedExpiryPolicy(
new javax.cache.expiry.Duration(TimeUnit.SECONDS, session.getMaxInactiveInterval().getSeconds()));
}
}