package com.workshare.msnos.core;
import static com.workshare.msnos.core.CoreHelper.asSet;
import static com.workshare.msnos.core.CoreHelper.fakeSystemTime;
import static com.workshare.msnos.core.Message.Type.PIN;
import static com.workshare.msnos.core.Message.Type.PON;
import static com.workshare.msnos.core.Message.Type.PRS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
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.when;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.workshare.msnos.core.payloads.Presence;
import com.workshare.msnos.core.protocols.ip.Endpoint;
import com.workshare.msnos.soup.time.SystemTime;
@RunWith(PowerMockRunner.class)
@PrepareForTest(Gateways.class)
public class LocalAgentTest {
private Cloud cloud;
private LocalAgent karl;
private LocalAgent smith;
@Before
public void before() throws Exception {
PowerMockito.mockStatic(Gateways.class);
when(Gateways.allPublicEndpoints()).thenReturn(Collections.<Endpoint>emptySet());
when(Gateways.allEndpoints()).thenReturn(Collections.<Endpoint>emptySet());
System.setProperty("public.ip", "132.1.0.2");
cloud = mock(Cloud.class);
when(cloud.getIden()).thenReturn(new Iden(Iden.Type.CLD, UUID.randomUUID()));
karl = new LocalAgent(UUID.randomUUID());
karl.join(cloud);
smith = new LocalAgent(UUID.randomUUID());
smith.join(cloud);
}
@After
public void after() throws Exception {
SystemTime.reset();
}
@Test(expected = MsnosException.class)
public void shouldNotBeAllowedToJoinTwice() throws MsnosException {
smith.join(cloud);
}
@Test
public void shouldMantainCloudReferenceOnJoin() throws MsnosException {
assertEquals(cloud, smith.getCloud());
}
@Test
public void shouldReleaseCloudReferenceOnLeave() throws MsnosException {
smith.leave();
assertNull(smith.getCloud());
}
@Test
public void agentShouldAttachListenerToCloud() {
verify(cloud, atLeastOnce()).addListener(any(Cloud.Listener.class));
}
@Test
public void shouldSendPresenceWhenDiscoveryIsReceived() throws IOException {
Message discovery = new MessageBuilder(Message.Type.DSC, cloud, smith).make();
simulateMessageFromCloud(discovery);
Message message = getLastMessageToCloud();
assertNotNull(message);
assertEquals(smith.getIden(), message.getFrom());
assertEquals(PRS, message.getType());
}
@Test
public void shouldSendPongWhenPingIsReceived() throws IOException {
simulateMessageFromCloud(new MessageBuilder(Message.Type.PIN, cloud, smith).make());
Message message = getLastMessageToCloud();
assertNotNull(message);
assertEquals(smith.getIden(), message.getFrom());
assertEquals(PON, message.getType());
}
@Test
public void shouldSendUnreliableMessageThroughCloud() throws Exception {
smith.send(new MessageBuilder(Message.Type.PIN, smith, karl).make());
Message message = getLastMessageToCloud();
assertNotNull(message);
assertEquals(smith.getIden(), message.getFrom());
assertEquals(karl.getIden(), message.getTo());
assertEquals(PIN, message.getType());
assertEquals(false, message.isReliable());
}
@Test
public void shouldSendReliableMessageThroughCloud() throws Exception {
smith.send(new MessageBuilder(Message.Type.PIN, smith, karl).reliable(true).make());
Message message = getLastMessageToCloud();
assertNotNull(message);
assertEquals(smith.getIden(), message.getFrom());
assertEquals(karl.getIden(), message.getTo());
assertEquals(PIN, message.getType());
assertEquals(true, message.isReliable());
}
@Test
public void presenceMessageShouldContainNetworkInfo() throws Exception {
smith.send(new MessageBuilder(Message.Type.PRS, smith, cloud).with(Presence.on(smith)).make());
Message message = getLastMessageToCloud();
assertNotNull(message);
assertEquals(smith.getIden(), message.getFrom());
assertEquals(PRS, message.getType());
assertNotNull(((Presence) message.getData()).getEndpoints());
assertEquals(((Presence) message.getData()).getEndpoints(), smith.getEndpoints());
}
@Test
public void localAgentLastAccessTimeShouldAlwaysBeNow() throws Exception {
fakeSystemTime(123456L);
LocalAgent jeff = new LocalAgent(UUID.randomUUID());
jeff.join(cloud);
assertEquals(jeff.getAccessTime(), SystemTime.asMillis());
}
@Test
public void shouldUpdateAccessTimeWhenMessageIsReceived() {
fakeSystemTime(123456790L);
Message message = new MessageBuilder(Message.Type.PIN, cloud.getIden(), smith.getIden()).make();
simulateMessageFromCloud(message);
assertEquals(123456790L, smith.getAccessTime());
}
@Test
public void otherAgentsShouldNOTStillSeeAgentOnLeave() throws Exception {
smith.leave();
assertFalse(karl.getCloud().getLocalAgents().contains(smith));
}
@Test
public void agentShouldStoreEndpointsInformationAfterJoin() throws Exception {
final Set<Endpoint> expected = asSet(mock(Endpoint.class));
when(Gateways.allPublicEndpoints()).thenReturn(expected);
smith = new LocalAgent(UUID.randomUUID());
smith.join(cloud);
assertEquals(expected, smith.getEndpoints());
}
@Test
public void shouldCreateTimestampOnCreation() throws Exception {
fakeSystemTime(123456L);
smith.send(new MessageBuilder(Message.Type.PIN, smith, cloud).make());
Message toCloud = getLastMessageToCloud();
assertEquals(123456L, toCloud.getWhen());
}
private Message getLastMessageToCloud() throws IOException {
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
verify(cloud, atLeastOnce()).send(captor.capture());
return captor.getValue();
}
private void simulateMessageFromCloud(final Message message) {
ArgumentCaptor<Cloud.Listener> cloudListener = ArgumentCaptor.forClass(Cloud.Listener.class);
verify(cloud, atLeastOnce()).addListener(cloudListener.capture());
cloudListener.getValue().onMessage(message);
}
}