/*
* 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.module.extension.internal.runtime.config;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.any;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.rules.ExpectedException.none;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.same;
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;
import static org.mockito.Mockito.withSettings;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.mule.runtime.api.connection.ConnectionValidationResult.failure;
import static org.mule.runtime.api.connection.ConnectionValidationResult.success;
import static org.mule.runtime.core.api.config.MuleProperties.OBJECT_CONNECTION_MANAGER;
import static org.mule.runtime.core.api.config.MuleProperties.OBJECT_TIME_SUPPLIER;
import org.mule.runtime.api.connection.ConnectionException;
import org.mule.runtime.api.connection.ConnectionProvider;
import org.mule.runtime.api.lifecycle.Disposable;
import org.mule.runtime.api.lifecycle.Initialisable;
import org.mule.runtime.api.lifecycle.Lifecycle;
import org.mule.runtime.api.lifecycle.Startable;
import org.mule.runtime.api.lifecycle.Stoppable;
import org.mule.runtime.api.meta.model.config.ConfigurationModel;
import org.mule.runtime.core.api.context.MuleContextAware;
import org.mule.runtime.core.api.retry.RetryNotifier;
import org.mule.runtime.core.api.retry.RetryPolicyTemplate;
import org.mule.runtime.core.internal.connection.ConnectionManagerAdapter;
import org.mule.runtime.core.internal.metadata.MuleMetadataService;
import org.mule.runtime.core.retry.RetryPolicyExhaustedException;
import org.mule.runtime.core.retry.policies.SimpleRetryPolicyTemplate;
import org.mule.runtime.extension.api.runtime.ConfigurationInstance;
import org.mule.runtime.module.extension.internal.AbstractInterceptableContractTestCase;
import org.mule.tck.probe.JUnitLambdaProbe;
import org.mule.tck.probe.PollingProber;
import org.mule.tck.size.SmallTest;
import org.mule.tck.util.TestTimeSupplier;
import java.util.Collection;
import java.util.Optional;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.verification.VerificationMode;
@SmallTest
@RunWith(Parameterized.class)
public class LifecycleAwareConfigurationInstanceTestCase
extends AbstractInterceptableContractTestCase<LifecycleAwareConfigurationInstance> {
protected static final int RECONNECTION_MAX_ATTEMPTS = 5;
private static final int RECONNECTION_FREQ = 100;
private static final String NAME = "name";
@Parameters(name = "{0}")
public static Collection<Object[]> data() {
return asList(new Object[][] {
{"With provider",
mock(ConnectionProvider.class, withSettings().extraInterfaces(Lifecycle.class, MuleContextAware.class))},
{"Without provider", null}
});
}
@Rule
public ExpectedException expectedException = none();
@Mock
private ConfigurationModel configurationModel;
@Mock
protected Lifecycle value;
@Mock
protected ConnectionManagerAdapter connectionManager;
protected RetryPolicyTemplate retryPolicyTemplate;
private String name;
protected Optional<ConnectionProvider> connectionProvider;
public LifecycleAwareConfigurationInstanceTestCase(String name, ConnectionProvider connectionProvider) {
this.name = name;
this.connectionProvider = Optional.ofNullable(connectionProvider);
}
private TestTimeSupplier timeSupplier = new TestTimeSupplier(System.currentTimeMillis());
@Before
@Override
public void before() throws Exception {
initMocks(this);
muleContext.getRegistry().registerObject(OBJECT_CONNECTION_MANAGER, connectionManager);
muleContext.getRegistry().registerObject(OBJECT_TIME_SUPPLIER, timeSupplier);
retryPolicyTemplate = createRetryTemplate();
retryPolicyTemplate.setNotifier(mock(RetryNotifier.class));
super.before();
}
protected RetryPolicyTemplate createRetryTemplate() {
return new SimpleRetryPolicyTemplate(RECONNECTION_FREQ, RECONNECTION_MAX_ATTEMPTS);
}
@After
public void after() {
interceptable.dispose();
}
@Override
protected LifecycleAwareConfigurationInstance createInterceptable() {
if (connectionProvider.isPresent()) {
reset(connectionProvider.get());
}
setup(connectionManager);
return new LifecycleAwareConfigurationInstance(NAME, configurationModel, value, getInterceptors(), connectionProvider);
}
private void setup(ConnectionManagerAdapter connectionManager) {
if (connectionProvider.isPresent()) {
when(connectionManager.getRetryTemplateFor(connectionProvider.get())).thenReturn(retryPolicyTemplate);
when(connectionManager.testConnectivity(Mockito.any(ConfigurationInstance.class))).thenReturn(success());
}
}
private void reset(Object object) {
Mockito.reset(object);
if (object instanceof ConnectionManagerAdapter) {
setup((ConnectionManagerAdapter) object);
}
}
@Test
public void valueInjected() throws Exception {
interceptable.initialise();
verify(injector).inject(value);
if (connectionProvider.isPresent()) {
verify(injector).inject(connectionProvider.get());
} else {
verify(injector, never()).inject(any(ConnectionProvider.class));
}
}
@Test
public void connectionBound() throws Exception {
interceptable.initialise();
assertBound();
}
private void assertBound() throws Exception {
if (connectionProvider.isPresent()) {
verify(connectionManager, times(1)).bind(value, connectionProvider.get());
} else {
verify(connectionManager, never()).bind(same(value), anyObject());
}
}
private VerificationMode getBindingVerificationMode() {
return connectionProvider.map(p -> times(1)).orElse(never());
}
@Test
public void connectionReBoundfterStopStart() throws Exception {
connectionBound();
interceptable.start();
interceptable.stop();
verify(connectionManager, getBindingVerificationMode()).unbind(value);
reset(connectionManager);
interceptable.start();
assertBound();
}
@Test
public void valueInitialised() throws Exception {
interceptable.initialise();
verify((Initialisable) value).initialise();
if (connectionProvider.isPresent()) {
verify((Initialisable) connectionProvider.get()).initialise();
}
}
@Test
public void valueStarted() throws Exception {
interceptable.start();
verify((Startable) value).start();
if (connectionProvider.isPresent()) {
verify((Startable) connectionProvider.get()).start();
}
}
@Test
public void testConnectivityUponStart() throws Exception {
if (connectionProvider.isPresent()) {
valueStarted();
verify(connectionManager).testConnectivity(interceptable);
}
}
@Test
public void testConnectivityFailsUponStart() throws Exception {
if (connectionProvider.isPresent()) {
Exception connectionException = new ConnectionException("Oops!");
when(connectionManager.testConnectivity(interceptable))
.thenReturn(failure(connectionException.getMessage(), connectionException));
try {
interceptable.start();
fail("Was expecting connectivity testing to fail");
} catch (Exception e) {
verify(connectionManager, times(RECONNECTION_MAX_ATTEMPTS + 1)).testConnectivity(interceptable);
assertThat(e.getCause(), is(instanceOf(RetryPolicyExhaustedException.class)));
}
}
}
@Test
public void valueStopped() throws Exception {
interceptable.start();
interceptable.stop();
verify((Stoppable) value).stop();
if (connectionProvider.isPresent()) {
verify((Stoppable) connectionProvider.get()).stop();
}
}
@Test
public void connectionUnbound() throws Exception {
interceptable.start();
interceptable.stop();
if (connectionProvider.isPresent()) {
verify(connectionManager).unbind(value);
} else {
verify(connectionManager, never()).unbind(anyObject());
}
}
@Test
public void valueDisposed() throws Exception {
interceptable.initialise();
interceptable.dispose();
verify((Disposable) value).dispose();
if (connectionProvider.isPresent()) {
verify((Disposable) connectionProvider.get()).dispose();
}
}
@Test
public void disposeMetadataCacheWhenConfigIsDisposed() throws Exception {
MuleMetadataService muleMetadataManager = muleContext.getRegistry().lookupObject(MuleMetadataService.class);
muleMetadataManager.getMetadataCache(NAME);
interceptable.start();
interceptable.stop();
new PollingProber(1000, 100).check(new JUnitLambdaProbe(() -> muleMetadataManager.getMetadataCaches().entrySet().isEmpty()));
}
@Test
public void getName() {
assertThat(interceptable.getName(), is(NAME));
}
@Test
public void getModel() {
assertThat(interceptable.getModel(), is(sameInstance(configurationModel)));
}
@Test
public void getValue() {
assertThat(interceptable.getValue(), is(sameInstance(value)));
}
@Test(expected = IllegalStateException.class)
public void getStatsBeforeInit() {
interceptable.getStatistics();
}
@Test
public void getStatistics() throws Exception {
interceptable.initialise();
assertThat(interceptable.getStatistics(), is(notNullValue()));
}
}