package com.workshare.msnos.usvc; import static com.workshare.msnos.core.CoreHelper.fakeSystemTime; import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.*; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.ScheduledExecutorService; 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.Agent; import com.workshare.msnos.core.Cloud; import com.workshare.msnos.core.Gateway; import com.workshare.msnos.core.Iden; import com.workshare.msnos.core.Identifiable; import com.workshare.msnos.core.Message; import com.workshare.msnos.core.MessageBuilder; import com.workshare.msnos.core.MockMessageHelper; import com.workshare.msnos.core.Receipt; import com.workshare.msnos.core.RemoteAgent; import com.workshare.msnos.core.RemoteEntity; import com.workshare.msnos.core.Ring; import com.workshare.msnos.core.payloads.FltPayload; import com.workshare.msnos.core.payloads.QnePayload; import com.workshare.msnos.core.protocols.ip.BaseEndpoint; import com.workshare.msnos.core.protocols.ip.Endpoint; import com.workshare.msnos.core.protocols.ip.Endpoints; import com.workshare.msnos.core.protocols.ip.Endpoint.Type; import com.workshare.msnos.core.protocols.ip.Network; import com.workshare.msnos.soup.time.SystemTime; 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("unused") public class MicroserviceTest { private static final long CURRENT_TIME = 12345L; private Cloud cloud; private Microcloud microcloud; private Microservice localMicroservice; @BeforeClass public static void disableCaching() { System.setProperty(CachingRoutingStrategy.SYSP_TIMEOUT, "0"); } @Before public void prepare() throws Exception { cloud = Mockito.mock(Cloud.class); when(cloud.getIden()).thenReturn(new Iden(Iden.Type.CLD, new UUID(111, 111))); Ring ring = mock(Ring.class); when(cloud.getRing()).thenReturn(ring); microcloud = newMicrocloud(cloud); localMicroservice = new Microservice("fluffy"); localMicroservice.join(microcloud); fakeSystemTime(CURRENT_TIME); } @After public void after() { System.setProperty(PriorityRoutingStrategy.SYSP_PRIORITY_ENABLED, "true"); SystemTime.reset(); } @Test public void shouldInternalAgentJoinTheCloudOnJoin() throws Exception { localMicroservice = new Microservice("jeff"); cloud = new Cloud(UUID.randomUUID(), " ", mockGateways()); localMicroservice.join(newMicrocloud(cloud)); assertEquals(localMicroservice.getAgent(), cloud.getLocalAgents().iterator().next()); } @Test public void shouldInternalAgentLeaveTheCloudOnLeave() throws Exception { localMicroservice = new Microservice("jeff"); cloud = new Cloud(UUID.randomUUID(), " ", mockGateways()); final Microcloud ucloud = newMicrocloud(cloud); localMicroservice.join(ucloud); localMicroservice.leave(); assertEquals(0, cloud.getLocalAgents().size()); } @Test public void shouldSendQNEwhenPublishApi() throws Exception { RestApi api = new RestApi("/foo", 8080); localMicroservice.publish(api); Message msg = getLastMessageSent(); assertEquals(Message.Type.QNE, msg.getType()); assertEquals(cloud.getIden(), msg.getTo()); Set<RestApi> apis = ((QnePayload) msg.getData()).getApis(); assertTrue(api.equals(apis.iterator().next())); } @Test public void shouldSendENQonJoin() throws Exception { Message msg = getLastMessageSent(); assertEquals(Message.Type.ENQ, msg.getType()); assertEquals(cloud.getIden(), msg.getTo()); } @Test public void shouldSendQNEOnEnquiry() throws Exception { RemoteMicroservice remoteMicroservice = setupRemoteMicroservice("remote", "/endpoint"); simulateMessageFromCloud(newENQMessage(remoteMicroservice.getAgent(), localMicroservice.getAgent())); Message msg = getLastMessageSent(); assertEquals(Message.Type.QNE, msg.getType()); assertEquals(cloud.getIden(), msg.getTo()); } @Test public void shouldNotProcessMessagesFromSelf() throws Exception { simulateMessageFromCloud(newENQMessage(localMicroservice.getAgent(), localMicroservice.getAgent())); Message msg = getLastMessageSent(); assertEquals(Message.Type.ENQ, msg.getType()); } @Test public void shouldMarkWorkingTouchTheUnderlyingAgent() throws Exception { RemoteMicroservice service = setupRemoteMicroservice("content", "/files"); final long now = 987654L; fakeSystemTime(now); service.markWorking(); assertEquals(now, service.getAgent().getAccessTime()); } @Test public void shouldPublishRestApisWithHighPriorityWhenSet() throws Exception { System.setProperty(PriorityRoutingStrategy.SYSP_PRIORITY_DEFAULT_LEVEL, "5"); localMicroservice.publish(new RestApi("path", 9999)); assertEquals(5, getLastPublishedRestApi().getPriority()); } @Test public void shouldMantainCopyOfPublishedApis() throws Exception { final RestApi api = new RestApi("path", 9999); localMicroservice.publish(api); assertEquals(1, localMicroservice.getApis().size()); assertEquals(api, localMicroservice.getApis().iterator().next()); } @Test public void shouldNotBoomWhenLeavingCloduWhenNotjoinedBefore() throws Exception { Mockito.reset(cloud); Microservice ms = new Microservice("temp"); ms.leave(); verifyZeroInteractions(cloud); } @Test public void shouldNotBoomWhenLeavingCloduTwice() throws Exception { localMicroservice.leave(); Mockito.reset(cloud); localMicroservice.leave(); verifyZeroInteractions(cloud); } @Test public void shouldNotifyRing() { verify(localMicroservice.getAgent().getRing()).onMicroserviceJoin(localMicroservice); } private RestApi getLastPublishedRestApi() throws IOException { return ((QnePayload) getLastMessageSent().getData()).getApis().iterator().next(); } private RestApi createRestApi(String name, String endpoint) { return new RestApi(endpoint, 9999).onHost("24.24.24.24"); } private Set<RestApi> getApis(RemoteMicroservice remoteMicroservice) { return microcloud.getMicroServices().get(microcloud.getMicroServices().indexOf(remoteMicroservice)).getApis(); } private RestApi[] getRestApis(RemoteMicroservice ms1) { Set<RestApi> apiSet = ms1.getApis(); return apiSet.toArray(new RestApi[apiSet.size()]); } private void putRemoteAgentInCloudAgentsList(final RemoteAgent agent) { when(cloud.getRemoteAgents()).thenReturn(new HashSet<RemoteAgent>(Arrays.asList(agent))); when(cloud.find(agent.getIden())).thenReturn(agent); } private RestApi getRestApi() { RemoteMicroservice remote = microcloud.getMicroServices().get(0); Set<RestApi> apis = remote.getApis(); return apis.iterator().next(); } private void assertAgentInMicroserviceList(Agent remoteAgent) { assertEquals(remoteAgent, microcloud.getMicroServices().iterator().next().getAgent()); } private RemoteMicroservice setupRemoteMicroservice(String name, String endpoint) throws IOException { return setupRemoteMicroservice("10.10.10.10", name, endpoint); } private RemoteMicroservice setupRemoteMicroserviceWithSessionAffinity(String host, String name, String endpoint) throws Exception { RemoteAgent agent = newRemoteAgent(); RestApi restApi = new RestApi(endpoint, 9999).onHost(host).withAffinity(); RemoteMicroservice remote = new RemoteMicroservice(name, agent, toSet(restApi)); return addRemoteAgentToCloudListAndMicroserviceToLocalList(name, remote, restApi); } private RemoteMicroservice setupRemoteMicroservice(String host, String name, String endpoint) { return setupRemoteMicroservice(host, name, endpoint, 9999); } private RemoteMicroservice setupRemoteMicroservice(String host, String name, String endpoint, int port) { RemoteAgent agent = newRemoteAgent(); RestApi restApi = new RestApi(endpoint, port).onHost(host); RemoteMicroservice remote = new RemoteMicroservice(name, agent, toSet(restApi)); return addRemoteAgentToCloudListAndMicroserviceToLocalList(name, remote, restApi); } private RemoteMicroservice setupRemoteMicroserviceWithAgentUUIDAndRestApi(String host, String name, String endpoint, UUID uuid, RestApi restApi) { RemoteAgent agent = newRemoteAgentWithUUID(uuid); RemoteMicroservice remote = new RemoteMicroservice(name, agent, toSet(restApi)); return addRemoteAgentToCloudListAndMicroserviceToLocalList(name, remote, restApi); } private RemoteMicroservice setupRemoteMicroserviceWithMultipleRestAPIs(String host1, String host2, String name, String endpoint) throws IOException { RemoteAgent agent = newRemoteAgent(); RestApi alfa = new RestApi(endpoint, 9999).onHost(host1); RestApi beta = new RestApi(endpoint, 9999).onHost(host2); RemoteMicroservice remote = new RemoteMicroservice(name, agent, toSet(alfa, beta)); return addRemoteAgentToCloudListAndMicroserviceToLocalList(name, remote, alfa, beta); } private RemoteMicroservice addRemoteAgentToCloudListAndMicroserviceToLocalList(String name, RemoteMicroservice remote, RestApi... restApi) { putRemoteAgentInCloudAgentsList(remote.getAgent()); simulateMessageFromCloud(newQNEMessage(remote.getAgent(), name, restApi)); return remote; } private Set<RestApi> toSet(RestApi... restApi) { return new HashSet<RestApi>(Arrays.asList(restApi)); } private Message newENQMessage(Identifiable from, Identifiable to) { return new MockMessageHelper(Message.Type.ENQ, from.getIden(), to.getIden()).make(); } private Message newQNEMessage(RemoteEntity from, String name, RestApi... apis) { return new MockMessageHelper(Message.Type.QNE, from.getIden(), cloud.getIden()).data(new QnePayload("content", apis)).make(); } private Message newFaultMessage(Agent agent) { return new MessageBuilder(Message.Type.FLT, cloud, cloud).with(new FltPayload(agent.getIden())).make(); } private Message getLastMessageSent() throws IOException { ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); verify(cloud, atLeastOnce()).send(captor.capture()); return captor.getValue(); } private Message simulateMessageFromCloud(final Message message) { ArgumentCaptor<Cloud.Listener> cloudListener = ArgumentCaptor.forClass(Cloud.Listener.class); verify(cloud, atLeastOnce()).addListener(cloudListener.capture()); cloudListener.getValue().onMessage(message); return message; } private RemoteEntity newRemoteAgentWithFakeHosts(String address, short suffix) throws Exception { List<String> tokens = Arrays.asList(address.split("\\.")); if (tokens.size() > 4) throw new Exception("Split too large, not correct network address"); byte[] nibbles = new byte[tokens.size()]; for (int i = 0; i < nibbles.length; i++) { nibbles[i] = Byte.valueOf(tokens.get(i)); } return newRemoteAgent(UUID.randomUUID(), new BaseEndpoint(Type.UDP, new Network(nibbles, suffix))); } private RemoteAgent newRemoteAgent() { return newRemoteAgent(UUID.randomUUID()); } private RemoteAgent newRemoteAgentWithUUID(UUID uuid) { return newRemoteAgent(uuid); } private RemoteAgent newRemoteAgent(final UUID uuid, BaseEndpoint... endpoints) { RemoteAgent remote = new RemoteAgent(uuid, cloud, new HashSet<Endpoint>(Arrays.asList(endpoints))); putRemoteAgentInCloudAgentsList(remote); return remote; } private Microcloud newMicrocloud(Cloud cloud) { return new Microcloud(cloud, mock(ScheduledExecutorService.class)); } private Set<Gateway> mockGateways() throws IOException { Endpoints epoints = mock(Endpoints.class); Gateway gate = mock(Gateway.class); when(gate.endpoints()).thenReturn(epoints); Receipt receipt = mock(Receipt.class); when(gate.send(any(Cloud.class), any(Message.class), any(Identifiable.class))).thenReturn(receipt); return new HashSet<Gateway>(Arrays.asList(new Gateway[]{gate})); } }