From dd56417e130376af1c7185a9efc5a1ca56408628 Mon Sep 17 00:00:00 2001 From: timtay-microsoft Date: Thu, 30 Aug 2018 11:35:54 -0700 Subject: [PATCH] fix(iot-dev): fix amqp exception logging to check for all exposed exceptions --- .../amqps/AmqpsIotHubConnection.java | 65 +++- .../amqps/AmqpsIotHubConnectionTest.java | 364 +++++++++++++++++- 2 files changed, 415 insertions(+), 14 deletions(-) diff --git a/device/iot-device-client/src/main/java/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnection.java b/device/iot-device-client/src/main/java/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnection.java index 79a34d1b43..f79cb5e01d 100644 --- a/device/iot-device-client/src/main/java/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnection.java +++ b/device/iot-device-client/src/main/java/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnection.java @@ -1099,17 +1099,66 @@ private TransportException getTransportExceptionFromEvent(Event event) { TransportException transportException = new TransportException("Unknown transport exception occurred"); transportException.setRetryable(true); - if (event.getSender() != null && event.getSender().getRemoteCondition() != null && event.getSender().getRemoteCondition().getCondition() != null) + + String error = ""; + String errorDescription = ""; + + String senderError = event.getSender() != null && event.getSender().getRemoteCondition() != null && event.getSender().getRemoteCondition().getCondition() != null ? event.getSender().getRemoteCondition().getCondition().toString() : ""; + String receiverError = event.getReceiver() != null && event.getReceiver().getRemoteCondition() != null && event.getReceiver().getRemoteCondition().getCondition() != null ? event.getReceiver().getRemoteCondition().getCondition().toString() : ""; + String sessionError = event.getSession() != null && event.getSession().getRemoteCondition() != null && event.getSession().getRemoteCondition().getCondition() != null ? event.getSession().getRemoteCondition().getCondition().toString() : ""; + String connectionError = event.getConnection() != null && event.getConnection().getRemoteCondition() != null && event.getConnection().getRemoteCondition().getCondition() != null ? event.getConnection().getRemoteCondition().getCondition().toString() : ""; + String linkError = event.getLink() != null && event.getLink().getRemoteCondition() != null && event.getLink().getRemoteCondition().getCondition() != null ? event.getLink().getRemoteCondition().getCondition().toString() : ""; + String transportError = event.getTransport() != null && event.getTransport().getRemoteCondition() != null && event.getTransport().getRemoteCondition().getCondition() != null ? event.getTransport().getRemoteCondition().getCondition().toString() : ""; + + String senderErrorDescription = event.getSender() != null && event.getSender().getRemoteCondition() != null && event.getSender().getRemoteCondition().getDescription() != null ? event.getSender().getRemoteCondition().getDescription() : ""; + String receiverErrorDescription = event.getReceiver() != null && event.getReceiver().getRemoteCondition() != null && event.getReceiver().getRemoteCondition().getDescription() != null ? event.getReceiver().getRemoteCondition().getDescription() : ""; + String sessionErrorDescription = event.getSession() != null && event.getSession().getRemoteCondition() != null && event.getSession().getRemoteCondition().getDescription() != null ? event.getSession().getRemoteCondition().getDescription() : ""; + String connectionErrorDescription = event.getConnection() != null && event.getConnection().getRemoteCondition() != null && event.getConnection().getRemoteCondition().getDescription() != null ? event.getConnection().getRemoteCondition().getDescription() : ""; + String linkErrorDescription = event.getLink() != null && event.getLink().getRemoteCondition() != null && event.getLink().getRemoteCondition().getDescription() != null ? event.getLink().getRemoteCondition().getDescription() : ""; + String transportErrorDescription = event.getTransport() != null && event.getTransport().getRemoteCondition() != null && event.getTransport().getRemoteCondition().getDescription() != null ? event.getTransport().getRemoteCondition().getDescription() : ""; + + if (!senderError.isEmpty()) + { + // Codes_SRS_AMQPSIOTHUBCONNECTION_34_081: [If an exception can be found in the sender, this function shall return a the mapped amqp exception derived from that exception.] + error = senderError; + errorDescription = senderErrorDescription; + } + else if (!receiverError.isEmpty()) + { + // Codes_SRS_AMQPSIOTHUBCONNECTION_34_082: [If an exception can be found in the receiver, this function shall return a the mapped amqp exception derived from that exception.] + error = receiverError; + errorDescription = receiverErrorDescription; + } + else if (!sessionError.isEmpty()) + { + // Codes_SRS_AMQPSIOTHUBCONNECTION_34_083: [If an exception can be found in the session, this function shall return a the mapped amqp exception derived from that exception.] + error = sessionError; + errorDescription = sessionErrorDescription; + } + else if (!connectionError.isEmpty()) + { + // Codes_SRS_AMQPSIOTHUBCONNECTION_34_084: [If an exception can be found in the connection, this function shall return a the mapped amqp exception derived from that exception.] + error = connectionError; + errorDescription = connectionErrorDescription; + } + else if (!linkError.isEmpty()) + { + // Codes_SRS_AMQPSIOTHUBCONNECTION_34_085: [If an exception can be found in the link, this function shall return a the mapped amqp exception derived from that exception.] + error = linkError; + errorDescription = linkErrorDescription; + } + else if (!transportError.isEmpty()) { - String amqpErrorType = event.getSender().getRemoteCondition().getCondition().toString(); + // Codes_SRS_AMQPSIOTHUBCONNECTION_34_086: [If an exception can be found in the transport, this function shall return a the mapped amqp exception derived from that exception.] + error = transportError; + errorDescription = transportErrorDescription; + } - String errorDescription = ""; - if (event.getSender().getRemoteCondition().getDescription() != null) - { - errorDescription = event.getSender().getRemoteCondition().getDescription(); - } - transportException = AmqpsExceptionTranslator.convertToAmqpException(amqpErrorType, errorDescription); + // Codes_SRS_AMQPSIOTHUBCONNECTION_34_080: [If no exception can be found in the sender, receiver, session, connection, link, or transport, this function shall return a generic TransportException.] + if (!error.isEmpty()) + { + transportException = AmqpsExceptionTranslator.convertToAmqpException(error, errorDescription); } return transportException; diff --git a/device/iot-device-client/src/test/java/tests/unit/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnectionTest.java b/device/iot-device-client/src/test/java/tests/unit/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnectionTest.java index 0d8fa5d8ba..bcdc651fdf 100644 --- a/device/iot-device-client/src/test/java/tests/unit/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnectionTest.java +++ b/device/iot-device-client/src/test/java/tests/unit/com/microsoft/azure/sdk/iot/device/transport/amqps/AmqpsIotHubConnectionTest.java @@ -17,10 +17,7 @@ import com.microsoft.azure.sdk.iot.device.transport.IotHubListener; import com.microsoft.azure.sdk.iot.device.transport.IotHubTransportMessage; import com.microsoft.azure.sdk.iot.device.transport.amqps.*; -import com.microsoft.azure.sdk.iot.device.transport.amqps.exceptions.AmqpConnectionForcedException; -import com.microsoft.azure.sdk.iot.device.transport.amqps.exceptions.AmqpConnectionThrottledException; -import com.microsoft.azure.sdk.iot.device.transport.amqps.exceptions.AmqpLinkRedirectException; -import com.microsoft.azure.sdk.iot.device.transport.amqps.exceptions.AmqpSessionWindowViolationException; +import com.microsoft.azure.sdk.iot.device.transport.amqps.exceptions.*; import mockit.*; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.amqp.Symbol; @@ -575,7 +572,7 @@ public void openCallsAuthenticateAndOpenLinks() throws TransportException, Inter } } - // Codes_SRS_AMQPSIOTHUBCONNECTION_34_062: [If, after attempting to open the connection, this + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_062: [If, after attempting to open the connection, this // object has a saved exception, this function shall throw that saved exception.] @Test (expected = AmqpConnectionForcedException.class) public void openThrowsSavedExceptionsIfAnyExceptionsSavedDuringOpen() throws TransportException, InterruptedException @@ -869,7 +866,7 @@ public void closeThrowsIfWaitLatchThrows() throws Exception // Tests_SRS_AMQPSIOTHUBCONNECTION_15_012: [The function shall set the status of the AMQPS connection to DISCONNECTED.] // Tests_SRS_AMQPSIOTHUBCONNECTION_15_013: [The function shall closeNow the AmqpsSessionManager and the AMQP connection.] - // Codes_SRS_AMQPSIOTHUBCONNECTION_34_014: [If this object's proton reactor is not null, this function shall stop the Proton reactor.] + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_014: [If this object's proton reactor is not null, this function shall stop the Proton reactor.] @Test public void closeClosesAllProtonVariablesAndStopsProtonReactor() throws TransportException { @@ -2481,7 +2478,362 @@ public void getConnectionIdReturnsSavedConnectionId() throws TransportException Assert.assertEquals(expectedConnectionId, actualConnectionId); } + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_081: [If an exception can be found in the sender, this function shall return a the mapped amqp exception derived from that exception.] + @Test + public void getErrorFromEventOutOfSender(final @Mocked ErrorCondition mockedErrorCondition, @Mocked final AmqpConnectionFramingErrorException mockedAmqpConnectionFramingErrorException) throws TransportException + { + //arrange + baseExpectations(); + AmqpsIotHubConnection connection = new AmqpsIotHubConnection(mockConfig); + final String expectedError = AmqpConnectionFramingErrorException.errorCode; + final String expectedErrorDescription = "sender error description"; + new NonStrictExpectations() + { + { + mockEvent.getSender(); + result = mockSender; + mockEvent.getReceiver(); + result = null; + mockEvent.getTransport(); + result = null; + mockEvent.getSession(); + result = null; + mockEvent.getConnection(); + result = null; + mockEvent.getLink(); + result = null; + + mockSender.getRemoteCondition(); + result = mockedErrorCondition; + + mockedErrorCondition.getCondition(); + result = mockedSymbol; + + mockedSymbol.toString(); + result = expectedError; + + mockedErrorCondition.getDescription(); + result = expectedErrorDescription; + + new AmqpConnectionFramingErrorException(expectedErrorDescription); + result = mockedAmqpConnectionFramingErrorException; + + mockedAmqpConnectionFramingErrorException.getMessage(); + result = expectedErrorDescription; + } + }; + + //act + TransportException actualException = Deencapsulation.invoke(connection, "getTransportExceptionFromEvent", mockEvent); + + //assert + assertTrue(actualException instanceof AmqpConnectionFramingErrorException); + assertEquals(expectedErrorDescription, actualException.getMessage()); + } + + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_082: [If an exception can be found in the receiver, this function shall return a the mapped amqp exception derived from that exception.] + @Test + public void getErrorFromEventOutOfReceiver(final @Mocked ErrorCondition mockedErrorCondition, @Mocked final AmqpConnectionFramingErrorException mockedAmqpConnectionFramingErrorException) throws TransportException + { + //arrange + baseExpectations(); + AmqpsIotHubConnection connection = new AmqpsIotHubConnection(mockConfig); + final String expectedError = AmqpConnectionFramingErrorException.errorCode; + final String expectedErrorDescription = "sender error description"; + new NonStrictExpectations() + { + { + mockEvent.getSender(); + result = null; + mockEvent.getReceiver(); + result = mockReceiver; + mockEvent.getTransport(); + result = null; + mockEvent.getSession(); + result = null; + mockEvent.getConnection(); + result = null; + mockEvent.getLink(); + result = null; + + mockReceiver.getRemoteCondition(); + result = mockedErrorCondition; + + mockedErrorCondition.getCondition(); + result = mockedSymbol; + + mockedSymbol.toString(); + result = expectedError; + + mockedErrorCondition.getDescription(); + result = expectedErrorDescription; + + new AmqpConnectionFramingErrorException(expectedErrorDescription); + result = mockedAmqpConnectionFramingErrorException; + + mockedAmqpConnectionFramingErrorException.getMessage(); + result = expectedErrorDescription; + } + }; + + //act + TransportException actualException = Deencapsulation.invoke(connection, "getTransportExceptionFromEvent", mockEvent); + + //assert + assertTrue(actualException instanceof AmqpConnectionFramingErrorException); + assertEquals(expectedErrorDescription, actualException.getMessage()); + } + + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_086: [If an exception can be found in the transport, this function shall return a the mapped amqp exception derived from that exception.] + @Test + public void getErrorFromEventOutOfTransport(final @Mocked ErrorCondition mockedErrorCondition, @Mocked final AmqpConnectionFramingErrorException mockedAmqpConnectionFramingErrorException) throws TransportException + { + //arrange + baseExpectations(); + AmqpsIotHubConnection connection = new AmqpsIotHubConnection(mockConfig); + final String expectedError = AmqpConnectionFramingErrorException.errorCode; + final String expectedErrorDescription = "sender error description"; + new NonStrictExpectations() + { + { + mockEvent.getSender(); + result = null; + mockEvent.getReceiver(); + result = null; + mockEvent.getTransport(); + result = mockTransport; + mockEvent.getSession(); + result = null; + mockEvent.getConnection(); + result = null; + mockEvent.getLink(); + result = null; + + mockTransport.getRemoteCondition(); + result = mockedErrorCondition; + + mockedErrorCondition.getCondition(); + result = mockedSymbol; + + mockedSymbol.toString(); + result = expectedError; + + mockedErrorCondition.getDescription(); + result = expectedErrorDescription; + + new AmqpConnectionFramingErrorException(expectedErrorDescription); + result = mockedAmqpConnectionFramingErrorException; + + mockedAmqpConnectionFramingErrorException.getMessage(); + result = expectedErrorDescription; + } + }; + + //act + TransportException actualException = Deencapsulation.invoke(connection, "getTransportExceptionFromEvent", mockEvent); + + //assert + assertTrue(actualException instanceof AmqpConnectionFramingErrorException); + assertEquals(expectedErrorDescription, actualException.getMessage()); + } + + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_083: [If an exception can be found in the session, this function shall return a the mapped amqp exception derived from that exception.] + @Test + public void getErrorFromEventOutOfSession(final @Mocked ErrorCondition mockedErrorCondition, @Mocked final AmqpConnectionFramingErrorException mockedAmqpConnectionFramingErrorException) throws TransportException + { + //arrange + baseExpectations(); + AmqpsIotHubConnection connection = new AmqpsIotHubConnection(mockConfig); + final String expectedError = AmqpConnectionFramingErrorException.errorCode; + final String expectedErrorDescription = "sender error description"; + new NonStrictExpectations() + { + { + mockEvent.getSender(); + result = null; + mockEvent.getReceiver(); + result = null; + mockEvent.getTransport(); + result = null; + mockEvent.getSession(); + result = mockSession; + mockEvent.getConnection(); + result = null; + mockEvent.getLink(); + result = null; + + mockSession.getRemoteCondition(); + result = mockedErrorCondition; + + mockedErrorCondition.getCondition(); + result = mockedSymbol; + + mockedSymbol.toString(); + result = expectedError; + + mockedErrorCondition.getDescription(); + result = expectedErrorDescription; + + new AmqpConnectionFramingErrorException(expectedErrorDescription); + result = mockedAmqpConnectionFramingErrorException; + + mockedAmqpConnectionFramingErrorException.getMessage(); + result = expectedErrorDescription; + } + }; + + //act + TransportException actualException = Deencapsulation.invoke(connection, "getTransportExceptionFromEvent", mockEvent); + + //assert + assertTrue(actualException instanceof AmqpConnectionFramingErrorException); + assertEquals(expectedErrorDescription, actualException.getMessage()); + } + + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_084: [If an exception can be found in the connection, this function shall return a the mapped amqp exception derived from that exception.] + @Test + public void getErrorFromEventOutOfConnection(final @Mocked ErrorCondition mockedErrorCondition, @Mocked final AmqpConnectionFramingErrorException mockedAmqpConnectionFramingErrorException) throws TransportException + { + //arrange + baseExpectations(); + AmqpsIotHubConnection connection = new AmqpsIotHubConnection(mockConfig); + final String expectedError = AmqpConnectionFramingErrorException.errorCode; + final String expectedErrorDescription = "sender error description"; + new NonStrictExpectations() + { + { + mockEvent.getSender(); + result = null; + mockEvent.getReceiver(); + result = null; + mockEvent.getTransport(); + result = null; + mockEvent.getSession(); + result = null; + mockEvent.getConnection(); + result = mockConnection; + mockEvent.getLink(); + result = null; + + mockConnection.getRemoteCondition(); + result = mockedErrorCondition; + + mockedErrorCondition.getCondition(); + result = mockedSymbol; + + mockedSymbol.toString(); + result = expectedError; + + mockedErrorCondition.getDescription(); + result = expectedErrorDescription; + + new AmqpConnectionFramingErrorException(expectedErrorDescription); + result = mockedAmqpConnectionFramingErrorException; + + mockedAmqpConnectionFramingErrorException.getMessage(); + result = expectedErrorDescription; + } + }; + + //act + TransportException actualException = Deencapsulation.invoke(connection, "getTransportExceptionFromEvent", mockEvent); + + //assert + assertTrue(actualException instanceof AmqpConnectionFramingErrorException); + assertEquals(expectedErrorDescription, actualException.getMessage()); + } + + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_085: [If an exception can be found in the link, this function shall return a the mapped amqp exception derived from that exception.] + @Test + public void getErrorFromEventOutOfLink(final @Mocked ErrorCondition mockedErrorCondition, @Mocked final AmqpConnectionFramingErrorException mockedAmqpConnectionFramingErrorException) throws TransportException + { + //arrange + baseExpectations(); + AmqpsIotHubConnection connection = new AmqpsIotHubConnection(mockConfig); + final String expectedError = AmqpConnectionFramingErrorException.errorCode; + final String expectedErrorDescription = "sender error description"; + new NonStrictExpectations() + { + { + mockEvent.getSender(); + result = null; + mockEvent.getReceiver(); + result = null; + mockEvent.getTransport(); + result = null; + mockEvent.getSession(); + result = null; + mockEvent.getConnection(); + result = null; + mockEvent.getLink(); + result = mockLink; + mockLink.getRemoteCondition(); + result = mockedErrorCondition; + + mockedErrorCondition.getCondition(); + result = mockedSymbol; + + mockedSymbol.toString(); + result = expectedError; + + mockedErrorCondition.getDescription(); + result = expectedErrorDescription; + + new AmqpConnectionFramingErrorException(expectedErrorDescription); + result = mockedAmqpConnectionFramingErrorException; + + mockedAmqpConnectionFramingErrorException.getMessage(); + result = expectedErrorDescription; + } + }; + + //act + TransportException actualException = Deencapsulation.invoke(connection, "getTransportExceptionFromEvent", mockEvent); + + //assert + assertTrue(actualException instanceof AmqpConnectionFramingErrorException); + assertEquals(expectedErrorDescription, actualException.getMessage()); + } + + // Tests_SRS_AMQPSIOTHUBCONNECTION_34_080: [If no exception can be found in the sender, receiver, session, connection, link, or transport, this function shall return a generic TransportException.] + @Test + public void getErrorFromEventDefault(final @Mocked ErrorCondition mockedErrorCondition, @Mocked final AmqpConnectionFramingErrorException mockedAmqpConnectionFramingErrorException) throws TransportException + { + //arrange + baseExpectations(); + AmqpsIotHubConnection connection = new AmqpsIotHubConnection(mockConfig); + new NonStrictExpectations() + { + { + mockEvent.getSender(); + result = null; + mockEvent.getReceiver(); + result = null; + mockEvent.getTransport(); + result = null; + mockEvent.getSession(); + result = null; + mockEvent.getConnection(); + result = null; + mockEvent.getLink(); + result = null; + + new TransportException("Unknown transport exception occurred"); + result = mockedTransportException; + + mockedTransportException.getMessage(); + result = "Unknown transport exception occurred"; + } + }; + + //act + TransportException actualException = Deencapsulation.invoke(connection, "getTransportExceptionFromEvent", mockEvent); + + //assert + assertEquals("Unknown transport exception occurred", actualException.getMessage()); + } + private void baseExpectations() { new NonStrictExpectations() {