package com.workshare.msnos.usvc.api.routing; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.List; import java.util.UUID; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import com.workshare.msnos.core.geo.Location; import com.workshare.msnos.usvc.Microservice; import com.workshare.msnos.usvc.RemoteMicroservice; import com.workshare.msnos.usvc.api.RestApi; import com.workshare.msnos.usvc.api.routing.strategies.CachingRoutingStrategy; import com.workshare.msnos.usvc.api.routing.strategies.PriorityRoutingStrategy; @SuppressWarnings({"unchecked", "rawtypes"}) public class ApiListTest { private ApiList apiList; private Microservice svc; private RoutingStrategy routing = ApiList.defaultRoutingStrategy(); @BeforeClass public static void disableCaching() { System.setProperty(CachingRoutingStrategy.SYSP_TIMEOUT, "0"); } @Before public void preparation() { disablePriorityStrategy(); this.apiList = null; this.svc = Mockito.mock(Microservice.class); when(svc.getLocation()).thenReturn(Location.UNKNOWN); } @After public void tearDown() throws Exception { disablePriorityStrategy(); } private void disablePriorityStrategy() { System.clearProperty(PriorityRoutingStrategy.SYSP_PRIORITY_ENABLED); System.clearProperty(PriorityRoutingStrategy.SYSP_PRIORITY_DEFAULT_LEVEL); } @Test public void shouldStoreOneApi() { RestApi rest = newRestApi("alfa"); RemoteMicroservice remote = newRemoteMicroservice(); apiList().add(remote, rest); assertEquals(rest, apiList().get(svc)); assertEquals(rest, apiList().get(svc)); } @Test public void shouldNotReturnFaultyApis() { RestApi rest = newRestApi("alfa"); RemoteMicroservice remote = newRemoteMicroservice(); apiList().add(remote, rest); markAsFaulty(rest); assertNull(apiList().get(svc)); } @Test public void shouldSelectApisUsingRoundRobin() { RestApi alfa = newRestApi("alfa"); RestApi beta = newRestApi("beta"); RemoteMicroservice remote = newRemoteMicroservice(); apiList().add(remote, alfa); apiList().add(remote, beta); assertEquals(alfa, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); assertEquals(alfa, apiList().get(svc)); } @Test public void shouldSelectApisUsingRoundRobinButSkippingFailingRemoteServices() { RestApi oneAlfa = newRestApi("one.alfa"); RestApi oneBeta = newRestApi("one,beta"); RestApi twoAlfa = newRestApi("two.alfa"); RemoteMicroservice one = newRemoteMicroservice(); RemoteMicroservice two = newRemoteMicroservice(); apiList().add(one, oneAlfa); apiList().add(one, oneBeta); apiList().add(two, twoAlfa); markAsFaulty(oneAlfa); assertEquals(twoAlfa, apiList().get(svc)); } @Test public void shouldHonourAffinity() { RestApi alfa = newRestApi("alfa"); RestApi beta = newRestApiWithAffinity("beta"); RemoteMicroservice remote = newRemoteMicroservice(); apiList().add(remote, alfa); apiList().add(remote, beta); assertEquals(alfa, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); } @Test public void shouldRemoveAffinityWhenMicroserviceRemoved() { RemoteMicroservice alfaMicro = newRemoteMicroservice(); RemoteMicroservice betaMicro = newRemoteMicroservice(); RestApi alfa = newRestApiWithAffinity("alfa"); RestApi beta = newRestApi("beta"); apiList().add(alfaMicro, alfa); apiList().add(betaMicro, beta); assertEquals(alfa, apiList().get(svc)); assertEquals(alfa, apiList().get(svc)); apiList().remove(alfaMicro); assertEquals(beta, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); } @Test public void shouldMarkApiFaultyWhenMicroserviceRemoved_RequiredForCachingRoutingStrategy() { RemoteMicroservice micro = newRemoteMicroservice(); RestApi alfa = newRestApi("alfa"); apiList().add(micro, alfa); apiList().remove(micro); verify(alfa).markFaulty(); } @Test public void shouldRemoveAffinityWhenFaulty() { RestApi alfa = newRestApi("alfa"); RestApi beta = newRestApiWithAffinity("beta"); RestApi thre = newRestApi("thre"); apiList().add(newRemoteMicroservice(), alfa); apiList().add(newRemoteMicroservice(), beta); apiList().add(newRemoteMicroservice(), thre); markAsFaulty(beta); assertEquals(alfa, apiList().get(svc)); assertEquals(thre, apiList().get(svc)); assertEquals(alfa, apiList().get(svc)); } @Test public void shouldMapNewAffinityWhenOriginalFaulty() { RestApi alfa = newRestApiWithAffinity("alfa"); RestApi beta = newRestApiWithAffinity("beta"); RestApi thre = newRestApi("thre"); apiList().add(newRemoteMicroservice(), alfa); apiList().add(newRemoteMicroservice(), beta); apiList().add(newRemoteMicroservice(), thre); markAsFaulty(alfa); assertEquals(beta, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); } @Test public void shouldNOTRemapToNewlyWorkingAffinityAfterFaulty() { RestApi alfa = newRestApiWithAffinity("alfa"); RestApi beta = newRestApiWithAffinity("beta"); RestApi thre = newRestApi("thre"); apiList().add(newRemoteMicroservice(), alfa); apiList().add(newRemoteMicroservice(), beta); apiList().add(newRemoteMicroservice(), thre); markAsFaulty(alfa); assertEquals(beta, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); markAsWorking(alfa); assertEquals(beta, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); } @Test public void shouldAddLocationToApiEndpoint() { Location location = mock(Location.class); final RemoteMicroservice micro = newRemoteMicroservice(); when(micro.getLocation()).thenReturn(location); apiList().add(micro, newRestApiWithHost("alfa", "1.1.1.1")); ApiEndpoint ep = apiList().getEndpoints().get(0); assertEquals(location, ep.location()); } @Test public void shouldInvokeUnderlyingStrategy() { routing = mock(RoutingStrategy.class); final RestApi alfa = newRestApi("alfa"); apiList().add(newRemoteMicroservice(), alfa); when(routing.select(svc, apiList.getEndpoints())).thenReturn(apiList.getEndpoints()); apiList().get(svc); ArgumentCaptor<List> apis = ArgumentCaptor.forClass(List.class); verify(routing).select(eq(svc), apis.capture()); assertEquals(1, apis.getValue().size()); assertEquals(((ApiEndpoint) apis.getValue().get(0)).api(), alfa); } @Test public void shouldInvokePriorityStrategyWhenHighPriorityEngaged() throws Exception { System.setProperty(PriorityRoutingStrategy.SYSP_PRIORITY_ENABLED, "true"); routing = ApiList.defaultRoutingStrategy(); final RestApi alfa = newRestApiWithHighPriority("alfa"); final RestApi beta = newRestApi("beta"); apiList().add(newRemoteMicroservice(), alfa); apiList().add(newRemoteMicroservice(), beta); assertEquals(alfa, apiList().get(svc)); assertEquals(alfa, apiList().get(svc)); assertEquals(alfa, apiList().get(svc)); } @Test public void shouldNOTInvokePriorityStrategyWhenHighPriorityNOTEngaged() throws Exception { final RestApi alfa = newRestApiWithHighPriority("alfa"); final RestApi beta = newRestApi("beta"); apiList().add(newRemoteMicroservice(), alfa); apiList().add(newRemoteMicroservice(), beta); assertEquals(alfa, apiList().get(svc)); assertEquals(beta, apiList().get(svc)); assertEquals(alfa, apiList().get(svc)); } private RemoteMicroservice newRemoteMicroservice() { final RemoteMicroservice micro = Mockito.mock(RemoteMicroservice.class); Mockito.when(micro.getName()).thenReturn("usvc"); Mockito.when(micro.getLocation()).thenReturn(Location.UNKNOWN); Mockito.when(micro.getUuid()).thenReturn(UUID.randomUUID()); return micro; } private void markAsWorking(RestApi api) { Mockito.when(api.isFaulty()).thenReturn(false); } private void markAsFaulty(RestApi api) { Mockito.when(api.isFaulty()).thenReturn(true); } private RestApi newRestApi(String name) { RestApi api = Mockito.mock(RestApi.class); Mockito.when(api.toString()).thenReturn(name); Mockito.when(api.getHost()).thenReturn("127.0.0.1"); return api; } private RestApi newRestApiWithHighPriority(String name) { RestApi api = newRestApi(name); Mockito.when(api.getPriority()).thenReturn(5); return api; } private RestApi newRestApiWithAffinity(String name) { RestApi api = newRestApi(name); Mockito.when(api.hasAffinity()).thenReturn(true); return api; } private RestApi newRestApiWithHost(String name, String host) { RestApi api = newRestApi(name); Mockito.when(api.getHost()).thenReturn(host); return api; } private ApiList apiList() { if (apiList == null) apiList = new ApiList(routing); return apiList; } }