/*
* Copyright 2015 herd contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.finra.herd.service.helper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.sql.DataTruncation;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import org.springframework.security.access.AccessDeniedException;
import org.activiti.engine.ActivitiClassLoadingException;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.impl.javax.el.ELException;
import org.hibernate.exception.ConstraintViolationException;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletResponse;
import org.finra.herd.model.AlreadyExistsException;
import org.finra.herd.model.MethodNotAllowedException;
import org.finra.herd.model.ObjectNotFoundException;
import org.finra.herd.model.api.xml.ErrorInformation;
import org.finra.herd.service.AbstractServiceTest;
/**
* Test the herd REST Controller Advice.
*/
public class HerdErrorInformationExceptionHandlerTest extends AbstractServiceTest
{
@Autowired
private HerdErrorInformationExceptionHandler exceptionHandler;
private static final String MESSAGE = "This is a test and is not an actual error. Please ignore.";
@Test
public void testHandleInternalServerError() throws Exception
{
// Calling handleException will log a stack trace which is normal so don't be concerned if you see it in the logs.
validateErrorInformation(exceptionHandler.handleInternalServerErrorException(new Exception(MESSAGE)), HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void testHandleInternalServerErrorNoErrorMessage() throws Exception
{
// Get the error information for an exception that has no message. In this case, the message is the exception class name.
ErrorInformation errorInformation = exceptionHandler.handleInternalServerErrorException(new NullPointerException());
// Validate the error status information, but not the message.
validateErrorInformation(errorInformation, HttpStatus.INTERNAL_SERVER_ERROR, false);
// Validate that the error message is the class name.
assertEquals(NullPointerException.class.getName(), errorInformation.getMessage());
}
@Test
public void testDataTruncationException() throws Exception
{
validateErrorInformation(
exceptionHandler.handlePersistenceException(getPersistenceException(new DataTruncation(1, true, true, 5, 10)), new MockHttpServletResponse()),
HttpStatus.BAD_REQUEST, false);
}
@Test
public void testDataTruncationExceptions() throws Exception
{
validatePersistenceException(HerdErrorInformationExceptionHandler.ORACLE_SQL_STATE_CODE_ERROR,
HerdErrorInformationExceptionHandler.ORACLE_DATA_TOO_LARGE_ERROR_CODE);
validatePersistenceException(HerdErrorInformationExceptionHandler.ORACLE_SQL_STATE_CODE_ERROR,
HerdErrorInformationExceptionHandler.ORACLE_LONG_DATA_IN_LONG_COLUMN_ERROR_CODE);
validatePersistenceException(HerdErrorInformationExceptionHandler.POSTGRES_SQL_STATE_CODE_TRUNCATION_ERROR, 0);
}
/**
* Validates that calling handlePersistenceException with a SQL exception with the given SQL state code and error code wrapped in a
* {@link PersistenceException} returns an {@link ErrorInformation} with a {@link HttpStatus#BAD_REQUEST}.
*
* @param sqlState - {@link SQLException#getSQLState()}
* @param errorCode - {@link SQLException#getErrorCode()}
*/
private void validatePersistenceException(String sqlState, int errorCode)
{
SQLException sqlException = new SQLException("Test Reason", sqlState, errorCode, null);
PersistenceException persistenceException = getPersistenceException(sqlException);
ErrorInformation errorInformation = exceptionHandler.handlePersistenceException(persistenceException, new MockHttpServletResponse());
validateErrorInformation(errorInformation, HttpStatus.BAD_REQUEST, false);
}
@Test
public void testConstraintViolationException() throws Exception
{
validateErrorInformation(exceptionHandler
.handlePersistenceException(getPersistenceException(new ConstraintViolationException(MESSAGE, null, "testConstraint")),
new MockHttpServletResponse()), HttpStatus.BAD_REQUEST, false);
validateErrorInformation(exceptionHandler.handlePersistenceException(getPersistenceException(new SQLException(MESSAGE,
HerdErrorInformationExceptionHandler.POSTGRES_SQL_STATE_CODE_FOREIGN_KEY_VIOLATION, 0)), new MockHttpServletResponse()), HttpStatus.BAD_REQUEST,
false);
}
@Test
public void testConstraintViolationExceptionNoWrap() throws Exception
{
validateErrorInformation(
exceptionHandler.handlePersistenceException(new ConstraintViolationException(MESSAGE, null, "testConstraint"), new MockHttpServletResponse()),
HttpStatus.BAD_REQUEST, false);
}
@Test
public void testPersistenceExceptionInternalServerError() throws Exception
{
validateErrorInformation(
exceptionHandler.handlePersistenceException(getPersistenceException(new RuntimeException(MESSAGE)), new MockHttpServletResponse()),
HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void testPersistenceExceptionInternalServerErrorNullException() throws Exception
{
validateErrorInformation(exceptionHandler.handlePersistenceException(null, new MockHttpServletResponse()), HttpStatus.INTERNAL_SERVER_ERROR, false);
}
@Test
public void testHandleOperationNotAllowedFound() throws Exception
{
validateErrorInformation(exceptionHandler.handleOperationNotAllowedException(new MethodNotAllowedException(MESSAGE)), HttpStatus.METHOD_NOT_ALLOWED);
}
@Test
public void testHandleNotFound() throws Exception
{
validateErrorInformation(exceptionHandler.handleNotFoundException(new ObjectNotFoundException(MESSAGE)), HttpStatus.NOT_FOUND);
}
@Test
public void testAlreadyExists() throws Exception
{
validateErrorInformation(exceptionHandler.handleConflictException(new AlreadyExistsException(MESSAGE)), HttpStatus.CONFLICT);
}
@Test
public void testIllegalArgument() throws Exception
{
validateErrorInformation(exceptionHandler.handleBadRequestException(new IllegalArgumentException(MESSAGE)), HttpStatus.BAD_REQUEST);
}
@Test
public void testActivitiExceptionBadRequest() throws Exception
{
// Test out both ActivitiClassLoadingException and ELException.
validateErrorInformation(exceptionHandler
.handleActivitiException(new ActivitiClassLoadingException(ActivitiClassLoadingException.class.getName(), new RuntimeException(MESSAGE)),
new MockHttpServletResponse()), HttpStatus.BAD_REQUEST, false);
validateErrorInformation(exceptionHandler.handleActivitiException(new ELException(MESSAGE), new MockHttpServletResponse()), HttpStatus.BAD_REQUEST);
}
@Test
public void testActivitiExceptionInternalServerError() throws Exception
{
validateErrorInformation(exceptionHandler.handleActivitiException(new ActivitiException(MESSAGE), new MockHttpServletResponse()),
HttpStatus.INTERNAL_SERVER_ERROR, false);
}
@Test
public void testAccessDeniedException() throws Exception
{
validateErrorInformation(exceptionHandler.handleAccessDeniedException(new AccessDeniedException(MESSAGE)), HttpStatus.FORBIDDEN, false);
}
@Test
public void testIsReportableErrorThrowable() throws Exception
{
assertTrue(exceptionHandler.isReportableError(new Throwable(MESSAGE)));
}
@Test
public void testGetErrorWithCause() throws Exception
{
Exception ex = new Exception();
ErrorInformation errorInformation = exceptionHandler.handleBadRequestException(ex);
assertTrue(errorInformation.getMessageDetails().size() == 0);
ex = new Exception(new Exception("cause_1_exception", new Exception("cause_2_exception")));
errorInformation = exceptionHandler.handleBadRequestException(ex);
assertTrue(errorInformation.getMessageDetails().size() == 2);
assertEquals("cause_1_exception", errorInformation.getMessageDetails().get(0));
assertEquals("cause_2_exception", errorInformation.getMessageDetails().get(1));
}
/**
* Validate the error information by checking the message, the error status code, and the error status description.
*
* @param errorInformation the error information to validate.
* @param expectedStatus the expected status.
*/
private void validateErrorInformation(ErrorInformation errorInformation, HttpStatus expectedStatus)
{
validateErrorInformation(errorInformation, expectedStatus, true);
}
/**
* Gets a new persistence exception that wraps the passed in child exception.
*
* @param childException the child exception.
*
* @return the persistence exception.
*/
private PersistenceException getPersistenceException(Exception childException)
{
return new PersistenceException("Persistence Error", childException);
}
/**
* Validate the error information by checking the message, the error status code, and the error status description.
*
* @param errorInformation the error information to validate.
* @param expectedStatus the expected status.
* @param checkMessage If true, the message will be checked. Otherwise, it won't.
*/
private void validateErrorInformation(ErrorInformation errorInformation, HttpStatus expectedStatus, boolean checkMessage)
{
if (checkMessage)
{
assertEquals(MESSAGE, errorInformation.getMessage());
}
assertEquals(expectedStatus.value(), errorInformation.getStatusCode());
assertEquals(expectedStatus.getReasonPhrase(), errorInformation.getStatusDescription());
}
}