/*
* 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.
*/
/**
* This file was automatically generated by the Mule Development Kit
*/
package org.mule.munit;
import static org.mule.modules.interceptor.processors.MessageProcessorId.getName;
import static org.mule.modules.interceptor.processors.MessageProcessorId.getNamespace;
import static org.mule.munit.common.MunitCore.buildMuleStackTrace;
import org.mule.DefaultMuleMessage;
import org.mule.api.DefaultMuleException;
import org.mule.api.MuleContext;
import org.mule.api.MuleEvent;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.api.NestedProcessor;
import org.mule.api.annotations.Module;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Optional;
import org.mule.api.context.MuleContextAware;
import org.mule.api.processor.MessageProcessor;
import org.mule.munit.common.mocking.EndpointMocker;
import org.mule.munit.common.mocking.MessageProcessorMocker;
import org.mule.munit.common.mocking.MunitMuleMessageTransformer;
import org.mule.munit.common.mocking.MunitSpy;
import org.mule.munit.common.mocking.MunitVerifier;
import org.mule.munit.common.mocking.NotDefinedPayload;
import org.mule.munit.common.mocking.SpyProcess;
import org.mule.transformer.AbstractMessageTransformer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
/**
* <p>
* Munit module for mocking message processors.
* <p/>
* </p>
*
* @author Mulesoft Inc.
* @since 3.3.2
*/
@Module(name = "mock", schemaVersion = "3.4", friendlyName = "Mock")
public class MockModule implements MuleContextAware
{
private MuleContext muleContext;
/**
* <p>Define what the mock must return on a message processor call.</p>
* <p/>
* <p>If the message processor doesn't return any value then there is no need to define an expect.</p>
* <p/>
* <p>You can define the message processor parameters in the same order they appear in the API documentation. In
* order to define the behaviour on that particular case.</p>
* <p/>
* {@sample.xml ../../../doc/mock-connector.xml.sample mock:expect}
*
* @param messageProcessor Message processor name.
* @param thenReturn Expected return value.
* @param withAttributes Message processor parameters.
* @param thenApplyTransformer Custom transformer to apply to the message
*/
@Processor
public void when(String messageProcessor,
@Optional List<Attribute> withAttributes,
@Optional MunitMuleMessage thenReturn,
@Optional final Object thenApplyTransformer)
{
if (thenApplyTransformer != null && thenApplyTransformer instanceof AbstractMessageTransformer)
{
mocker().when(getName(messageProcessor))
.ofNamespace(getNamespace(messageProcessor))
.withAttributes(createAttributes(withAttributes))
.thenApply(new MunitMuleMessageTransformer((AbstractMessageTransformer) thenApplyTransformer));
}
else
{
MunitMuleMessage munitMuleMessage = thenReturn == null ? new MunitMuleMessage() : thenReturn;
mocker().when(getName(messageProcessor))
.ofNamespace(getNamespace(messageProcessor))
.withAttributes(createAttributes(withAttributes))
.thenReturn(createMuleMessageFrom(munitMuleMessage.getPayload(),
munitMuleMessage.getInboundProperties(),
munitMuleMessage.getOutboundProperties(),
munitMuleMessage.getSessionProperties(),
munitMuleMessage.getInvocationProperties()));
}
}
/**
* <p>Define what the mock must return on a message processor call.</p>
* <p/>
* <p>If the message processor doesn't return any value then there is no need to define an expect.</p>
* <p/>
* <p>You can define the message processor parameters in the same order they appear in the API documentation. In
* order to define the behaviour on that particular case.</p>
* <p/>
* {@sample.xml ../../../doc/mock-connector.xml.sample mock:spy}
*
* @param messageProcessor Message processor name.
* @param withAttributes Sets of attributes to narrow-down a specific message processor
* @param assertionsBeforeCall Expected return value.
* @param assertionsAfterCall Message processor parameters.
*/
@Processor
public void spy(String messageProcessor,
@Optional List<Attribute> withAttributes,
@Optional List<NestedProcessor> assertionsBeforeCall,
@Optional List<NestedProcessor> assertionsAfterCall)
{
spy().spyMessageProcessor(getName(messageProcessor))
.ofNamespace(getNamespace(messageProcessor))
.withAttributes(createAttributes(withAttributes))
.before(createSpyAssertion(createMessageProcessorsFrom(assertionsBeforeCall)))
.after(createSpyAssertion(createMessageProcessorsFrom(assertionsAfterCall)));
}
/**
* <p>Expect to throw an exception when message processor is called. </p>
* <p/>
* {@sample.xml ../../../doc/mock-connector.xml.sample mock:expectFail}
*
* @param exception Java Exception full qualified name.
* @param whenCalling Message processor name.
* @param withAttributes list of expected attributes
*/
@Processor
public void throwAn(Throwable exception, String whenCalling,
@Optional List<Attribute> withAttributes)
{
mocker().when(getName(whenCalling))
.ofNamespace(getNamespace(whenCalling))
.withAttributes(createAttributes(withAttributes))
.thenThrow(exception);
}
/**
* Check that the message processor was called with some specified parameters
* <p/>
* {@sample.xml ../../../doc/mock-connector.xml.sample mock:verifyCall}
*
* @param messageProcessor Message processor Id
* @param attributes Message processor parameters.
* @param times Number of times the message processor has to be called
* @param atLeast Number of time the message processor has to be called at least.
* @param atMost Number of times the message processor has to be called at most.
*/
@Processor
public void verifyCall(String messageProcessor, @Optional List<Attribute> attributes,
@Optional Integer times,
@Optional Integer atLeast, @Optional Integer atMost)
{
try
{
MunitVerifier mockVerifier =
verifier().verifyCallOfMessageProcessor(getName(messageProcessor))
.ofNamespace(getNamespace(messageProcessor))
.withAttributes(createAttributes(attributes));
if (times != null)
{
mockVerifier.times(times);
}
else if (atLeast != null)
{
mockVerifier.atLeast(atLeast);
}
else if (atMost != null)
{
mockVerifier.atMost(atMost);
}
else
{
mockVerifier.atLeastOnce();
}
}
catch (AssertionError error)
{
AssertionError assertionException = new AssertionError(getMessage(error, "Verify Processor Failed"));
assertionException.setStackTrace(buildMuleStackTrace(muleContext).toArray(new StackTraceElement[] {}));
throw assertionException;
}
}
/**
* Reset mock behaviour
* <p/>
* {@sample.xml ../../../doc/mock-connector.xml.sample mock:outboundEndpoint}
*
* @param address the address
* @param exception in case it fails
* @param returnPayload the Return Payload
* @param thenApplyTransformer custom transformer to be applied to the message
* @param returnInboundProperties inbound properties
* @param returnInvocationProperties invocation properties
* @param returnSessionProperties invocation session properties
* @param returnOutboundProperties oubound properties
* @param assertions assertions
*/
@Processor
public void outboundEndpoint(String address,
@Optional Object returnPayload,
@Optional DefaultMuleException exception,
@Optional Object thenApplyTransformer,
@Optional Map<String, Object> returnInvocationProperties,
@Optional Map<String, Object> returnInboundProperties,
@Optional Map<String, Object> returnSessionProperties,
@Optional Map<String, Object> returnOutboundProperties,
@Optional List<NestedProcessor> assertions)
{
if (thenApplyTransformer != null && thenApplyTransformer instanceof AbstractMessageTransformer)
{
endpointMocker().whenEndpointWithAddress(address)
.withIncomingMessageSatisfying(createSpyAssertion(createMessageProcessorsFrom(assertions)))
.thenApply(new MunitMuleMessageTransformer((AbstractMessageTransformer) thenApplyTransformer));
}
else
{
if (exception != null)
{
endpointMocker().whenEndpointWithAddress(address)
.withIncomingMessageSatisfying(createSpyAssertion(createMessageProcessorsFrom(assertions)))
.thenThrow(exception);
}
else
{
endpointMocker().whenEndpointWithAddress(address)
.withIncomingMessageSatisfying(createSpyAssertion(createMessageProcessorsFrom(assertions)))
.thenReturn(createMuleMessageFrom(returnPayload,
returnInboundProperties,
returnOutboundProperties,
returnSessionProperties,
returnInvocationProperties));
}
}
}
@Override
public void setMuleContext(MuleContext muleContext)
{
this.muleContext = muleContext;
}
private MuleMessage createMuleMessageFrom(Object payload,
Map<String, Object> inboundProperties,
Map<String, Object> outboundProperties,
Map<String, Object> sessionProperties,
Map<String, Object> invocationProperties
)
{
Object definedPayload = payload;
if (payload == null)
{
definedPayload = NotDefinedPayload.getInstance();
}
DefaultMuleMessage message = new DefaultMuleMessage(definedPayload, muleContext);
if (inboundProperties != null)
{
for (String property : inboundProperties.keySet())
{
message.setInboundProperty(property, inboundProperties.get(property));
}
}
if (outboundProperties != null)
{
for (String property : outboundProperties.keySet())
{
message.setOutboundProperty(property, outboundProperties.get(property));
}
}
if (invocationProperties != null)
{
for (String property : invocationProperties.keySet())
{
message.setInvocationProperty(property, invocationProperties.get(property));
}
}
// TODO: how we can set the session properties?
// if ( sessionProperties != null ){
// for (String property : sessionProperties.keySet() ){
// message.setProperty(property, sessionProperties.get(property), PropertyScope.SESSION);
// }
// }
return message;
}
private List<MessageProcessor> createMessageProcessorsFrom(List<NestedProcessor> assertions)
{
if (assertions == null)
{
return null;
}
final List<MessageProcessor> mps = new ArrayList<MessageProcessor>();
for (NestedProcessor nestedProcessor : assertions)
{
mps.add(new NestedMessageProcessor(nestedProcessor));
}
return mps;
}
private Map<String, Object> createAttributes(List<Attribute> attributes)
{
Map<String, Object> attrs = new HashMap<String, Object>();
if (attributes == null)
{
return attrs;
}
for (Attribute attr : attributes)
{
attrs.put(attr.getName(), attr.getWhereValue());
}
return attrs;
}
private List<SpyProcess> createSpyAssertion(final List<MessageProcessor> messageProcessorsFrom)
{
List<SpyProcess> mps = new ArrayList<SpyProcess>(1);
if (messageProcessorsFrom != null)
{
mps.add(createSpy(messageProcessorsFrom));
}
return mps;
}
protected SpyProcess createSpy(final List<MessageProcessor> messageProcessorsFrom)
{
return new SpyProcess()
{
@Override
public void spy(MuleEvent event) throws MuleException
{
for (MessageProcessor mp : messageProcessorsFrom)
{
mp.process(event);
}
}
};
}
protected MessageProcessorMocker mocker()
{
return new MessageProcessorMocker(muleContext);
}
protected EndpointMocker endpointMocker()
{
return new EndpointMocker(muleContext);
}
protected MunitVerifier verifier()
{
return new MunitVerifier(muleContext);
}
protected MunitSpy spy()
{
return new MunitSpy(muleContext);
}
private String getMessage(AssertionError error, String defaultValue)
{
String message = error.getMessage();
if (StringUtils.isEmpty(message))
{
return defaultValue;
}
return message;
}
}