/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.exception; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.rules.ExpectedException.none; import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.mule.runtime.api.message.Message.of; import static org.mule.tck.MuleTestUtils.getTestFlow; import static org.mule.tck.util.MuleContextUtils.mockContextWithServices; import static org.mule.test.allure.AllureConstants.ErrorHandlingFeature.ERROR_HANDLING; import static org.mule.test.allure.AllureConstants.ErrorHandlingFeature.ErrorHandlingStory.ON_ERROR_CONTINUE; import org.mule.runtime.api.lifecycle.Initialisable; import org.mule.runtime.api.message.Message; import org.mule.runtime.api.tx.TransactionException; import org.mule.runtime.core.DefaultEventContext; import org.mule.runtime.core.api.DefaultMuleException; import org.mule.runtime.core.api.Event; import org.mule.runtime.core.api.EventContext; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.core.api.construct.Flow; import org.mule.runtime.core.api.processor.Processor; import org.mule.runtime.core.api.transaction.Transaction; import org.mule.runtime.core.api.util.StreamCloserService; import org.mule.runtime.core.internal.message.InternalMessage; import org.mule.runtime.core.streaming.StreamingManager; import org.mule.runtime.core.transaction.TransactionCoordination; import org.mule.tck.junit4.AbstractMuleContextTestCase; import org.mule.tck.testmodels.mule.TestTransaction; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Stories; @Features(ERROR_HANDLING) @Stories(ON_ERROR_CONTINUE) @RunWith(MockitoJUnitRunner.class) public class OnErrorContinueHandlerTestCase extends AbstractMuleContextTestCase { protected MuleContext muleContext = mockContextWithServices(); private MuleContext mockMuleContext = mock(MuleContext.class, RETURNS_DEEP_STUBS.get()); @Rule public ExpectedException expectedException = none(); @Mock private MessagingException mockException; private Event muleEvent; private Message muleMessage = of(""); @Mock private StreamCloserService mockStreamCloserService; @Mock private StreamingManager mockStreamingManager; @Spy private TestTransaction mockTransaction = new TestTransaction(mockMuleContext); @Spy private TestTransaction mockXaTransaction = new TestTransaction(mockMuleContext, true); private Flow flow; private EventContext context; private OnErrorContinueHandler onErrorContinueHandler; @Before public void before() throws Exception { Transaction currentTransaction = TransactionCoordination.getInstance().getTransaction(); if (currentTransaction != null) { TransactionCoordination.getInstance().unbindTransaction(currentTransaction); } flow = getTestFlow(muleContext); flow.initialise(); onErrorContinueHandler = new OnErrorContinueHandler(); onErrorContinueHandler.setMuleContext(mockMuleContext); onErrorContinueHandler.setFlowConstruct(flow); when(mockMuleContext.getStreamCloserService()).thenReturn(mockStreamCloserService); when(mockMuleContext.getRegistry().lookupObject(StreamingManager.class)).thenReturn(mockStreamingManager); context = DefaultEventContext.create(flow, TEST_CONNECTOR_LOCATION); muleEvent = Event.builder(context).message(muleMessage).flow(flow).build(); } @Test public void testHandleExceptionWithNoConfig() throws Exception { configureXaTransactionAndSingleResourceTransaction(); Event resultEvent = onErrorContinueHandler.handleException(mockException, muleEvent); assertThat(resultEvent.getMessage().getPayload().getValue(), equalTo(muleEvent.getMessage().getPayload().getValue())); verify(mockTransaction, times(0)).setRollbackOnly(); verify(mockTransaction, times(0)).commit(); verify(mockTransaction, times(0)).rollback(); verify(mockStreamCloserService).closeStream(any(Object.class)); } @Test public void testHandleExceptionWithConfiguredMessageProcessors() throws Exception { onErrorContinueHandler .setMessageProcessors(asList(createSetStringMessageProcessor("A"), createSetStringMessageProcessor("B"))); onErrorContinueHandler.initialise(); final Event result = onErrorContinueHandler.handleException(mockException, muleEvent); assertThat(result.getMessage().getPayload().getValue(), is("B")); assertThat(result.getError().isPresent(), is(false)); } @Test public void testHandleExceptionWithMessageProcessorsChangingEvent() throws Exception { Event lastEventCreated = Event.builder(context).message(muleMessage).flow(flow).build(); onErrorContinueHandler .setMessageProcessors(asList(createChagingEventMessageProcessor(Event.builder(context).message(muleMessage).flow(flow) .build()), createChagingEventMessageProcessor(lastEventCreated))); onErrorContinueHandler.initialise(); Event exceptionHandlingResult = onErrorContinueHandler.handleException(mockException, muleEvent); assertThat(exceptionHandlingResult.getCorrelationId(), is(lastEventCreated.getCorrelationId())); } /** * On fatal error, the exception strategies are not supposed to use Message.toString() as it could potentially log sensible * data. */ @Test public void testMessageToStringNotCalledOnFailure() throws Exception { muleEvent = Event.builder(muleEvent).message(spy(muleMessage)).build(); muleEvent = spy(muleEvent); when(mockException.getStackTrace()).thenReturn(new StackTraceElement[0]); Event lastEventCreated = Event.builder(context).message(muleMessage).flow(flow).build(); onErrorContinueHandler .setMessageProcessors(asList(createFailingEventMessageProcessor(Event.builder(context).message(muleMessage).flow(flow) .build()), createFailingEventMessageProcessor(lastEventCreated))); onErrorContinueHandler.initialise(); when(muleEvent.getMessage().toString()).thenThrow(new RuntimeException("Message.toString() should not be called")); expectedException.expect(Exception.class); Event exceptionHandlingResult = exceptionHandlingResult = onErrorContinueHandler.handleException(mockException, muleEvent); } private Processor createChagingEventMessageProcessor(final Event lastEventCreated) { return event -> lastEventCreated; } private Processor createFailingEventMessageProcessor(final Event lastEventCreated) { return event -> { throw new DefaultMuleException(mockException); }; } private Processor createSetStringMessageProcessor(final String appendText) { return event -> { return Event.builder(event).message(InternalMessage.builder(event.getMessage()).payload(appendText).build()).build(); }; } private void configureXaTransactionAndSingleResourceTransaction() throws TransactionException { TransactionCoordination.getInstance().bindTransaction(mockXaTransaction); TransactionCoordination.getInstance().suspendCurrentTransaction(); TransactionCoordination.getInstance().bindTransaction(mockTransaction); } }