/* * 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()); } }