package com.nightscout.core.mqtt; import com.nightscout.core.events.EventReporter; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.MqttSecurityException; import org.junit.Before; import org.junit.Test; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class MqttEventMgrTest { private MqttClient mockClient; private MqttPinger mockPinger; private MqttTimer mockTimer; private MqttConnectOptions options; private MqttEventMgr manager; private EventReporter mockReporter; @Before public void setup() { options = new MqttConnectOptions(); options.setUserName("test"); options.setPassword("pass".toCharArray()); options.setKeepAliveInterval(Constants.KEEPALIVE_INTERVAL); options.setCleanSession(Constants.MQTT_CLEAN_SESSION); mockClient = mock(MqttClient.class); mockPinger = mock(MqttPinger.class); mockTimer = mock(MqttTimer.class); mockReporter = mock(EventReporter.class); manager = new MqttEventMgr(mockClient, options, mockPinger, mockTimer, mockReporter); } private void setupConnectWithSecurityException() throws Exception { doThrow(new MqttSecurityException(1)).when(mockClient).connect((MqttConnectOptions) anyObject()); } @Test public void connectWithSecurityExceptionShouldNotReconnectDelayed() throws Exception { setupConnectWithSecurityException(); manager.connect(); verify(mockTimer, never()).setTimer(anyInt()); } @Test public void connectWithMqttExceptionShouldReconnectDelayed() throws Exception { doThrow(new MqttException(1)).when(mockClient).connect((MqttConnectOptions) anyObject()); manager.setShouldReconnect(true); manager.connect(); verify(mockTimer).setTimer(anyInt()); } @Test public void connectWithSecurityExceptionShouldStopPingerIfActive() throws Exception { setupConnectWithSecurityException(); when(mockPinger.isActive()).thenReturn(true); manager.connect(); verify(mockPinger, times(1)).stop(); } @Test public void connectWithSecurityExceptionShouldDeactivateTimerIfActive() throws Exception { setupConnectWithSecurityException(); when(mockTimer.isActive()).thenReturn(true); manager.connect(); verify(mockTimer, times(1)).deactivate(); } @Test public void connectWithInactivePingerShouldActivatePinger() throws Exception { when(mockPinger.isActive()).thenReturn(false); manager.connect(); verify(mockPinger, times(1)).start(); } @Test public void reconnectDelayedWithInactiveTimerShouldActivateTimer() throws Exception { when(mockTimer.isActive()).thenReturn(false); manager.setShouldReconnect(true); manager.delayedReconnect(); verify(mockTimer, times(1)).activate(); } @Test public void reconnectWithInActiveNetworkShouldNotReconnect() throws Exception { when(mockPinger.isNetworkActive()).thenReturn(false); manager.reconnect(); verify(mockClient, never()).close(); verify(mockClient, never()).connect(); } @Test public void reconnectInConnectedStateWithActiveNetworkShouldReconnect() throws Exception { when(mockPinger.isNetworkActive()).thenReturn(true); when(mockClient.isConnected()).thenReturn(true); manager.reconnect(); verify(mockClient, times(1)).disconnect(); verify(mockClient, times(1)).connect((MqttConnectOptions) anyObject()); } @Test public void disconnectWhileNotConnectedShouldDeactivateTimer() { when(mockClient.isConnected()).thenReturn(false); manager.disconnect(); verify(mockTimer, times(1)).deactivate(); } @Test public void disconnectWhileNotConnectedShouldStopPinger() { when(mockClient.isConnected()).thenReturn(false); manager.disconnect(); verify(mockPinger, times(1)).stop(); } @Test public void disconnectWhileNotConnectedShouldNotDisconnect() throws Exception { when(mockClient.isConnected()).thenReturn(false); manager.disconnect(); verify(mockClient, never()).disconnect(); } @Test public void disconnectWhileConnectedShouldDisconnect() throws Exception { when(mockClient.isConnected()).thenReturn(true); manager.disconnect(); verify(mockClient, times(1)).disconnect(); } @Test public void closeWhileClientActiveShouldDisconnect() throws Exception { when(mockClient.isConnected()).thenReturn(true); manager.close(); verify(mockClient, times(1)).disconnect(); } @Test public void regiseringTwiceShouldNotAddToObserversTwice() throws Exception { MqttMgrObserver observer = mock(MqttMgrObserver.class); manager.registerObserver(observer); manager.registerObserver(observer); assertThat(manager.getNumberOfObservers(), is(1)); } @Test public void unregisteringWithSingleRegisterShouldRemoveAllObservers() throws Exception { MqttMgrObserver observer = mock(MqttMgrObserver.class); manager.registerObserver(observer); manager.unregisterObserver(observer); assertThat(manager.getNumberOfObservers(), is(0)); } @Test public void unregisteringWithMultipleRegisterShouldRemoveOneObservers() throws Exception { MqttMgrObserver observer1 = mock(MqttMgrObserver.class); MqttMgrObserver observer2 = mock(MqttMgrObserver.class); manager.registerObserver(observer1); manager.registerObserver(observer2); manager.unregisterObserver(observer1); assertThat(manager.getNumberOfObservers(), is(1)); } // Moved some of the reconnect logic out from lostConnection to prevent double reconnects. // May readd it later. @Test public void lostConnectionShouldNotifyObserver() { MqttMgrObserver observer = mock(MqttMgrObserver.class); manager.registerObserver(observer); manager.connectionLost(new Throwable("Some random throwable")); verify(observer, times(1)).onDisconnect(); } // Moved some of the reconnect logic out from lostConnection to prevent double reconnects. // May readd it later. @Test public void lostConnectionShouldSetTimerToReconnect() { MqttMgrObserver observer = mock(MqttMgrObserver.class); manager.setShouldReconnect(true); manager.connectionLost(new Throwable("Some random throwable")); verify(mockTimer).setTimer(anyInt()); } @Test public void onMessageShouldNotifyObservers() { MqttMgrObserver observer = mock(MqttMgrObserver.class); manager.registerObserver(observer); byte[] message = "my message".getBytes(); manager.messageArrived("/topic", new MqttMessage(message)); verify(observer).onMessage(anyString(), (MqttMessage) anyObject()); } @Test public void onMessageShouldNotifyMultipleObservers() { MqttMgrObserver observer1 = mock(MqttMgrObserver.class); MqttMgrObserver observer2 = mock(MqttMgrObserver.class); manager.registerObserver(observer1); manager.registerObserver(observer2); byte[] message = "my message".getBytes(); manager.messageArrived("/topic", new MqttMessage(message)); verify(observer1).onMessage(anyString(), (MqttMessage) anyObject()); verify(observer2).onMessage(anyString(), (MqttMessage) anyObject()); } @Test public void onMessageShouldNotifyMultipleObserversWithExceptions() { MqttMgrObserver observer1 = mock(MqttMgrObserver.class); MqttMgrObserver observer2 = mock(MqttMgrObserver.class); doThrow(new NullPointerException("NPE")).when(observer1).onMessage(anyString(), (MqttMessage) anyObject()); manager.registerObserver(observer1); manager.registerObserver(observer2); manager.messageArrived("/topic", new MqttMessage("my message".getBytes())); verify(observer2).onMessage(anyString(), (MqttMessage) anyObject()); } @Test public void onMessageShouldResetPinger() { manager.messageArrived("/topic", new MqttMessage("my message".getBytes())); verify(mockPinger, times(1)).reset(); } @Test public void messageDeliveredShouldResetPinger() { IMqttDeliveryToken token = mock(IMqttDeliveryToken.class); manager.deliveryComplete(token); verify(mockPinger, times(1)).reset(); } @Test public void subscribeMultipleTopicsShouldSubscribeToAllTopics() throws Exception { manager.subscribe("/topic/1", "/topic/2", "/topic/3"); verify(mockClient, times(3)).subscribe(anyString(), anyInt()); } @Test public void subscribeMultipleTopicsShouldSubscribeWithExceptions() throws Exception { doThrow(new MqttException(1)).when(mockClient).subscribe(eq("/topic/2"), anyInt()); manager.subscribe("/topic/1", "/topic/2", "/topic/3"); verify(mockClient, times(3)).subscribe(anyString(), anyInt()); } @Test public void subscribeMultipleTopicsShouldReconnectWithExceptions() throws Exception { doThrow(new MqttException(1)).when(mockClient).subscribe(anyString(), anyInt()); manager.setShouldReconnect(true); manager.subscribe("/topic/1", "/topic/2", "/topic/3"); verify(mockTimer, times(1)).setTimer(anyInt()); } @Test public void publishWithExceptionShouldReconnectDelayed() throws Exception { doThrow(new MqttException(1)).when(mockClient).publish(anyString(), (byte[]) anyObject(), anyInt(), anyBoolean()); manager.setShouldReconnect(true); manager.publish("my message".getBytes(), "/topic/1"); verify(mockTimer, times(1)).setTimer(anyInt()); } @Test public void closeShouldUnregisterPinger() throws Exception { manager.close(); verify(mockPinger).unregisterObserver((MqttPingerObserver) anyObject()); } @Test public void closeShouldUnregisterTimer() throws Exception { manager.close(); verify(mockTimer).unregisterObserver((MqttTimerObserver) anyObject()); } @Test public void connectShouldRegisterWithPinger() throws Exception { when(mockPinger.isActive()).thenReturn(false); manager.connect(); verify(mockPinger, times(1)).registerObserver(manager); } @Test public void reconnectDelayedShouldRegisterWithTimer() throws Exception { when(mockTimer.isActive()).thenReturn(false); manager.setShouldReconnect(true); manager.delayedReconnect(); verify(mockTimer, times(1)).registerObserver(manager); } }