/*
* 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.loader.enricher;
import static java.util.Collections.singleton;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasProperty;
import static org.mule.runtime.core.exception.Errors.Identifiers.CONNECTIVITY_ERROR_IDENTIFIER;
import static org.mule.runtime.module.extension.internal.loader.enricher.EnricherTestUtils.getNamedObject;
import static org.mule.runtime.module.extension.internal.loader.enricher.LevelErrorTypes.EXTENSION;
import static org.mule.runtime.module.extension.internal.loader.enricher.LevelErrorTypes.OPERATION;
import static org.mule.test.heisenberg.extension.HeisenbergErrors.HEALTH;
import static org.mule.test.heisenberg.extension.HeisenbergExtension.EXTENSION_DESCRIPTION;
import static org.mule.runtime.module.extension.internal.util.MuleExtensionUtils.loadExtension;
import org.mule.runtime.api.meta.model.ExtensionModel;
import org.mule.runtime.api.meta.model.error.ErrorModel;
import org.mule.runtime.api.meta.model.operation.OperationModel;
import org.mule.runtime.extension.api.annotation.Extension;
import org.mule.runtime.extension.api.annotation.Operations;
import org.mule.runtime.extension.api.annotation.error.ErrorTypeProvider;
import org.mule.runtime.extension.api.annotation.error.ErrorTypes;
import org.mule.runtime.extension.api.annotation.error.Throws;
import org.mule.runtime.extension.api.error.ErrorTypeDefinition;
import org.mule.runtime.extension.api.error.MuleErrors;
import org.mule.runtime.extension.api.exception.IllegalModelDefinitionException;
import org.mule.tck.junit4.AbstractMuleTestCase;
import org.mule.test.heisenberg.extension.HeisenbergErrors;
import org.mule.test.heisenberg.extension.HeisenbergExtension;
import java.util.Optional;
import java.util.Set;
import org.junit.Test;
public class ErrorsDeclarationEnricherTestCase extends AbstractMuleTestCase {
private static final String HEISENBERG = "HEISENBERG";
private static final String MULE_NAMESPACE = "MULE";
private static final String TYPE = "type";
private ExtensionModel extensionModel;
@Test
public void detectErrorTypesCycleDependency() {
assertThatThrownBy(() -> extensionModel = loadExtension(HeisenbergWithCyclicErrorTypes.class))
.hasMessageContaining("Cyclic Error Types reference detected")
.isInstanceOf(IllegalModelDefinitionException.class);
}
@Test
public void operationsWithConnectionsThrowsConnectivityError() {
extensionModel = loadExtension(HeisenbergExtension.class);
OperationModel callSaul =
getNamedObject(extensionModel.getConfigurationModel("config").get().getOperationModels(), "callSaul");
Set<ErrorModel> errorTypesIdentifiers = callSaul.getErrorModels();
assertThat(errorTypesIdentifiers, hasItem(hasProperty(TYPE, is(CONNECTIVITY_ERROR_IDENTIFIER))));
}
@Test
public void extensionErrorsInheritFromMuleErrors() {
extensionModel = loadExtension(HeisenbergExtension.class);
OperationModel cureCancer = getNamedObject(extensionModel.getOperationModels(), "cureCancer");
assertThat(cureCancer.getErrorModels(), hasItem(hasProperty(TYPE, is(HEALTH.getType()))));
Optional<ErrorModel> healthError =
extensionModel.getErrorModels().stream()
.filter(errorModel -> errorModel.getType().equals(HEALTH.getType())).findFirst();
assertThat(healthError.isPresent(), is(true));
Optional<ErrorModel> optConnectivityError = healthError.get().getParent();
assertThat(optConnectivityError.isPresent(), is(true));
ErrorModel connectivityError = optConnectivityError.get();
assertThat(connectivityError.getType(), is(CONNECTIVITY_ERROR_IDENTIFIER));
assertThat(connectivityError.getNamespace(), is(HEISENBERG));
Optional<ErrorModel> optMuleConnectivityError = connectivityError.getParent();
assertThat(optMuleConnectivityError.isPresent(), is(true));
ErrorModel muleConnectivityError = optMuleConnectivityError.get();
assertThat(muleConnectivityError.getType(), is(CONNECTIVITY_ERROR_IDENTIFIER));
assertThat(muleConnectivityError.getNamespace(), is(MULE_NAMESPACE));
}
@Test
public void operationUsesANotMappedErrorType() {
assertThatThrownBy(() -> extensionModel = loadExtension(HeisenbergWithNotMappedErrorType.class))
.hasMessageContaining("Invalid operation throws detected, the extension declared to throw errors")
.isInstanceOf(IllegalModelDefinitionException.class);
}
@Test
public void orphanErrorsUsesAnyAsParent() {
extensionModel = loadExtension(HeisenbergWithOrphanErrors.class);
ErrorModel errorModel =
extensionModel.getErrorModels().stream().filter(error -> error.getType().equals("HEALTH")).findFirst().get();
assertThat(errorModel.getNamespace(), is(HEISENBERG));
Optional<ErrorModel> anyExtensionError = errorModel.getParent();
assertThat(anyExtensionError.isPresent(), is(true));
assertThat(anyExtensionError.get().getType(), is(ModuleErrors.ANY.getType()));
assertThat(anyExtensionError.get().getNamespace(), is(HEISENBERG));
Optional<ErrorModel> muleAnyError = anyExtensionError.get().getParent();
assertThat(muleAnyError.isPresent(), is(true));
assertThat(muleAnyError.get().getType(), is(MuleErrors.ANY.getType()));
assertThat(muleAnyError.get().getNamespace(), is(MULE_NAMESPACE));
}
@Test
public void operationThrowsOverridesExtensionThrows() {
extensionModel = loadExtension(HeisenbergWithOperationThrows.class);
OperationModel someOperation = extensionModel.getOperationModel("someOperation").get();
Optional<ErrorModel> operationError = someOperation.getErrorModels().stream()
.filter(errorModel -> errorModel.getType().equals(OPERATION.getType())).findFirst();
assertThat(operationError.isPresent(), is(true));
}
@Test
public void operationInheritsExtensionErrorThrows() {
extensionModel = loadExtension(HeisenbergWithExtensionThrows.class);
OperationModel someOperation = extensionModel.getOperationModel("someOperation").get();
Optional<ErrorModel> operationError = someOperation.getErrorModels().stream()
.filter(errorModel -> errorModel.getType().equals(EXTENSION.getType())).findFirst();
assertThat(operationError.isPresent(), is(true));
}
@ErrorTypes(CyclicErrorTypes.class)
@Extension(name = "Heisenberg", description = EXTENSION_DESCRIPTION)
public static class HeisenbergWithCyclicErrorTypes extends HeisenbergExtension {
}
@Extension(name = "Heisenberg", description = EXTENSION_DESCRIPTION)
@Operations(InvalidErrorOperations.class)
@ErrorTypes(HeisenbergErrors.class)
public static class HeisenbergWithNotMappedErrorType extends HeisenbergExtension {
}
@Extension(name = "Heisenberg", description = EXTENSION_DESCRIPTION)
@ErrorTypes(OrphanErrorTypes.class)
public static class HeisenbergWithOrphanErrors extends HeisenbergExtension {
}
@Extension(name = "Heisenberg", description = EXTENSION_DESCRIPTION)
@ErrorTypes(LevelErrorTypes.class)
@Throws(ExtensionLevelErrorTypeProvider.class)
@Operations(OperationWithThrows.class)
public static class HeisenbergWithOperationThrows extends HeisenbergExtension {
}
@Extension(name = "Heisenberg", description = EXTENSION_DESCRIPTION)
@ErrorTypes(LevelErrorTypes.class)
@Throws(ExtensionLevelErrorTypeProvider.class)
@Operations(OperationWithOutThrows.class)
public static class HeisenbergWithExtensionThrows extends HeisenbergExtension {
}
private static class InvalidErrorOperations {
@Throws(ErrorTypeProviderWithInvalidErrors.class)
public void someOperation() {
}
public static class ErrorTypeProviderWithInvalidErrors implements ErrorTypeProvider {
@Override
public Set<ErrorTypeDefinition> getErrorTypes() {
return singleton(MuleErrors.CONNECTIVITY);
}
}
}
public enum CyclicErrorTypes implements ErrorTypeDefinition<CyclicErrorTypes> {
TYPE_A {
@Override
public Optional<ErrorTypeDefinition<?>> getParent() {
return Optional.of(TYPE_B);
}
},
TYPE_B {
@Override
public Optional<ErrorTypeDefinition<?>> getParent() {
return Optional.of(TYPE_C);
}
},
TYPE_C {
@Override
public Optional<ErrorTypeDefinition<?>> getParent() {
return Optional.of(TYPE_A);
}
}
}
private static class OperationWithThrows {
@Throws(OperationLevelErrorTypeProvider.class)
public void someOperation() {
}
}
private static class OperationWithOutThrows {
public void someOperation() {
}
}
public static class OperationLevelErrorTypeProvider implements ErrorTypeProvider {
@Override
public Set<ErrorTypeDefinition> getErrorTypes() {
return singleton(OPERATION);
}
}
public static class ExtensionLevelErrorTypeProvider implements ErrorTypeProvider {
@Override
public Set<ErrorTypeDefinition> getErrorTypes() {
return singleton(EXTENSION);
}
}
public enum OrphanErrorTypes implements ErrorTypeDefinition<OrphanErrorTypes> {
HEALTH, CONNECTIVITY, OAUTH2
}
}