/* * 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.akka.actors.core.user; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import akka.actor.ActorContext; import akka.actor.ActorRef; import org.junit.Before; import org.junit.Test; import org.kaaproject.kaa.common.hash.EndpointObjectHash; import org.kaaproject.kaa.server.common.Base64Util; import org.kaaproject.kaa.server.operations.service.akka.AkkaContext; import org.kaaproject.kaa.server.operations.service.akka.messages.core.route.RouteOperation; import org.kaaproject.kaa.server.operations.service.akka.messages.core.session.EndpointEventTimeoutMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.EndpointEventReceiveMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.EndpointEventSendMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.EndpointUserConnectMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.RemoteEndpointEventMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.RouteInfoMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.UserRouteInfoMessage; import org.kaaproject.kaa.server.operations.service.cache.CacheService; import org.kaaproject.kaa.server.operations.service.cache.EventClassFqnKey; import org.kaaproject.kaa.server.operations.service.event.EndpointEvent; 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.EventService; import org.kaaproject.kaa.server.operations.service.event.EventStorage; import org.kaaproject.kaa.server.operations.service.event.RemoteEndpointEvent; import org.kaaproject.kaa.server.operations.service.event.RouteInfo; import org.kaaproject.kaa.server.operations.service.event.RouteTableAddress; import org.kaaproject.kaa.server.operations.service.event.RouteTableKey; import org.kaaproject.kaa.server.operations.service.event.UserRouteInfo; import org.kaaproject.kaa.server.sync.Event; import org.mockito.Mockito; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; public class UserActorMessageProcessorTest { private static final int ECF_ID2_VERSION = 3; private static final int ECF_ID1_VERSION = 1; private static final String ECF_ID2 = "ECF_ID2"; private static final String ECF_ID1 = "ECF_ID1"; private static final String SERVER2 = "SERVER2"; private static final String SERVER3 = "SERVER3"; private static final String APP_TOKEN = "APP_TOKEN"; private static final String TENANT_ID = "TENANT_ID"; private static final String USER_ID = "USER_ID"; private final EndpointObjectHash endpoint1Key = EndpointObjectHash.fromSha1("endpoint1"); private final EndpointObjectHash endpoint2Key = EndpointObjectHash.fromSha1("endpoint2"); private final EndpointObjectHash endpoint3Key = EndpointObjectHash.fromSha1("endpoint3"); private LocalUserActorMessageProcessor messageProcessor; private List<EventClassFamilyVersion> ecfVersions; private EventClassFamilyVersion ecfVersion1; private EventClassFamilyVersion ecfVersion2; private RouteTableAddress address1; private RouteTableAddress address2; private RouteTableAddress address3; private AkkaContext akkaContextMock; private CacheService cacheServiceMock; private EventService eventServiceMock; private ActorContext actorContextMock; private ActorRef originatorRefMock; @Before public void before() { cacheServiceMock = mock(CacheService.class); eventServiceMock = mock(EventService.class); originatorRefMock = mock(ActorRef.class); actorContextMock = mock(ActorContext.class); akkaContextMock = mock(AkkaContext.class); when(akkaContextMock.getCacheService()).thenReturn(cacheServiceMock); when(akkaContextMock.getEventService()).thenReturn(eventServiceMock); when(akkaContextMock.getEventTimeout()).thenReturn(60 * 1000L); messageProcessor = spy(new LocalUserActorMessageProcessor(akkaContextMock, USER_ID, TENANT_ID)); doReturn("dummyPathName").when(messageProcessor).getActorPathName(any(ActorRef.class)); Mockito.doNothing().when(messageProcessor).scheduleTimeoutMessage(Mockito.any(ActorContext.class), Mockito.any(EndpointEvent.class)); Mockito.doNothing().when(messageProcessor).sendEventToLocal(Mockito.any(ActorContext.class), Mockito.any(EndpointEventReceiveMessage.class)); ecfVersions = new ArrayList<>(); ecfVersion1 = new EventClassFamilyVersion(ECF_ID1, ECF_ID1_VERSION); ecfVersion2 = new EventClassFamilyVersion(ECF_ID2, ECF_ID2_VERSION); ecfVersions.add(ecfVersion1); ecfVersions.add(ecfVersion2); address1 = new RouteTableAddress(endpoint1Key, APP_TOKEN); address2 = new RouteTableAddress(endpoint2Key, APP_TOKEN, SERVER2); address3 = new RouteTableAddress(endpoint3Key, APP_TOKEN); } @Test public void testEndpointConnectFlow() { EndpointUserConnectMessage message1 = new EndpointUserConnectMessage(USER_ID, endpoint1Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message1); verify(eventServiceMock).sendUserRouteInfo(new UserRouteInfo(TENANT_ID, USER_ID)); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(any(RouteInfo.class), any(String.class)); RouteInfo routeInfo = new RouteInfo(TENANT_ID, USER_ID, address2, ecfVersions); RouteInfoMessage message2 = new RouteInfoMessage(routeInfo); messageProcessor.processRouteInfoMessage(actorContextMock, message2); RouteInfo localRouteInfo = new RouteInfo(TENANT_ID, USER_ID, address1, ecfVersions); verify(eventServiceMock).sendRouteInfo(Collections.singletonList(localRouteInfo), SERVER2); EndpointUserConnectMessage message3 = new EndpointUserConnectMessage(USER_ID, endpoint3Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message3); verify(eventServiceMock).sendRouteInfo(new RouteInfo(TENANT_ID, USER_ID, address3, ecfVersions), SERVER2); } @Test public void testEndpointLocalEvent() { EndpointUserConnectMessage message1 = new EndpointUserConnectMessage(USER_ID, endpoint1Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message1); EndpointUserConnectMessage message2 = new EndpointUserConnectMessage(USER_ID, endpoint2Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message2); verify(eventServiceMock).sendUserRouteInfo(new UserRouteInfo(TENANT_ID, USER_ID)); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(any(RouteInfo.class), any(String.class)); when(cacheServiceMock.getEventClassFamilyIdByEventClassFqn(new EventClassFqnKey(TENANT_ID, "testClassFqn"))).thenReturn(ECF_ID1); RouteTableKey routeKey = new RouteTableKey(APP_TOKEN, ecfVersion1); when(cacheServiceMock.getRouteKeys(new EventClassFqnVersion(TENANT_ID, "testClassFqn", ECF_ID1_VERSION))).thenReturn(Collections.singleton(routeKey)); Event event = new Event(0, "testClassFqn", ByteBuffer.wrap(new byte[0]), null, Base64Util.encode(endpoint1Key.getData())); EndpointEventSendMessage eventMessage = new EndpointEventSendMessage(USER_ID, Collections.singletonList(event), endpoint2Key, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointEventSendMessage(actorContextMock, eventMessage); verify(messageProcessor).sendEventToLocal(Mockito.any(ActorContext.class), Mockito.any(EndpointEventReceiveMessage.class)); } @Test public void testEndpointRemoteReceiveEvent() { EndpointUserConnectMessage message1 = new EndpointUserConnectMessage(USER_ID, endpoint1Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message1); RouteTableKey routeKey = new RouteTableKey(APP_TOKEN, ecfVersion1); when(cacheServiceMock.getRouteKeys(new EventClassFqnVersion(TENANT_ID, "testClassFqn", ECF_ID1_VERSION))).thenReturn(Collections.singleton(routeKey)); Event event = new Event(0, "testClassFqn", ByteBuffer.wrap(new byte[0]), null, Base64Util.encode(endpoint1Key.getData())); EndpointEvent endpointEvent = new EndpointEvent(endpoint2Key, event, UUID.randomUUID(), System.currentTimeMillis(), ECF_ID1_VERSION); RemoteEndpointEvent remoteEvent = new RemoteEndpointEvent(TENANT_ID, USER_ID, endpointEvent, new RouteTableAddress(endpoint1Key, APP_TOKEN, "SERVER1")); RemoteEndpointEventMessage message2 = new RemoteEndpointEventMessage(remoteEvent); messageProcessor.processRemoteEndpointEventMessage(actorContextMock, message2); verify(cacheServiceMock, Mockito.never()).getEventClassFamilyIdByEventClassFqn(new EventClassFqnKey(TENANT_ID, "testClassFqn")); verify(messageProcessor).sendEventToLocal(Mockito.any(ActorContext.class), Mockito.any(EndpointEventReceiveMessage.class)); } @Test public void testEndpointRemoteSendEvent() { EndpointUserConnectMessage message1 = new EndpointUserConnectMessage(USER_ID, endpoint1Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message1); verify(eventServiceMock).sendUserRouteInfo(new UserRouteInfo(TENANT_ID, USER_ID)); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(any(RouteInfo.class), any(String.class)); RouteInfo routeInfo = new RouteInfo(TENANT_ID, USER_ID, address2, ecfVersions); RouteInfoMessage message2 = new RouteInfoMessage(routeInfo); messageProcessor.processRouteInfoMessage(actorContextMock, message2); when(cacheServiceMock.getEventClassFamilyIdByEventClassFqn(new EventClassFqnKey(TENANT_ID, "testClassFqn"))).thenReturn(ECF_ID1); RouteTableKey routeKey = new RouteTableKey(APP_TOKEN, ecfVersion1); when(cacheServiceMock.getRouteKeys(new EventClassFqnVersion(TENANT_ID, "testClassFqn", ECF_ID1_VERSION))).thenReturn(Collections.singleton(routeKey)); Event event = new Event(0, "testClassFqn", ByteBuffer.wrap(new byte[0]), null, Base64Util.encode(endpoint2Key.getData())); EndpointEventSendMessage eventMessage = new EndpointEventSendMessage(USER_ID, Collections.singletonList(event), endpoint1Key, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointEventSendMessage(actorContextMock, eventMessage); verify(messageProcessor, Mockito.never()).sendEventToLocal(Mockito.any(ActorContext.class), Mockito.any(EndpointEventReceiveMessage.class)); verify(eventServiceMock).sendEvent(any(RemoteEndpointEvent.class)); } @Test public void testEndpointTimeoutMessage() throws NoSuchFieldException, SecurityException { EndpointUserConnectMessage message1 = new EndpointUserConnectMessage(USER_ID, endpoint1Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message1); when(cacheServiceMock.getEventClassFamilyIdByEventClassFqn(new EventClassFqnKey(TENANT_ID, "testClassFqn"))).thenReturn(ECF_ID1); RouteTableKey routeKey = new RouteTableKey(APP_TOKEN, ecfVersion1); when(cacheServiceMock.getRouteKeys(new EventClassFqnVersion(TENANT_ID, "testClassFqn", ECF_ID1_VERSION))).thenReturn(Collections.singleton(routeKey)); Event event = new Event(0, "testClassFqn", ByteBuffer.wrap(new byte[0]), null, null); EndpointEventSendMessage eventMessage = new EndpointEventSendMessage(USER_ID, Collections.singletonList(event), endpoint1Key, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointEventSendMessage(actorContextMock, eventMessage); EndpointUserConnectMessage message2 = new EndpointUserConnectMessage(USER_ID, endpoint2Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message2); //1 - means it is called once verify(messageProcessor, Mockito.times(1)).sendEventToLocal(Mockito.any(ActorContext.class), Mockito.any(EndpointEventReceiveMessage.class)); Field field = LocalUserActorMessageProcessor.class.getDeclaredField("eventStorage"); field.setAccessible(true); EventStorage storage = (EventStorage) ReflectionUtils.getField(field, messageProcessor); EndpointEventTimeoutMessage message = new EndpointEventTimeoutMessage(storage.getEvents(routeKey).iterator().next()); messageProcessor.processEndpointEventTimeoutMessage(actorContextMock, message); EndpointUserConnectMessage message3 = new EndpointUserConnectMessage(USER_ID, endpoint3Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message3); //still 1 - means it was not called after first call verify(messageProcessor, Mockito.times(1)).sendEventToLocal(Mockito.any(ActorContext.class), Mockito.any(EndpointEventReceiveMessage.class)); } @Test public void testUserRouteInfoAddFlow() { EndpointUserConnectMessage message1 = new EndpointUserConnectMessage(USER_ID, endpoint1Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message1); verify(eventServiceMock).sendUserRouteInfo(new UserRouteInfo(TENANT_ID, USER_ID)); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(any(RouteInfo.class), any(String.class)); UserRouteInfoMessage message2 = new UserRouteInfoMessage(new UserRouteInfo(TENANT_ID, USER_ID, SERVER2, RouteOperation.ADD)); messageProcessor.processUserRouteInfoMessage(actorContextMock, message2); RouteInfo localRouteInfo = new RouteInfo(TENANT_ID, USER_ID, address1, ecfVersions); verify(eventServiceMock, Mockito.times(1)).sendRouteInfo(Collections.singletonList(localRouteInfo), SERVER2); UserRouteInfoMessage message3 = new UserRouteInfoMessage(new UserRouteInfo(TENANT_ID, USER_ID, SERVER3, RouteOperation.ADD)); messageProcessor.processUserRouteInfoMessage(actorContextMock, message3); verify(eventServiceMock, Mockito.times(1)).sendRouteInfo(Collections.singletonList(localRouteInfo), SERVER3); verify(eventServiceMock, Mockito.times(1)).sendRouteInfo(Collections.singletonList(localRouteInfo), SERVER2); } @Test public void testUserRouteInfoRemoveFlow() { EndpointUserConnectMessage message1 = new EndpointUserConnectMessage(USER_ID, endpoint1Key, ecfVersions, 1, null, APP_TOKEN, originatorRefMock); messageProcessor.processEndpointConnectMessage(actorContextMock, message1); verify(eventServiceMock).sendUserRouteInfo(new UserRouteInfo(TENANT_ID, USER_ID)); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(any(RouteInfo.class), any(String.class)); UserRouteInfoMessage message2 = new UserRouteInfoMessage(new UserRouteInfo(TENANT_ID, USER_ID, SERVER2, RouteOperation.DELETE)); messageProcessor.processUserRouteInfoMessage(actorContextMock, message2); RouteInfo localRouteInfo = new RouteInfo(TENANT_ID, USER_ID, address1, ecfVersions); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(Collections.singletonList(localRouteInfo), SERVER2); UserRouteInfoMessage message3 = new UserRouteInfoMessage(new UserRouteInfo(TENANT_ID, USER_ID, SERVER3, RouteOperation.DELETE)); messageProcessor.processUserRouteInfoMessage(actorContextMock, message3); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(Collections.singletonList(localRouteInfo), SERVER3); verify(eventServiceMock, Mockito.times(0)).sendRouteInfo(Collections.singletonList(localRouteInfo), SERVER2); } }