/* * Copyright 2014-2016 CyberVision, Inc. * * 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.kaaproject.kaa.server.operations.service.cache; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.mock; 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.when; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.kaaproject.kaa.common.dto.ApplicationDto; import org.kaaproject.kaa.common.dto.ChangeDto; import org.kaaproject.kaa.common.dto.ChangeType; import org.kaaproject.kaa.common.dto.ConfigurationDto; import org.kaaproject.kaa.common.dto.ConfigurationSchemaDto; import org.kaaproject.kaa.common.dto.EndpointConfigurationDto; import org.kaaproject.kaa.common.dto.EndpointProfileDto; import org.kaaproject.kaa.common.dto.EndpointProfileSchemaDto; import org.kaaproject.kaa.common.dto.HistoryDto; import org.kaaproject.kaa.common.dto.ProfileFilterDto; import org.kaaproject.kaa.common.dto.TopicListEntryDto; import org.kaaproject.kaa.common.dto.admin.SdkProfileDto; import org.kaaproject.kaa.common.dto.event.ApplicationEventAction; import org.kaaproject.kaa.common.dto.event.ApplicationEventFamilyMapDto; import org.kaaproject.kaa.common.dto.event.ApplicationEventMapDto; import org.kaaproject.kaa.common.dto.event.EventClassDto; import org.kaaproject.kaa.common.dto.event.EventClassFamilyDto; import org.kaaproject.kaa.common.hash.EndpointObjectHash; import org.kaaproject.kaa.server.common.dao.AbstractTest; import org.kaaproject.kaa.server.common.dao.ApplicationEventMapService; import org.kaaproject.kaa.server.common.dao.ApplicationService; import org.kaaproject.kaa.server.common.dao.ConfigurationService; import org.kaaproject.kaa.server.common.dao.EndpointService; import org.kaaproject.kaa.server.common.dao.EventClassService; import org.kaaproject.kaa.server.common.dao.HistoryService; import org.kaaproject.kaa.server.common.dao.ProfileService; import org.kaaproject.kaa.server.common.dao.SdkProfileService; import org.kaaproject.kaa.server.operations.pojo.exceptions.GetDeltaException; import org.kaaproject.kaa.server.operations.service.cache.concurrent.ConcurrentCacheService; import org.kaaproject.kaa.server.operations.service.event.EventClassFamilyVersion; import org.kaaproject.kaa.server.operations.service.event.EventClassFqnVersion; import org.kaaproject.kaa.server.operations.service.event.RouteTableKey; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.util.ReflectionTestUtils; import java.security.GeneralSecurityException; import java.security.KeyPairGenerator; import java.security.PublicKey; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "/operations/cache-test-context.xml") @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) public class ConcurrentCacheServiceTest extends AbstractTest { private static final int EVENT_CLASS_FAMILY_VERSION = 42; private static final String EVENT_CLASS_ID = "EVENT_CLASS_ID"; private static final String EVENT_CLASS_FAMILY_VERSION_ID = "EVENT_CLASS_FAMILY_VERSION_ID"; private static final String EVENT_CLASS_FAMILY_ID = "EVENT_CLASS_FAMILY_ID"; private static final String ECF_NAME = "ECF_NAME"; private static final String EC_FQN = "EC_FQN"; private static final String TENANT_ID = "TENANT_ID"; private static final int CONF1_SCHEMA_VERSION = 1; private static final int PROFILE1_SCHEMA_VERSION = 1; private static final int PROFILE1_SERVER_SCHEMA_VERSION = 1; private static final int PROFILE2_SCHEMA_VERSION = 2; private static final int PROFILE2_SERVER_SCHEMA_VERSION = 2; private static final String CF1_ID = "cf1"; private static final String CF2_ID = "cf2"; private static final String CF3_ID = "cf3"; private static final String PF1_ID = "pf1"; private static final String PF2_ID = "pf2"; private static final String PF3_ID = "pf3"; private static final String ENDPOINT_GROUP1_ID = "eg1"; private static final int STRESS_TEST_N_THREADS = 10; private static final int STRESS_TEST_INVOCATIONS = 50; private static final int ESTIMATED_METHOD_EXECUTION_TIME = 5; private static final String TEST_APP_ID = "testAppId"; private static final String TEST_APP_TOKEN = "testApp"; private static final String TEST_SDK_TOKEN = "testSdkToken"; private static final String APP_ID = "testAppId"; private static final String DEFAULT_VERIFIER_TOKEN = "defaultVerifierToken"; private static final int TEST_APP_SEQ_NUMBER = 42; private static final int TEST_APP_SEQ_NUMBER_NEW = 46; private static final EndpointConfigurationDto CF1 = new EndpointConfigurationDto(); private static final ConfigurationSchemaDto CF1_SCHEMA = new ConfigurationSchemaDto(); private static final EndpointProfileSchemaDto PF1_SCHEMA = new EndpointProfileSchemaDto(); private static final SdkProfileDto SDK_PROFILE = new SdkProfileDto(); private static final ProfileFilterDto TEST_PROFILE_FILTER = new ProfileFilterDto(); private static final List<ProfileFilterDto> TEST_PROFILE_FILTER_LIST = Collections.singletonList(TEST_PROFILE_FILTER); private static final EndpointObjectHash CF1_HASH = EndpointObjectHash.fromSha1(CF1_ID); private static final EndpointObjectHash TLCE1_HASH = CF1_HASH; private static final TopicListEntryDto TLCE1 = new TopicListEntryDto(100, TLCE1_HASH.getData(), null); private static final ConfigurationIdKey TEST_CONF_ID_KEY = new ConfigurationIdKey(APP_ID, TEST_APP_SEQ_NUMBER, CONF1_SCHEMA_VERSION, ENDPOINT_GROUP1_ID); private static final HistoryKey TEST_HISTORY_KEY = new HistoryKey(TEST_APP_TOKEN, TEST_APP_SEQ_NUMBER, TEST_APP_SEQ_NUMBER_NEW, CONF1_SCHEMA_VERSION, PROFILE1_SCHEMA_VERSION, PROFILE1_SERVER_SCHEMA_VERSION); private static final AppProfileVersionsKey TEST_GET_PROFILES_KEY = new AppProfileVersionsKey(TEST_APP_TOKEN, PROFILE1_SCHEMA_VERSION, PROFILE1_SERVER_SCHEMA_VERSION); private static final AppVersionKey CF_SCHEMA_KEY = new AppVersionKey(TEST_APP_TOKEN, CONF1_SCHEMA_VERSION); private static final AppVersionKey PF_SCHEMA_KEY = new AppVersionKey(TEST_APP_TOKEN, PROFILE1_SCHEMA_VERSION); private static final List<String> AEFMAP_IDS = Arrays.asList("id1"); private static final ApplicationEventFamilyMapDto APPLICATION_EVENT_FAMILY_MAP_DTO = new ApplicationEventFamilyMapDto(); private static final List<ApplicationEventFamilyMapDto> AEFM_LIST = Arrays.asList(APPLICATION_EVENT_FAMILY_MAP_DTO); private PublicKey publicKey; private PublicKey publicKey2; private EndpointObjectHash publicKeyHash; private EndpointObjectHash publicKeyHash2; @Autowired private CacheService cacheService; private ApplicationService appService; private ConfigurationService configurationService; private HistoryService historyService; private ProfileService profileService; private EndpointService endpointService; private EventClassService eventClassService; private ApplicationEventMapService applicationEventMapService; private SdkProfileService sdkProfileService; public static void launchCodeInParallelThreads(final int nThreads, final Runnable task) { final CountDownLatch startGate = new CountDownLatch(1); final CountDownLatch endGate = new CountDownLatch(nThreads); for (int i = 0; i < nThreads; i++) { Thread t = new Thread() { @Override public void run() { try { startGate.await(); try { task.run(); } finally { endGate.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } } }; t.start(); } startGate.countDown(); try { endGate.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Before public void prepare() throws GeneralSecurityException { registerMocks(); final ApplicationDto appDto = new ApplicationDto(); appDto.setSequenceNumber(TEST_APP_SEQ_NUMBER); appDto.setId(APP_ID); appDto.setApplicationToken(TEST_APP_TOKEN); when(appService.findAppByApplicationToken(TEST_APP_TOKEN)).then(new Answer<ApplicationDto>() { @Override public ApplicationDto answer(InvocationOnMock invocation) { sleepABit(); return appDto; } }); SDK_PROFILE.setApplicationToken(TEST_APP_TOKEN); SDK_PROFILE.setApplicationId(APP_ID); SDK_PROFILE.setDefaultVerifierToken(DEFAULT_VERIFIER_TOKEN); when(sdkProfileService.findSdkProfileByToken(TEST_SDK_TOKEN)).then(new Answer<SdkProfileDto>() { @Override public SdkProfileDto answer(InvocationOnMock invocationOnMock) throws Throwable { sleepABit(); return SDK_PROFILE; } }); final List<ConfigurationDto> configurations = new ArrayList<ConfigurationDto>(); ConfigurationDto theConf = new ConfigurationDto(); theConf.setId(CF1_ID); theConf.setSchemaVersion(CONF1_SCHEMA_VERSION); configurations.add(theConf); when(configurationService.findConfigurationsByEndpointGroupId(ENDPOINT_GROUP1_ID)).then(new Answer<List<ConfigurationDto>>() { @Override public List<ConfigurationDto> answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return configurations; } }); final List<HistoryDto> historyList = new ArrayList<HistoryDto>(); historyList.add(buildMatchingHistoryDto(ChangeType.ADD_CONF)); historyList.add(buildNotMatchingHistoryDto(ChangeType.ADD_CONF)); historyList.add(buildMatchingHistoryDto(ChangeType.REMOVE_CONF)); historyList.add(buildNotMatchingHistoryDto(ChangeType.REMOVE_CONF)); historyList.add(buildMatchingHistoryDto(ChangeType.ADD_PROF)); historyList.add(buildNotMatchingHistoryDto(ChangeType.ADD_PROF)); historyList.add(buildMatchingHistoryDto(ChangeType.REMOVE_PROF)); historyList.add(buildNotMatchingHistoryDto(ChangeType.REMOVE_PROF)); historyList.add(buildMatchingHistoryDto(ChangeType.ADD_TOPIC)); historyList.add(buildMatchingHistoryDto(ChangeType.REMOVE_TOPIC)); historyList.add(buildMatchingHistoryDto(ChangeType.REMOVE_GROUP)); when(historyService.findHistoriesBySeqNumberRange(APP_ID, TEST_APP_SEQ_NUMBER, TEST_APP_SEQ_NUMBER_NEW)).then( new Answer<List<HistoryDto>>() { @Override public List<HistoryDto> answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return historyList; } }); when( profileService.findProfileFiltersByAppIdAndVersionsCombination(APP_ID, TEST_GET_PROFILES_KEY.getEndpointProfileSchemaVersion(), TEST_GET_PROFILES_KEY.getServerProfileSchemaVersion())).then(new Answer<List<ProfileFilterDto>>() { @Override public List<ProfileFilterDto> answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return TEST_PROFILE_FILTER_LIST; } }); when(profileService.findProfileFilterById(PF1_ID)).then(new Answer<ProfileFilterDto>() { @Override public ProfileFilterDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return TEST_PROFILE_FILTER; } }); when(profileService.findProfileFilterById(PF2_ID)).then(new Answer<ProfileFilterDto>() { @Override public ProfileFilterDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return TEST_PROFILE_FILTER; } }); when(profileService.findProfileFilterById(PF3_ID)).then(new Answer<ProfileFilterDto>() { @Override public ProfileFilterDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); ProfileFilterDto dto = new ProfileFilterDto(); dto.setEndpointProfileSchemaId(PROFILE2_SCHEMA_VERSION + ""); dto.setEndpointProfileSchemaVersion(PROFILE2_SCHEMA_VERSION); dto.setServerProfileSchemaId(PROFILE2_SERVER_SCHEMA_VERSION + ""); dto.setServerProfileSchemaVersion(PROFILE2_SERVER_SCHEMA_VERSION); return dto; } }); when(endpointService.findEndpointConfigurationByHash(CF1_HASH.getData())).then(new Answer<EndpointConfigurationDto>() { @Override public EndpointConfigurationDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return CF1; } }); when(endpointService.findTopicListEntryByHash(TLCE1_HASH.getData())).then(new Answer<TopicListEntryDto>() { @Override public TopicListEntryDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return TLCE1; } }); when(configurationService.findConfSchemaByAppIdAndVersion(APP_ID, CF_SCHEMA_KEY.getVersion())).then( new Answer<ConfigurationSchemaDto>() { @Override public ConfigurationSchemaDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return CF1_SCHEMA; } }); when(profileService.findProfileSchemaByAppIdAndVersion(APP_ID, PF_SCHEMA_KEY.getVersion())).then( new Answer<EndpointProfileSchemaDto>() { @Override public EndpointProfileSchemaDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return PF1_SCHEMA; } }); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN"); keyGen.initialize(512, random); publicKey = keyGen.genKeyPair().getPublic(); byte[] key = publicKey.getEncoded(); publicKeyHash = EndpointObjectHash.fromSha1(key); publicKey2 = keyGen.genKeyPair().getPublic(); byte[] key2 = publicKey2.getEncoded(); publicKeyHash2 = EndpointObjectHash.fromSha1(key2); final EndpointProfileDto ep = new EndpointProfileDto(); ep.setEndpointKey(key); when(endpointService.findEndpointProfileByKeyHash(publicKeyHash.getData())).then(new Answer<EndpointProfileDto>() { @Override public EndpointProfileDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return ep; } }); final EventClassFamilyDto ecfDto = new EventClassFamilyDto(); ecfDto.setId(EVENT_CLASS_FAMILY_ID); when(eventClassService.findEventClassFamilyByTenantIdAndName(TENANT_ID, ECF_NAME)).then(new Answer<EventClassFamilyDto>() { @Override public EventClassFamilyDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return ecfDto; } }); when(eventClassService.findEventClassFamilyByEcfvId(EVENT_CLASS_FAMILY_VERSION_ID)).then(new Answer<EventClassFamilyDto>() { @Override public EventClassFamilyDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return ecfDto; } }); final EventClassDto ecDto = new EventClassDto(); ecDto.setId(EVENT_CLASS_ID); ecDto.setEcfvId(EVENT_CLASS_FAMILY_VERSION_ID); final List<EventClassDto> eventClassDtos = new ArrayList<>(); eventClassDtos.add(ecDto); when(eventClassService.findEventClassByTenantIdAndFqn(TENANT_ID, EC_FQN)).then(new Answer<List<EventClassDto>>() { @Override public List<EventClassDto> answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return eventClassDtos; } }); final EventClassDto evcDto = new EventClassDto(); evcDto.setId(EVENT_CLASS_ID); evcDto.setEcfvId(EVENT_CLASS_FAMILY_VERSION_ID); evcDto.setVersion(EVENT_CLASS_FAMILY_VERSION); when(eventClassService.findEventClassByTenantIdAndFqnAndVersion(TENANT_ID, EC_FQN, EVENT_CLASS_FAMILY_VERSION)).then( new Answer<EventClassDto>() { @Override public EventClassDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return evcDto; } }); when(applicationEventMapService.findByEcfIdAndVersion(EVENT_CLASS_FAMILY_ID, EVENT_CLASS_FAMILY_VERSION)).then( new Answer<List<ApplicationEventFamilyMapDto>>() { @Override public List<ApplicationEventFamilyMapDto> answer(InvocationOnMock invocation) throws Throwable { ApplicationEventMapDto matchButSource = new ApplicationEventMapDto(); matchButSource.setAction(ApplicationEventAction.SOURCE); matchButSource.setEventClassId(EVENT_CLASS_ID); ApplicationEventMapDto match = new ApplicationEventMapDto(); match.setAction(ApplicationEventAction.BOTH); match.setEventClassId(EVENT_CLASS_ID); ApplicationEventFamilyMapDto mapping = new ApplicationEventFamilyMapDto(); mapping.setApplicationId(APP_ID); mapping.setEcfId(EVENT_CLASS_FAMILY_ID); mapping.setVersion(EVENT_CLASS_FAMILY_VERSION); mapping.setEventMaps(Arrays.asList(matchButSource, match)); return Arrays.asList(mapping); } }); APPLICATION_EVENT_FAMILY_MAP_DTO.setEcfName("someName"); when(applicationEventMapService.findApplicationEventFamilyMapsByIds(AEFMAP_IDS)).thenReturn(AEFM_LIST); when(appService.findAppById(APP_ID)).thenReturn(appDto); } @Test public void testGetAppSeqNumber() throws GetDeltaException { assertEquals(new AppSeqNumber(TENANT_ID, TEST_APP_ID, TEST_APP_TOKEN, TEST_APP_SEQ_NUMBER), cacheService.getAppSeqNumber(TEST_APP_TOKEN)); verify(appService, times(1)).findAppByApplicationToken(TEST_APP_TOKEN); reset(appService); assertEquals(new AppSeqNumber(TENANT_ID, TEST_APP_ID, TEST_APP_TOKEN, TEST_APP_SEQ_NUMBER), cacheService.getAppSeqNumber(TEST_APP_TOKEN)); verify(appService, times(0)).findAppByApplicationToken(TEST_APP_TOKEN); reset(appService); } @Test public void testConcurrentGetAppSeqNumberMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(new AppSeqNumber(TENANT_ID, TEST_APP_ID, TEST_APP_TOKEN, TEST_APP_SEQ_NUMBER), cacheService.getAppSeqNumber(TEST_APP_TOKEN)); } }); verify(appService, atMost(2)).findAppByApplicationToken(TEST_APP_TOKEN); reset(appService); } } @Test public void testGetConfIdNumber() throws GetDeltaException { assertEquals(CF1_ID, cacheService.getConfIdByKey(TEST_CONF_ID_KEY)); verify(configurationService, times(1)).findConfigurationsByEndpointGroupId(ENDPOINT_GROUP1_ID); reset(configurationService); assertEquals(CF1_ID, cacheService.getConfIdByKey(TEST_CONF_ID_KEY)); verify(configurationService, times(0)).findConfigurationsByEndpointGroupId(ENDPOINT_GROUP1_ID); reset(configurationService); } @Test public void testGetAppTokenBySdkToken() { assertEquals(TEST_APP_TOKEN, cacheService.getAppTokenBySdkToken(TEST_SDK_TOKEN)); verify(sdkProfileService, times(1)).findSdkProfileByToken(TEST_SDK_TOKEN); reset(sdkProfileService); assertEquals(TEST_APP_TOKEN, cacheService.getAppTokenBySdkToken(TEST_SDK_TOKEN)); verify(sdkProfileService, never()).findSdkProfileByToken(TEST_SDK_TOKEN); reset(sdkProfileService); } @Test public void testConcurrentGetConfIdMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(CF1_ID, cacheService.getConfIdByKey(TEST_CONF_ID_KEY)); } }); verify(configurationService, atMost(2)).findConfigurationsByEndpointGroupId(ENDPOINT_GROUP1_ID); reset(configurationService); } } @Test public void testGetHistory() throws GetDeltaException { List<HistoryDto> expectedList = getResultHistoryList(); assertEquals(expectedList, cacheService.getHistory(TEST_HISTORY_KEY)); verify(historyService, times(1)).findHistoriesBySeqNumberRange(APP_ID, TEST_APP_SEQ_NUMBER, TEST_APP_SEQ_NUMBER_NEW); reset(historyService); assertEquals(expectedList, cacheService.getHistory(TEST_HISTORY_KEY)); verify(historyService, times(0)).findHistoriesBySeqNumberRange(APP_ID, TEST_APP_SEQ_NUMBER, TEST_APP_SEQ_NUMBER_NEW); reset(historyService); } @Test public void testConcurrentGetHistoryMultipleTimes() throws GetDeltaException { final List<HistoryDto> expectedList = getResultHistoryList(); for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(expectedList, cacheService.getHistory(TEST_HISTORY_KEY)); } }); verify(historyService, atMost(2)).findHistoriesBySeqNumberRange(APP_ID, TEST_APP_SEQ_NUMBER, TEST_APP_SEQ_NUMBER_NEW); reset(historyService); } } @Test public void testGetFilters() throws GetDeltaException { assertEquals(TEST_PROFILE_FILTER_LIST, cacheService.getFilters(TEST_GET_PROFILES_KEY)); verify(profileService, times(1)).findProfileFiltersByAppIdAndVersionsCombination(APP_ID, TEST_GET_PROFILES_KEY.getEndpointProfileSchemaVersion(), TEST_GET_PROFILES_KEY.getServerProfileSchemaVersion()); reset(profileService); assertEquals(TEST_PROFILE_FILTER_LIST, cacheService.getFilters(TEST_GET_PROFILES_KEY)); verify(profileService, times(0)).findProfileFiltersByAppIdAndVersionsCombination(APP_ID, TEST_GET_PROFILES_KEY.getEndpointProfileSchemaVersion(), TEST_GET_PROFILES_KEY.getServerProfileSchemaVersion()); reset(profileService); } @Test public void testConcurrentGetFiltersMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(TEST_PROFILE_FILTER_LIST, cacheService.getFilters(TEST_GET_PROFILES_KEY)); } }); verify(profileService, atMost(2)).findProfileFiltersByAppIdAndVersionsCombination(APP_ID, TEST_GET_PROFILES_KEY.getEndpointProfileSchemaVersion(), TEST_GET_PROFILES_KEY.getServerProfileSchemaVersion()); reset(profileService); } } @Test public void testGetFilter() throws GetDeltaException { assertEquals(TEST_PROFILE_FILTER, cacheService.getFilter(PF1_ID)); verify(profileService, times(1)).findProfileFilterById(PF1_ID); reset(profileService); assertEquals(TEST_PROFILE_FILTER, cacheService.getFilter(PF1_ID)); verify(profileService, times(0)).findProfileFilterById(PF1_ID); reset(profileService); } @Test public void testConcurrentGetFilterMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(TEST_PROFILE_FILTER, cacheService.getFilter(PF1_ID)); } }); verify(profileService, atMost(2)).findProfileFilterById(PF1_ID); reset(profileService); } } @Test public void testGetConf() throws GetDeltaException { assertEquals(CF1, cacheService.getConfByHash(CF1_HASH)); verify(endpointService, times(1)).findEndpointConfigurationByHash(CF1_HASH.getData()); reset(endpointService); assertEquals(CF1, cacheService.getConfByHash(CF1_HASH)); verify(endpointService, times(0)).findEndpointConfigurationByHash(CF1_HASH.getData()); reset(endpointService); } @Test public void testConcurrentGetConfMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(CF1, cacheService.getConfByHash(CF1_HASH)); } }); verify(endpointService, atMost(2)).findEndpointConfigurationByHash(CF1_HASH.getData()); reset(endpointService); } } @Test public void testGetConfSchema() throws GetDeltaException { assertEquals(CF1_SCHEMA, cacheService.getConfSchemaByAppAndVersion(CF_SCHEMA_KEY)); verify(configurationService, times(1)).findConfSchemaByAppIdAndVersion(APP_ID, CF_SCHEMA_KEY.getVersion()); reset(configurationService); assertEquals(CF1_SCHEMA, cacheService.getConfSchemaByAppAndVersion(CF_SCHEMA_KEY)); verify(configurationService, times(0)).findConfSchemaByAppIdAndVersion(APP_ID, CF_SCHEMA_KEY.getVersion()); reset(configurationService); } @Test public void testConcurrentGetConfSchemaMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(CF1_SCHEMA, cacheService.getConfSchemaByAppAndVersion(CF_SCHEMA_KEY)); } }); verify(configurationService, atMost(2)).findConfigurationByAppIdAndVersion(APP_ID, CF_SCHEMA_KEY.getVersion()); reset(configurationService); } } @Test public void testGetProfileSchema() throws GetDeltaException { assertEquals(PF1_SCHEMA, cacheService.getProfileSchemaByAppAndVersion(PF_SCHEMA_KEY)); verify(profileService, times(1)).findProfileSchemaByAppIdAndVersion(APP_ID, PF_SCHEMA_KEY.getVersion()); reset(profileService); assertEquals(PF1_SCHEMA, cacheService.getProfileSchemaByAppAndVersion(PF_SCHEMA_KEY)); verify(profileService, times(0)).findProfileSchemaByAppIdAndVersion(APP_ID, PF_SCHEMA_KEY.getVersion()); reset(profileService); } @Test public void testGetSdkProperties() { assertEquals(SDK_PROFILE, cacheService.getSdkProfileBySdkToken(TEST_SDK_TOKEN)); verify(sdkProfileService, times(1)).findSdkProfileByToken(TEST_SDK_TOKEN); reset(sdkProfileService); assertEquals(SDK_PROFILE, cacheService.getSdkProfileBySdkToken(TEST_SDK_TOKEN)); verify(sdkProfileService, times(0)).findSdkProfileByToken(TEST_SDK_TOKEN); reset(sdkProfileService); } @Test public void testGetApplicationEventFamilyMapsByIds() { assertEquals(AEFM_LIST, cacheService.getApplicationEventFamilyMapsByIds(AEFMAP_IDS)); verify(applicationEventMapService, times(1)).findApplicationEventFamilyMapsByIds(AEFMAP_IDS); reset(applicationEventMapService); assertEquals(AEFM_LIST, cacheService.getApplicationEventFamilyMapsByIds(AEFMAP_IDS)); verify(applicationEventMapService, times(0)).findApplicationEventFamilyMapsByIds(AEFMAP_IDS); reset(applicationEventMapService); } @Test public void testConcurrentGetProfileSchemaMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(PF1_SCHEMA, cacheService.getProfileSchemaByAppAndVersion(PF_SCHEMA_KEY)); } }); verify(profileService, atMost(2)).findProfileSchemaByAppIdAndVersion(APP_ID, PF_SCHEMA_KEY.getVersion()); reset(profileService); } } @Test public void testGetEndpointKey() throws GetDeltaException { assertEquals(publicKey, cacheService.getEndpointKey(publicKeyHash)); verify(endpointService, times(1)).findEndpointProfileByKeyHash(publicKeyHash.getData()); reset(endpointService); assertEquals(publicKey, cacheService.getEndpointKey(publicKeyHash)); verify(endpointService, times(0)).findEndpointProfileByKeyHash(publicKeyHash.getData()); reset(endpointService); assertNull(cacheService.getEndpointKey(publicKeyHash2)); verify(endpointService, times(1)).findEndpointProfileByKeyHash(publicKeyHash2.getData()); reset(endpointService); final EndpointProfileDto ep2 = new EndpointProfileDto(); ep2.setEndpointKey(publicKey2.getEncoded()); when(endpointService.findEndpointProfileByKeyHash(publicKeyHash2.getData())).then(new Answer<EndpointProfileDto>() { @Override public EndpointProfileDto answer(InvocationOnMock invocation) throws Throwable { sleepABit(); return ep2; } }); cacheService.putEndpointKey(publicKeyHash2, publicKey2); assertEquals(publicKey2, cacheService.getEndpointKey(publicKeyHash2)); verify(endpointService, times(0)).findEndpointProfileByKeyHash(publicKeyHash2.getData()); reset(endpointService); } @Test public void testConcurrentGetEndpointMultipleTimes() throws GetDeltaException { for (int i = 0; i < STRESS_TEST_INVOCATIONS; i++) { launchCodeInParallelThreads(STRESS_TEST_N_THREADS, new Runnable() { @Override public void run() { assertEquals(publicKey, cacheService.getEndpointKey(publicKeyHash)); } }); verify(endpointService, atMost(2)).findEndpointProfileByKeyHash(publicKeyHash.getData()); reset(endpointService); } } @Test public void testGetEcfIdByName() throws GetDeltaException { assertEquals(EVENT_CLASS_FAMILY_ID, cacheService.getEventClassFamilyIdByName(new EventClassFamilyIdKey(TENANT_ID, ECF_NAME))); verify(eventClassService, times(1)).findEventClassFamilyByTenantIdAndName(TENANT_ID, ECF_NAME); reset(eventClassService); assertEquals(EVENT_CLASS_FAMILY_ID, cacheService.getEventClassFamilyIdByName(new EventClassFamilyIdKey(TENANT_ID, ECF_NAME))); verify(eventClassService, times(0)).findEventClassFamilyByTenantIdAndName(TENANT_ID, ECF_NAME); reset(eventClassService); } @Test public void testGetEcfIdByFqn() throws GetDeltaException { assertEquals(EVENT_CLASS_FAMILY_ID, cacheService.getEventClassFamilyIdByEventClassFqn(new EventClassFqnKey(TENANT_ID, EC_FQN))); verify(eventClassService, times(1)).findEventClassByTenantIdAndFqn(TENANT_ID, EC_FQN); reset(eventClassService); assertEquals(EVENT_CLASS_FAMILY_ID, cacheService.getEventClassFamilyIdByEventClassFqn(new EventClassFqnKey(TENANT_ID, EC_FQN))); verify(eventClassService, times(0)).findEventClassByTenantIdAndFqn(TENANT_ID, EC_FQN); reset(eventClassService); } @Test public void testGetRouteKeys() throws GetDeltaException { assertEquals(Collections.singleton(new RouteTableKey(TEST_APP_TOKEN, new EventClassFamilyVersion(EVENT_CLASS_FAMILY_ID, EVENT_CLASS_FAMILY_VERSION))), cacheService.getRouteKeys(new EventClassFqnVersion(TENANT_ID, EC_FQN, EVENT_CLASS_FAMILY_VERSION))); verify(eventClassService, times(1)).findEventClassByTenantIdAndFqnAndVersion(TENANT_ID, EC_FQN, EVENT_CLASS_FAMILY_VERSION); reset(eventClassService); assertEquals(Collections.singleton(new RouteTableKey(TEST_APP_TOKEN, new EventClassFamilyVersion(EVENT_CLASS_FAMILY_ID, EVENT_CLASS_FAMILY_VERSION))), cacheService.getRouteKeys(new EventClassFqnVersion(TENANT_ID, EC_FQN, EVENT_CLASS_FAMILY_VERSION))); verify(eventClassService, times(0)).findEventClassByTenantIdAndFqnAndVersion(TENANT_ID, EC_FQN, EVENT_CLASS_FAMILY_VERSION); reset(eventClassService); } @Test public void testIsSupported() { for (ChangeType change : ChangeType.values()) { switch (change) { case ADD_CONF: case ADD_PROF: case ADD_TOPIC: case REMOVE_CONF: case REMOVE_PROF: case REMOVE_TOPIC: case REMOVE_GROUP: Assert.assertTrue(ConcurrentCacheService.isSupported(change)); break; default: Assert.assertFalse(ConcurrentCacheService.isSupported(change)); break; } } } @Test public void testGetTopicListByHash() throws GetDeltaException { TopicListCacheEntry entry = cacheService.getTopicListByHash(CF1_HASH); assertNotNull(entry); verify(endpointService, times(1)).findTopicListEntryByHash(CF1_HASH.getData()); reset(endpointService); entry = cacheService.getTopicListByHash(CF1_HASH); assertNotNull(entry); verify(endpointService, times(0)).findTopicListEntryByHash(CF1_HASH.getData()); reset(endpointService); } private void registerMocks() { appService = mock(ApplicationService.class); configurationService = mock(ConfigurationService.class); historyService = mock(HistoryService.class); profileService = mock(ProfileService.class); endpointService = mock(EndpointService.class); eventClassService = mock(EventClassService.class); applicationEventMapService = mock(ApplicationEventMapService.class); sdkProfileService = mock(SdkProfileService.class); ReflectionTestUtils.invokeMethod(cacheService, "setApplicationService", appService); ReflectionTestUtils.invokeMethod(cacheService, "setConfigurationService", configurationService); ReflectionTestUtils.invokeMethod(cacheService, "setHistoryService", historyService); ReflectionTestUtils.invokeMethod(cacheService, "setProfileService", profileService); ReflectionTestUtils.invokeMethod(cacheService, "setEndpointService", endpointService); ReflectionTestUtils.invokeMethod(cacheService, "setEventClassService", eventClassService); ReflectionTestUtils.invokeMethod(cacheService, "setApplicationEventMapService", applicationEventMapService); ReflectionTestUtils.invokeMethod(cacheService, "setSdkProfileService", sdkProfileService); } private HistoryDto buildNotMatchingHistoryDto(ChangeType changeType) { HistoryDto notMatchingHistory = new HistoryDto(); ChangeDto notMatchingConfChange; notMatchingConfChange = new ChangeDto(); notMatchingHistory.setApplicationId(APP_ID); notMatchingHistory.setSequenceNumber(TEST_APP_SEQ_NUMBER + 2); notMatchingConfChange.setType(changeType); notMatchingConfChange.setEndpointGroupId(ENDPOINT_GROUP1_ID); notMatchingConfChange.setConfigurationId(CF3_ID); notMatchingConfChange.setCfVersion(CONF1_SCHEMA_VERSION + 1); notMatchingConfChange.setProfileFilterId(PF3_ID); notMatchingHistory.setChange(notMatchingConfChange); return notMatchingHistory; } private HistoryDto buildMatchingHistoryDto(ChangeType changeType) { HistoryDto matchingHistory; ChangeDto matchingConfChange; matchingHistory = new HistoryDto(); matchingConfChange = new ChangeDto(); matchingHistory.setApplicationId(APP_ID); matchingHistory.setSequenceNumber(TEST_APP_SEQ_NUMBER + 1); matchingConfChange.setType(changeType); matchingConfChange.setEndpointGroupId(ENDPOINT_GROUP1_ID); matchingConfChange.setConfigurationId(CF2_ID); matchingConfChange.setCfVersion(CONF1_SCHEMA_VERSION); matchingConfChange.setProfileFilterId(PF2_ID); matchingHistory.setChange(matchingConfChange); return matchingHistory; } private List<HistoryDto> getResultHistoryList() { List<HistoryDto> expectedList = new ArrayList<>(); expectedList.add(buildMatchingHistoryDto(ChangeType.ADD_CONF)); expectedList.add(buildMatchingHistoryDto(ChangeType.REMOVE_CONF)); expectedList.add(buildMatchingHistoryDto(ChangeType.ADD_PROF)); expectedList.add(buildMatchingHistoryDto(ChangeType.REMOVE_PROF)); expectedList.add(buildMatchingHistoryDto(ChangeType.ADD_TOPIC)); expectedList.add(buildMatchingHistoryDto(ChangeType.REMOVE_TOPIC)); expectedList.add(buildMatchingHistoryDto(ChangeType.REMOVE_GROUP)); return expectedList; } private void sleepABit() { try { Thread.sleep(ESTIMATED_METHOD_EXECUTION_TIME); } catch (InterruptedException e) { e.printStackTrace(); } } }