/*
* 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;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.finra.herd.dao.impl.MockJdbcOperations;
import org.finra.herd.model.api.xml.JdbcExecutionRequest;
import org.finra.herd.model.api.xml.JdbcExecutionResponse;
import org.finra.herd.model.api.xml.JdbcStatement;
import org.finra.herd.model.api.xml.JdbcStatementResultSetRow;
import org.finra.herd.model.api.xml.JdbcStatementStatus;
import org.finra.herd.model.api.xml.JdbcStatementType;
import org.finra.herd.model.api.xml.S3PropertiesLocation;
import org.finra.herd.model.dto.ConfigurationValue;
/**
* Test cases for {@link org.finra.herd.service.JdbcService}
*/
public class JdbcServiceTest extends AbstractServiceTest
{
@Autowired
private JdbcServiceTestHelper jdbcServiceTestHelper;
/**
* Use case where a single successful statement is executed.
*/
@Test
public void testExecuteJdbcStatementSuccess()
{
// Get test request
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
// Execute
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
// Assert results
Assert.assertNull("JDBC connection is not null", jdbcExecutionResponse.getConnection());
Assert.assertEquals("JDBC statements size", jdbcExecutionRequest.getStatements().size(), jdbcExecutionResponse.getStatements().size());
{
JdbcStatement expectedJdbcStatement = jdbcExecutionRequest.getStatements().get(0);
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(0);
Assert.assertEquals("JDBC statement [0] type", expectedJdbcStatement.getType(), actualJdbcStatement.getType());
Assert.assertEquals("JDBC statement [0] sql", expectedJdbcStatement.getSql(), actualJdbcStatement.getSql());
Assert.assertEquals("JDBC statement [0] status", JdbcStatementStatus.SUCCESS, actualJdbcStatement.getStatus());
Assert.assertEquals("JDBC statement [0] result", "1", actualJdbcStatement.getResult());
}
}
/**
* Use case where 3 statements are requested to be executed, but the 2nd is erroneous. The first statement should result in SUCCESS. The second should
* result in ERROR, with appropriate result message. The third should result in SKIPPED with no result.
*/
@Test
public void testExecuteJdbcStatementError()
{
// Create test request
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
// First statement already included
// Second statement uses case 2 which throws an error
jdbcExecutionRequest.getStatements().add(new JdbcStatement(JdbcStatementType.UPDATE, MockJdbcOperations.CASE_2_SQL, null, null, null, null, null));
jdbcExecutionRequest.getStatements().add(new JdbcStatement(JdbcStatementType.UPDATE, MockJdbcOperations.CASE_1_SQL, null, null, null, null, null));
// Execute
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
// Assert results
Assert.assertNull("JDBC connection is null", jdbcExecutionResponse.getConnection());
Assert.assertEquals("JDBC statements size", jdbcExecutionRequest.getStatements().size(), jdbcExecutionResponse.getStatements().size());
{
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(0);
Assert.assertEquals("JDBC statement [0] status", JdbcStatementStatus.SUCCESS, actualJdbcStatement.getStatus());
}
{
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(1);
Assert.assertEquals("JDBC statement [1] status", JdbcStatementStatus.ERROR, actualJdbcStatement.getStatus());
Assert.assertNull("JDBC statement [1] result is not null", actualJdbcStatement.getResult());
Assert.assertEquals("JDBC statement [1] error message", "java.sql.SQLException: test DataIntegrityViolationException cause",
actualJdbcStatement.getErrorMessage());
}
{
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(2);
Assert.assertEquals("JDBC statement [2] status", JdbcStatementStatus.SKIPPED, actualJdbcStatement.getStatus());
Assert.assertNull("JDBC statement [2] result is not null", actualJdbcStatement.getResult());
}
}
/**
* Test case where statements result in errors, but continue on error flag is set to true for those statements. The subsequent statements should continue
* executing.
*/
@Test
public void testExecuteJdbcStatementErrorContinueOnError()
{
// Create test request
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
// First statement already included
// Second statement uses case 2 which throws an error
jdbcExecutionRequest.getStatements().add(new JdbcStatement(JdbcStatementType.UPDATE, MockJdbcOperations.CASE_2_SQL, true, null, null, null, null));
jdbcExecutionRequest.getStatements().add(new JdbcStatement(JdbcStatementType.UPDATE, MockJdbcOperations.CASE_1_SQL, false, null, null, null, null));
// Execute
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
// Assert results
Assert.assertNull("JDBC connection is not null", jdbcExecutionResponse.getConnection());
Assert.assertEquals("JDBC statements size", jdbcExecutionRequest.getStatements().size(), jdbcExecutionResponse.getStatements().size());
{
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(0);
Assert.assertEquals("JDBC statement [0] status", JdbcStatementStatus.SUCCESS, actualJdbcStatement.getStatus());
}
{
JdbcStatement expectedJdbcStatement = jdbcExecutionResponse.getStatements().get(1);
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(1);
Assert.assertEquals("JDBC statement [1] continue on error", expectedJdbcStatement.isContinueOnError(), actualJdbcStatement.isContinueOnError());
Assert.assertEquals("JDBC statement [1] status", JdbcStatementStatus.ERROR, actualJdbcStatement.getStatus());
Assert.assertNull("JDBC statement [1] result is not null", actualJdbcStatement.getResult());
Assert.assertEquals("JDBC statement [1] error message", "java.sql.SQLException: test DataIntegrityViolationException cause",
actualJdbcStatement.getErrorMessage());
}
{
JdbcStatement expectedJdbcStatement = jdbcExecutionResponse.getStatements().get(2);
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(2);
Assert.assertEquals("JDBC statement [2] status", expectedJdbcStatement.isContinueOnError(), actualJdbcStatement.isContinueOnError());
Assert.assertEquals("JDBC statement [2] status", JdbcStatementStatus.SUCCESS, actualJdbcStatement.getStatus());
}
}
/**
* Test case where user specifies a QUERY statement type. A proper result set should be created.
*/
@Test
public void testExecuteJdbcStatementTypeQuerySuccess()
{
// Get test request
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultQueryJdbcExecutionRequest();
JdbcStatement expectedJdbcStatement = jdbcExecutionRequest.getStatements().get(0);
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.assertEquals("JDBC statements size", 1, jdbcExecutionResponse.getStatements().size());
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(0);
Assert.assertNull("JDBC statement error message is not null", actualJdbcStatement.getErrorMessage());
Assert.assertNull("JDBC statement result not is null", actualJdbcStatement.getResult());
Assert.assertEquals("JDBC statement SQL", expectedJdbcStatement.getSql(), actualJdbcStatement.getSql());
Assert.assertEquals("JDBC statement status", JdbcStatementStatus.SUCCESS, actualJdbcStatement.getStatus());
Assert.assertEquals("JDBC statement type", expectedJdbcStatement.getType(), actualJdbcStatement.getType());
Assert.assertNotNull("JDBC statement result set is null", actualJdbcStatement.getResultSet());
Assert.assertNotNull("JDBC statement result set column names is null", actualJdbcStatement.getResultSet().getColumnNames());
Assert
.assertEquals("JDBC statement result set column names", Arrays.asList("COL1", "COL2", "COL3"), actualJdbcStatement.getResultSet().getColumnNames());
Assert.assertNotNull("JDBC statement result set rows is null", actualJdbcStatement.getResultSet().getRows());
Assert.assertEquals("JDBC statement result set rows size", 2, actualJdbcStatement.getResultSet().getRows().size());
{
JdbcStatementResultSetRow row = actualJdbcStatement.getResultSet().getRows().get(0);
Assert.assertNotNull("JDBC statement row [0] columns is null", row.getColumns());
Assert.assertEquals("JDBC statement row [0] columns", Arrays.asList("A", "B", "C"), row.getColumns());
}
{
JdbcStatementResultSetRow row = actualJdbcStatement.getResultSet().getRows().get(1);
Assert.assertNotNull("JDBC statement row [1] columns is null", row.getColumns());
Assert.assertEquals("JDBC statement row [1] columns", Arrays.asList("D", "E", "F"), row.getColumns());
}
}
/**
* Test case where user specifies a QUERY statement type and a maximum number of rows is specified in the environment.
*/
@Test
public void testExecuteJdbcStatementTypeQueryMaximumRows()
{
int expectedRowSize = 1;
try
{
Map<String, Object> overrideMap = new HashMap<>();
overrideMap.put(ConfigurationValue.JDBC_RESULT_MAX_ROWS.getKey(), expectedRowSize);
modifyPropertySourceInEnvironment(overrideMap);
}
catch (Exception e)
{
throw new RuntimeException("Error modifying environment variables", e);
}
try
{
// Get test request
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultQueryJdbcExecutionRequest();
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.assertEquals("result set row size", expectedRowSize, jdbcExecutionResponse.getStatements().get(0).getResultSet().getRows().size());
}
finally
{
try
{
restorePropertySourceInEnvironment();
}
catch (Exception e)
{
throw new RuntimeException("Error restoring environment variables. Subsequent tests may be affected.", e);
}
}
}
/**
* Test case where user specifies a QUERY statement type, but there are SQL errors. The status should be ERROR and no result set should exist in the
* result.
*/
@Test
public void testExecuteJdbcStatementTypeQueryError()
{
// Get test request
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultQueryJdbcExecutionRequest();
JdbcStatement expectedJdbcStatement = jdbcExecutionRequest.getStatements().get(0);
expectedJdbcStatement.setSql(MockJdbcOperations.CASE_2_SQL);
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.assertEquals("JDBC statements size", 1, jdbcExecutionResponse.getStatements().size());
JdbcStatement actualJdbcStatement = jdbcExecutionResponse.getStatements().get(0);
Assert.assertNotNull("JDBC statement error message", actualJdbcStatement.getErrorMessage());
Assert.assertEquals("JDBC statement error message", "java.sql.SQLException: test DataIntegrityViolationException cause",
actualJdbcStatement.getErrorMessage());
Assert.assertNull("JDBC statement result", actualJdbcStatement.getResult());
Assert.assertEquals("JDBC statement status", JdbcStatementStatus.ERROR, actualJdbcStatement.getStatus());
Assert.assertEquals("JDBC statement type", expectedJdbcStatement.getType(), actualJdbcStatement.getType());
Assert.assertNull("JDBC statement result set", actualJdbcStatement.getResultSet());
}
/**
* Parameter validation, request object is null
*/
@Test
public void testExecuteJdbcParamValidationRequestNull()
{
try
{
// Execute
jdbcService.executeJdbc(null);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC execution request is required", e.getMessage());
}
}
/**
* Parameter validation, request connection is null
*/
@Test
public void testExecuteJdbcParamValidationConnectionNull()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.setConnection(null);
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC connection is required", e.getMessage());
}
}
/**
* Parameter validation, request connection URL is empty
*/
@Test
public void testExecuteJdbcParamValidationConnectionUrlEmpty()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getConnection().setUrl(" \t\n\r");
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC connection URL is required", e.getMessage());
}
}
/**
* Parameter validation, request connection username is null. Username can be empty however, since some databases allow that.
*/
@Test
public void testExecuteJdbcParamValidationConnectionUsernameNull()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getConnection().setUsername(null);
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC connection user name is required", e.getMessage());
}
}
/**
* Parameter validation, request connection password is null. Password can be empty however, since some databases allow that.
*/
@Test
public void testExecuteJdbcParamValidationConnectionPasswordNull()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getConnection().setPassword(null);
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC connection password is required", e.getMessage());
}
}
/**
* Parameter validation, request connection database type is null.
*/
@Test
public void testExecuteJdbcParamValidationConnectionDatabaseTypeNull()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getConnection().setDatabaseType(null);
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC connection database type is required", e.getMessage());
}
}
/**
* Parameter validation, request statement list is null
*/
@Test
public void testExecuteJdbcParamValidationStatementsNull()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.setStatements(null);
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC statements are required", e.getMessage());
}
}
/**
* Parameter validation, request statement list has less statements than configured maximum.
*/
@Test
public void testExecuteJdbcParamValidationStatementsMaximumStatementsSpecified()
{
/*
* Update environment variable to use a maximum statement
*/
int maxStatements = 1;
try
{
HashMap<String, Object> overrideMap = new HashMap<>();
overrideMap.put(ConfigurationValue.JDBC_MAX_STATEMENTS.getKey(), maxStatements);
modifyPropertySourceInEnvironment(overrideMap);
}
catch (Exception e)
{
throw new RuntimeException("Error modifying environment variable.", e);
}
/*
* Add 1 more statement to the JDBC request.
* The default request should already have 1 statement.
*/
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
try
{
// Execute
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.assertNotNull("jdbcExecutionResponse", jdbcExecutionResponse);
}
finally
{
try
{
restorePropertySourceInEnvironment();
}
catch (Exception e)
{
throw new RuntimeException("Error restoring environment variables. Subsequent tests may be affected.", e);
}
}
}
/**
* Parameter validation, request statement list has more statements than configured maximum.
*/
@Test
public void testExecuteJdbcParamValidationStatementsExceedMaximum()
{
/*
* Update environment variable to use a maximum statement
*/
int maxStatements = 1;
try
{
HashMap<String, Object> overrideMap = new HashMap<>();
overrideMap.put(ConfigurationValue.JDBC_MAX_STATEMENTS.getKey(), maxStatements);
modifyPropertySourceInEnvironment(overrideMap);
}
catch (Exception e)
{
throw new RuntimeException("Error modifying environment variable.", e);
}
/*
* Add 1 more statement to the JDBC request.
* The default request should already have 1 statement.
*/
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getStatements().add(new JdbcStatement());
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert
.assertEquals("thrown exception message", "The number of JDBC statements exceeded the maximum allowed " + maxStatements + ".", e.getMessage());
}
finally
{
/*
*
*/
try
{
restorePropertySourceInEnvironment();
}
catch (Exception e)
{
throw new RuntimeException("Error restoring environment variables. Subsequent tests may be affected.", e);
}
}
}
/**
* Parameter validation, request statement list is empty
*/
@Test
public void testExecuteJdbcParamValidationStatementsEmpty()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getStatements().clear();
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC statements are required", e.getMessage());
}
}
/**
* Parameter validation, request statement type is null
*/
@Test
public void testExecuteJdbcParamValidationStatementTypeNull()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getStatements().get(0).setType(null);
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC statement [0] type is required", e.getMessage());
}
}
/**
* Parameter validation, request statement sql is empty
*/
@Test
public void testExecuteJdbcParamValidationStatementTypeSqlEmpty()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getStatements().get(0).setSql(" \t\n\r");
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC statement [0] SQL is required", e.getMessage());
}
}
@Test
public void testExecuteJdbcErrorConnection()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getStatements().get(0).setSql(MockJdbcOperations.CASE_3_SQL);
try
{
// Execute
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "java.sql.SQLException: test CannotGetJdbcConnectionException cause", e.getMessage());
}
}
/**
* When S3 properties location is specified, bucket name must not be a blank string.
*/
@Test
public void testExecuteJdbcParamValidationS3PropertiesLocationBucketNameBlank()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.setS3PropertiesLocation(new S3PropertiesLocation(BLANK_TEXT, "test_key"));
try
{
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "S3 properties location bucket name is required", e.getMessage());
}
}
/**
* When S3 properties location is specified, object key must not be a blank string.
*/
@Test
public void testExecuteJdbcParamValidationS3PropertiesLocationKeyBlank()
{
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.setS3PropertiesLocation(new S3PropertiesLocation("test_bucket", BLANK_TEXT));
try
{
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "S3 properties location key is required", e.getMessage());
}
}
/**
* If the result of replacing URL using S3 properties is blank, throws a validation error.
*/
@Test
public void testExecuteJdbcWithS3PropertiesParamUrlBlankAfterReplace()
{
String s3BucketName = "test_bucket";
String s3ObjectKey = "test_key";
String content = "foo=";
putS3Object(s3BucketName, s3ObjectKey, content);
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getConnection().setUrl("${foo}");
jdbcExecutionRequest.setS3PropertiesLocation(new S3PropertiesLocation(s3BucketName, s3ObjectKey));
try
{
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC connection URL is required", e.getMessage());
}
}
/**
* If the result of replacing SQL using S3 properties is blank, throws a validation error.
*/
@Test
public void testExecuteJdbcWithS3PropertiesParamSqlBlankAfterReplace()
{
String s3BucketName = "test_bucket";
String s3ObjectKey = "test_key";
String content = "foo=";
putS3Object(s3BucketName, s3ObjectKey, content);
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getStatements().get(0).setSql("${foo}");
jdbcExecutionRequest.setS3PropertiesLocation(new S3PropertiesLocation(s3BucketName, s3ObjectKey));
try
{
jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.fail("expected an IllegalArgumentException, but no exception was thrown");
}
catch (Exception e)
{
Assert.assertEquals("thrown exception type", IllegalArgumentException.class, e.getClass());
Assert.assertEquals("thrown exception message", "JDBC statement [0] SQL is required", e.getMessage());
}
}
/**
* Execute JDBC using S3 properties file. Unfortunately, not many assertions that can be done through the service layer. Asserts that no errors are thrown,
* and that the response SQL does not expose the secrets.
*/
@Test
public void testExecuteJdbcWithS3PropertiesSuccess()
{
String s3BucketName = "test_bucket";
String s3ObjectKey = "test_key";
String content = "foo=bar";
putS3Object(s3BucketName, s3ObjectKey, content);
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getConnection().setUrl("test_url_${foo}");
jdbcExecutionRequest.getConnection().setUsername("test_username_${foo}");
jdbcExecutionRequest.getConnection().setPassword("test_password_${foo}");
JdbcStatement jdbcStatement = jdbcExecutionRequest.getStatements().get(0);
jdbcStatement.setSql("test_sql_${foo}");
jdbcExecutionRequest.setS3PropertiesLocation(new S3PropertiesLocation(s3BucketName, s3ObjectKey));
try
{
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.assertEquals("jdbc execution response statement [0] sql", "test_sql_${foo}", jdbcExecutionResponse.getStatements().get(0).getSql());
}
catch (Exception e)
{
Assert.fail("unexpected exception was thrown. " + e);
}
}
/**
* Some JDBC exception messages echoes back parts of the SQL statement. This is problem for security if some of the variables were replaced, and may
* accidentally expose secret information in the response error message. The application should mask any values given in the properties which exist in the
* exception message.
* <p/>
* This test will use a SQL that will throw an exception, and the exception message is known. Then asserts that the value has been replaced with a mask in
* the response error message.
*/
@Test
public void testExecuteJdbcSensitiveDataIsMaskedInErrorMessage()
{
String s3BucketName = "test_bucket";
String s3ObjectKey = "test_key";
String content = "foo=DataIntegrityViolationException";
putS3Object(s3BucketName, s3ObjectKey, content);
JdbcExecutionRequest jdbcExecutionRequest = jdbcServiceTestHelper.createDefaultUpdateJdbcExecutionRequest();
jdbcExecutionRequest.getStatements().get(0).setSql(MockJdbcOperations.CASE_2_SQL);
jdbcExecutionRequest.setS3PropertiesLocation(new S3PropertiesLocation(s3BucketName, s3ObjectKey));
JdbcExecutionResponse jdbcExecutionResponse = jdbcService.executeJdbc(jdbcExecutionRequest);
Assert.assertEquals("jdbc execution response statement [0] error message", "java.sql.SQLException: test **** cause",
jdbcExecutionResponse.getStatements().get(0).getErrorMessage());
}
/**
* Puts an S3 object with the given parameters directly into S3.
*
* @param s3BucketName the S3 bucket name
* @param s3ObjectKey the S3 object key
* @param content the content of the S3 object
*/
private void putS3Object(String s3BucketName, String s3ObjectKey, String content)
{
PutObjectRequest putObjectRequest = new PutObjectRequest(s3BucketName, s3ObjectKey, new ByteArrayInputStream(content.getBytes()), new ObjectMetadata());
s3Operations.putObject(putObjectRequest, null);
}
}