/*
* 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.client.channel.failover;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Test;
import org.kaaproject.kaa.client.channel.KaaChannelManager;
import org.kaaproject.kaa.client.channel.ServerType;
import org.kaaproject.kaa.client.channel.TransportConnectionInfo;
import org.kaaproject.kaa.client.channel.failover.strategies.DefaultFailoverStrategy;
import org.kaaproject.kaa.client.channel.failover.strategies.FailoverStrategy;
import org.kaaproject.kaa.client.context.ExecutorContext;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class DefaultFailoverManagerTest {
private static final int RESOLUTION_TIMEOUT_MS = 500;
private static final int BOOTSTRAP_RETRY_PERIOD = 100;
private KaaChannelManager channelManager;
private Map<ServerType, DefaultFailoverManager.AccessPointIdResolution> resolutionProgressMap;
private DefaultFailoverManager failoverManager;
private ExecutorContext context;
@Before
public void setUp() {
channelManager = Mockito.mock(KaaChannelManager.class);
context = Mockito.mock(ExecutorContext.class);
Mockito.when(context.getScheduledExecutor()).thenReturn(Executors.newScheduledThreadPool(1));
FailoverStrategy failoverStrategy = new DefaultFailoverStrategy(BOOTSTRAP_RETRY_PERIOD, 1, 1, TimeUnit.MILLISECONDS);
failoverManager = new DefaultFailoverManager(channelManager, context, failoverStrategy, RESOLUTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
resolutionProgressMap = Mockito.spy(new HashMap<ServerType, DefaultFailoverManager.AccessPointIdResolution>());
ReflectionTestUtils.setField(failoverManager, "resolutionProgressMap", resolutionProgressMap);
}
@Test
public void onServerConnectedTest() {
TransportConnectionInfo transportConnectionInfo = mockForTransportConnectionInfo(1);
failoverManager.onServerConnected(transportConnectionInfo);
failoverManager.onServerChanged(transportConnectionInfo);
failoverManager.onServerFailed(transportConnectionInfo, FailoverStatus.NO_CONNECTIVITY);
DefaultFailoverManager.AccessPointIdResolution accessPointIdResolutionSpy = spyForResolutionMap(transportConnectionInfo);
failoverManager.onServerConnected(transportConnectionInfo);
Mockito.verify(accessPointIdResolutionSpy, Mockito.times(1)).setCurResolution(Mockito.any(Future.class));
}
@Test
public void onServerChangedTest() {
TransportConnectionInfo info = mockForTransportConnectionInfo(1);
failoverManager.onServerChanged(null);
Mockito.verify(resolutionProgressMap, Mockito.never()).put(Mockito.any(ServerType.class),
Mockito.any(DefaultFailoverManager.AccessPointIdResolution.class));
failoverManager.onServerChanged(info);
Mockito.verify(resolutionProgressMap, Mockito.times(1)).put(info.getServerType(),
new DefaultFailoverManager.AccessPointIdResolution(info.getAccessPointId(), null));
TransportConnectionInfo info2 = mockForTransportConnectionInfo(2);
failoverManager.onServerFailed(info, FailoverStatus.NO_CONNECTIVITY);
DefaultFailoverManager.AccessPointIdResolution accessPointIdResolutionSpy = spyForResolutionMap(info);
failoverManager.onServerChanged(info2);
Mockito.verify(accessPointIdResolutionSpy, Mockito.times(1)).setCurResolution(null);
Mockito.verify(resolutionProgressMap, Mockito.times(1)).put(info2.getServerType(),
new DefaultFailoverManager.AccessPointIdResolution(info2.getAccessPointId(), null));
}
@Test
public void onServerFailedTest() throws InterruptedException {
TransportConnectionInfo info = mockForTransportConnectionInfo(1);
failoverManager.onServerFailed(null, FailoverStatus.NO_CONNECTIVITY);
Mockito.verify(resolutionProgressMap, Mockito.never()).put(Mockito.any(ServerType.class),
Mockito.any(DefaultFailoverManager.AccessPointIdResolution.class));
failoverManager.onServerFailed(info, FailoverStatus.NO_CONNECTIVITY);
Mockito.verify(channelManager, Mockito.times(1)).onServerFailed(info, FailoverStatus.NO_CONNECTIVITY);
Mockito.verify(context, Mockito.times(1)).getScheduledExecutor();
ArgumentCaptor<DefaultFailoverManager.AccessPointIdResolution> argument =
ArgumentCaptor.forClass(DefaultFailoverManager.AccessPointIdResolution.class);
Mockito.verify(resolutionProgressMap, Mockito.times(1)).put(Mockito.eq(info.getServerType()), argument.capture());
assertEquals(argument.getValue().getAccessPointId(), info.getAccessPointId());
assertNotNull(argument.getValue().getCurResolution());
Mockito.verify(resolutionProgressMap, Mockito.timeout(RESOLUTION_TIMEOUT_MS * 2).times(1)).remove(info.getServerType());
TransportConnectionInfo info2 = mockForTransportConnectionInfo(2, ServerType.BOOTSTRAP);
failoverManager.onServerFailed(info2, FailoverStatus.NO_CONNECTIVITY);
final DefaultFailoverManager.AccessPointIdResolution accessPointIdResolutionSpy = spyForResolutionMap(info2);
failoverManager.onServerFailed(info2, FailoverStatus.NO_CONNECTIVITY);
Mockito.verify(accessPointIdResolutionSpy, Mockito.never()).setCurResolution(null);
info2 = mockForTransportConnectionInfo(3, ServerType.BOOTSTRAP);
failoverManager.onServerFailed(info2, FailoverStatus.NO_CONNECTIVITY);
Mockito.verify(channelManager, Mockito.times(1)).onServerFailed(info2, FailoverStatus.NO_CONNECTIVITY);
Mockito.reset(accessPointIdResolutionSpy);
failoverManager.onFailover(FailoverStatus.BOOTSTRAP_SERVERS_NA);
failoverManager.onServerFailed(info2, FailoverStatus.NO_CONNECTIVITY);
Mockito.verify(accessPointIdResolutionSpy, Mockito.never()).setCurResolution(null);
Thread.sleep(BOOTSTRAP_RETRY_PERIOD * 2);
failoverManager.onServerFailed(info2, FailoverStatus.NO_CONNECTIVITY);
new Thread(new Runnable() {
@Override
public void run() {
Mockito.verify(accessPointIdResolutionSpy, Mockito.timeout(BOOTSTRAP_RETRY_PERIOD * 2).times(1)).setCurResolution(null);
}
}).start();
}
private TransportConnectionInfo mockForTransportConnectionInfo(int accessPointId) {
return mocksForTransportConnectionInfo(ServerType.OPERATIONS, accessPointId)[0];
}
private TransportConnectionInfo mockForTransportConnectionInfo(int accessPointId, ServerType serverType) {
return mocksForTransportConnectionInfo(serverType, accessPointId)[0];
}
private TransportConnectionInfo[] mocksForTransportConnectionInfo(ServerType serverType, int... accessPointIds) {
TransportConnectionInfo[] transportConnections = new TransportConnectionInfo[accessPointIds.length];
for (int i = 0; i < accessPointIds.length; i++) {
TransportConnectionInfo connectionInfo = Mockito.mock(TransportConnectionInfo.class);
Mockito.when(connectionInfo.getAccessPointId()).thenReturn(accessPointIds[i]);
Mockito.when(connectionInfo.getServerType()).thenReturn(serverType);
transportConnections[i] = connectionInfo;
}
return transportConnections;
}
private DefaultFailoverManager.AccessPointIdResolution spyForResolutionMap(TransportConnectionInfo info) {
DefaultFailoverManager.AccessPointIdResolution simplePointIdResolution = resolutionProgressMap.get(info.getServerType());
DefaultFailoverManager.AccessPointIdResolution spyPointIdResolution = Mockito.spy(simplePointIdResolution);
resolutionProgressMap.put(info.getServerType(), spyPointIdResolution);
return spyPointIdResolution;
}
}