/*
*
* * Copyright (c) 2016. David Sowerby
* *
* * 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 uk.q3c.krail.core.option;
import com.google.common.cache.CacheStats;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
import com.mycila.testing.junit.MycilaJunitRunner;
import com.mycila.testing.plugin.guice.GuiceContext;
import com.mycila.testing.plugin.guice.ModuleProvider;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import uk.q3c.krail.core.guice.vsscope.VaadinSessionScopeModule;
import uk.q3c.krail.core.i18n.LabelKey;
import uk.q3c.krail.core.i18n.Translate;
import uk.q3c.krail.core.persist.cache.common.GuavaCacheConfiguration;
import uk.q3c.krail.core.persist.cache.option.DefaultOptionCache;
import uk.q3c.krail.core.persist.cache.option.DefaultOptionCacheLoader;
import uk.q3c.krail.core.persist.cache.option.DefaultOptionCacheProvider;
import uk.q3c.krail.core.persist.cache.option.OptionCacheKey;
import uk.q3c.krail.core.persist.common.common.KrailPersistenceUnitHelper;
import uk.q3c.krail.core.persist.common.common.OptionDaoProviders;
import uk.q3c.krail.core.persist.common.common.PersistenceInfo;
import uk.q3c.krail.core.persist.common.option.*;
import uk.q3c.krail.core.persist.inmemory.option.DefaultInMemoryOptionStore;
import uk.q3c.krail.core.persist.inmemory.option.InMemoryOptionDaoDelegate;
import uk.q3c.krail.core.persist.inmemory.option.InMemoryOptionStore;
import uk.q3c.krail.core.shiro.SubjectIdentifier;
import uk.q3c.krail.core.shiro.SubjectProvider;
import uk.q3c.krail.core.user.profile.RankOption;
import uk.q3c.krail.core.user.profile.SimpleUserHierarchy;
import uk.q3c.krail.core.user.profile.UserHierarchy;
import uk.q3c.krail.core.view.component.LocaleContainer;
import uk.q3c.krail.testutil.option.TestOptionModule;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
/**
* Running this test through the debugger sometimes causes random failures - running normally doesn't
*/
@RunWith(MycilaJunitRunner.class)
@GuiceContext({TestOptionModule.class, VaadinSessionScopeModule.class})
public class Option_IntegrationTest {
Map<Class<? extends Annotation>, PersistenceInfo<?>> optionDaoProviders = new HashMap<>();
DefaultOptionCacheLoader cacheLoader;
DefaultOption option;
DefaultOptionCache optionCache;
@Inject
DefaultOptionCacheProvider cacheProvider;
@Inject
DefaultInMemoryOptionStore optionStore;
@Inject
OptionDao optionDao;
@Mock
LocaleContainer localeContainer;
@Mock
Subject subject1;
@Mock
Subject subject2;
@Mock
Translate translate;
OptionKey<Integer> key1 = LocaleContainer.optionKeyFlagSize;
OptionKey<Integer> key3 = new OptionKey<>(133, LocaleContainer.class, LabelKey.Alphabetic_Ascending);
@Mock
PersistenceInfo persistenceInfo;
private UserHierarchy hierarchy;
@Mock
private SubjectIdentifier subjectIdentifier;
@Mock
private SubjectProvider subjectProvider;
@Before
public void setup() {
optionStore.clear();
when(subjectIdentifier.userId()).thenReturn("fbaton");
when(subjectProvider.get()).thenReturn(subject1);
when(subject1.isPermitted(any(OptionPermission.class))).thenReturn(true);
when(subject2.isPermitted(any(OptionPermission.class))).thenReturn(true);
hierarchy = new SimpleUserHierarchy(subjectProvider, subjectIdentifier, translate);
cacheLoader = new DefaultOptionCacheLoader(optionDao);
optionCache = new DefaultOptionCache(optionDao, cacheProvider);
option = new DefaultOption(optionCache, hierarchy, subjectProvider, subjectIdentifier);
}
@Test
public void putAndGet_user_authenticated() {
//given
when(subject1.isAuthenticated()).thenReturn(true);
//when
option.set(key1, 3);
//then
assertThat(option.get(key1)).isEqualTo(3);
}
@Test
public void putAndGet_user_not_authenticated() {
//given
when(subject1.isAuthenticated()).thenReturn(false);
//when
option.set(key1, 3);
//then
assertThat(option.get(key1)).isEqualTo(3);
}
@Test
public void put_and_get_override() {
//given
when(subject1.isAuthenticated()).thenReturn(true);
//when
option.set(key1, 3);
option.set(key1, 1, 7);
//then
assertThat(option.get(key1)).isEqualTo(3);
assertThat(option.get(key1)).isEqualTo(3);
}
@Test
public void defaultValue_cache_empty_persistence_empty() {
//given
when(subject1.isAuthenticated()).thenReturn(true);
//when
//then
assertThat(option.get(key1)).isEqualTo(32);
}
@Test
public void valueIsSet() {
//given
when(subject1.isAuthenticated()).thenReturn(true);
//when
option.set(key1, 3);
//then
assertThat(option.get(key1)).isEqualTo(3);
}
@Test
public void stats() {
//given
//when
CacheStats actual = optionCache.stats();
//then
assertThat(actual).isNotNull();
assertThat(actual).isInstanceOf(CacheStats.class);
}
@Test
public void cacheHits() {
//given
when(subjectProvider.get()).thenReturn(subject1);
when(subject1.isAuthenticated()).thenReturn(true);
when(subjectIdentifier.userId()).thenReturn("fbaton");
DefaultOption option2 = new DefaultOption(optionCache, hierarchy, subjectProvider, subjectIdentifier);
//when
option2.set(key1, 3);
Integer actual = option2.get(key1);
//then
assertThat(actual).isEqualTo(3);
option2.get(key1); //populate
option2.get(key1); //cache hit
option2.get(key1); //cache hit
option2.get(key1); //cache hit
option2.get(key1); //cache hit
assertThat(optionCache.cacheSize()).isEqualTo(2); // creates a highest and a specific cache entry
assertThat(optionCache.stats()
.hitCount()).isEqualTo(5);
}
/**
* When a value is written to the cache, must invalidate the highest and lowest entry for the same OptionKey
*/
@SuppressWarnings("unchecked")
@Test
public void write_to_Cache_invalidate() {
//given
when(subjectProvider.get()).thenReturn(subject1);
when(subject1.isAuthenticated()).thenReturn(true);
when(subjectIdentifier.userId()).thenReturn("fbaton");
OptionCacheKey<Integer> highestKey = new OptionCacheKey<>(hierarchy, RankOption.HIGHEST_RANK, key3);
OptionCacheKey<Integer> lowestKey = new OptionCacheKey<>(hierarchy, RankOption.LOWEST_RANK, key3);
OptionCacheKey<Integer> specificKey = new OptionCacheKey<>(hierarchy, RankOption.SPECIFIC_RANK, 1, key3);
optionDao.write(specificKey, Optional.of(236));
//when
optionCache.get(Optional.of(highestKey.getOptionKey()
.getDefaultValue()), highestKey);
optionCache.get(Optional.of(lowestKey.getOptionKey()
.getDefaultValue()), lowestKey);
Optional<Integer> actualHigh = (Optional<Integer>) optionCache.getIfPresent(highestKey);
Optional<Integer> actualLow = (Optional<Integer>) optionCache.getIfPresent(lowestKey);
//then
assertThat(actualHigh).isNotEqualTo(Optional.empty());
assertThat(actualLow).isNotEqualTo(Optional.empty());
assertThat(actualHigh.get()).isEqualTo(236);
assertThat(actualLow.get()).isEqualTo(236);
//when write occurs cache should invalidate highest and lowest
optionCache.write(specificKey, Optional.of(44));
actualHigh = (Optional<Integer>) optionCache.getIfPresent(highestKey);
//noinspection unchecked
actualLow = (Optional<Integer>) optionCache.getIfPresent(lowestKey);
Optional<Integer> actualSpecific = (Optional<Integer>) optionCache.getIfPresent(specificKey);
//then highest and lowest invalidated
assertThat(actualSpecific).isEqualTo(Optional.of(44));
assertThat(actualHigh).isNull();
assertThat(actualLow).isNull();
}
@ModuleProvider
protected AbstractModule moduleProvider() {
return new AbstractModule() {
@Override
protected void configure() {
GuavaCacheConfiguration cacheConfig = new GuavaCacheConfiguration().recordStats();
bind(OptionDaoDelegate.class).annotatedWith(InMemory.class)
.to(InMemoryOptionDaoDelegate.class);
bind(OptionSource.class).to(DefaultOptionSource.class);
bind(KrailPersistenceUnitHelper.annotationClassLiteral()).annotatedWith(DefaultActiveOptionSource.class)
.toInstance(InMemory.class);
TypeLiteral<Map<Class<? extends Annotation>, PersistenceInfo<?>>> setAnnotationTypeLiteral = new TypeLiteral<Map<Class<? extends Annotation>, PersistenceInfo<?>>>() {
};
optionDaoProviders.put(InMemory.class, persistenceInfo);
bind(setAnnotationTypeLiteral).annotatedWith(OptionDaoProviders.class)
.toInstance(optionDaoProviders);
bind(OptionDaoDelegate.class).annotatedWith(InMemory.class)
.to(InMemoryOptionDaoDelegate.class);
bind(InMemoryOptionStore.class).to(DefaultInMemoryOptionStore.class);
}
};
}
}