/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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.pentaho.di.trans.steps.tablecompare;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.pentaho.di.TestUtilities;
import org.pentaho.di.core.KettleEnvironment;
import org.pentaho.di.core.RowMetaAndData;
import org.pentaho.di.core.database.Database;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LoggingObjectInterface;
import org.pentaho.di.core.logging.LoggingObjectType;
import org.pentaho.di.core.logging.SimpleLoggingObject;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.trans.RowStepCollector;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.TransTestFactory;
public class TableCompareTest {
public static String PKG = "blackbox/tests/trans/steps/tablecompare/";
static LoggingObjectInterface log;
static DatabaseMeta databaseMeta;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
KettleEnvironment.init();
log = new SimpleLoggingObject( "junit", LoggingObjectType.GENERAL, null );
databaseMeta =
new DatabaseMeta( "TableCompare", "Hypersonic", "JDBC", null, "mem:HSQLDB-JUNIT-LOGJOB", null, null, null );
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Before
public void setUp() throws Exception {
InputStream input = TableCompareTest.class.getClassLoader().getResourceAsStream( PKG + "PDI-7255.sql" );
String sql = TestUtilities.getStringFromInput( input );
Database db = new Database( log, databaseMeta );
db.connect();
db.execStatements( sql );
db.commit( true );
db.disconnect();
}
static final String schema = "public";
static String ehlkd = "ErrorHandlingKeyDescIn";
static String ehlrvi = "ErrorHandlingRefValueIn";
static String ehlcvif = "ErrorHandlingCompareValueInF";
static String schemaName = "schema";
static String reference = "reference";
static String compare = "compare";
static String key = "key";
static String exclude = "exclude";
private RowMetaInterface getRowMeta() {
RowMetaInterface rm = new RowMeta();
ValueMetaInterface[] valuesMeta =
{
// fields to handle error information
new ValueMetaString( ehlkd ),
new ValueMetaString( ehlrvi ),
new ValueMetaString( ehlcvif ),
// fields to handle connection properties
new ValueMetaString( schemaName ), new ValueMetaString( reference ),
new ValueMetaString( compare ), new ValueMetaString( key ),
new ValueMetaString( exclude ) };
for ( int i = 0; i < valuesMeta.length; i++ ) {
rm.addValueMeta( valuesMeta[i] );
}
return rm;
}
// used to test PDI-7255
private Object[] getData1() {
Object[] r1 = new Object[] { "", "", "", schema, reference, compare, "key1", "" };
return r1;
}
// used to test complex keys comparison
private Object[] getData2() {
Object[] r2 = new Object[] { "", "", "", schema, reference, compare, "key1, key2", "" };
return r2;
}
// used to test value comparison results
private Object[] getData3() {
Object[] r2 = new Object[] { "", "", "", schema, reference, compare, "key1", "" };
return r2;
}
// used to test value comparison results, but fields with values that does not match are excluded:
private Object[] getData4() {
Object[] r2 = new Object[] { "", "", "", schema, reference, compare, "key1", "key2" };
return r2;
}
/**
* PDI-7255 - The table compare step reports incorrect record as missing
*
* @throws KettleException
* @throws IOException
*/
@Test
public void testMissedReferenceLinesAreRepored() throws KettleException, IOException {
// prepare database
executeSqlPrecondition( "PDI-7255.sql" );
TableCompareMeta meta = getTableCompareMeta();
// prepare input date
List<RowMetaAndData> inputData = new ArrayList<RowMetaAndData>();
inputData.add( new RowMetaAndData( getRowMeta(), getData1() ) );
// execute transformations
TransMeta trMeta = TransTestFactory.generateTestTransformationError( null, meta, "junit" );
Map<String, RowStepCollector> result = TransTestFactory
.executeTestTransformationError( trMeta, "junit", inputData );
// check the results
List<RowMetaAndData> read = result.get( TransTestFactory.DUMMY_STEPNAME ).getRowsRead();
Assert.assertTrue( "Step achieve comparsion data", read.size() == 1 );
RowMetaAndData row = read.get( 0 );
Assert.assertEquals( "Number of errors", 1, row.getInteger( 8 ).intValue() );
Assert.assertEquals( "Reference table row count", 3, row.getInteger( 9 ).intValue() );
Assert.assertEquals( "Compare table row count", 2, row.getInteger( 10 ).intValue() );
Assert.assertEquals( "Number of left joins errors", 0, row.getInteger( 11 ).intValue() );
Assert.assertEquals( "Number of inner joins errors", 0, row.getInteger( 12 ).intValue() );
Assert.assertEquals( "Number of right joins errors", 1, row.getInteger( 13 ).intValue() );
List<RowMetaAndData> errors = result.get( TransTestFactory.ERROR_STEPNAME ).getRowsRead();
Assert.assertTrue( "One error output to negative step", errors.size() == 1 );
row = errors.get( 0 );
Assert.assertEquals( "Reported one missing key", "KEY1 = '2'", row.getString( ehlkd, null ) );
}
/**
* Test table compare test can handle complex keys comparison
*
* @throws IOException
* @throws KettleException
*/
@Test
public void testComplexKeysComparsion() throws IOException, KettleException {
executeSqlPrecondition( "complex_key_test.sql" );
TableCompareMeta meta = getTableCompareMeta();
List<RowMetaAndData> inputData = new ArrayList<RowMetaAndData>();
inputData.add( new RowMetaAndData( getRowMeta(), getData2() ) );
TransMeta trMeta = TransTestFactory.generateTestTransformationError( null, meta, "junit" );
Map<String, RowStepCollector> result = TransTestFactory
.executeTestTransformationError( trMeta, "junit", inputData );
List<RowMetaAndData> read = result.get( TransTestFactory.DUMMY_STEPNAME ).getRowsRead();
List<RowMetaAndData> errors = result.get( TransTestFactory.ERROR_STEPNAME ).getRowsRead();
Assert.assertEquals( "One row passed to positive step", 1, read.size() );
Assert.assertEquals( "Two rows passed to negative step", 2, errors.size() );
// check positive step output
RowMetaAndData row = read.get( 0 );
Assert.assertEquals( "Number errors", 2, row.getInteger( 8 ).intValue() );
Assert.assertEquals( "Reference table row count", 4, row.getInteger( 9 ).intValue() );
Assert.assertEquals( "Compare table row count", 4, row.getInteger( 10 ).intValue() );
Assert.assertEquals( "Number of left joins errors", 1, row.getInteger( 11 ).intValue() );
Assert.assertEquals( "Number of inner joins errors", 0, row.getInteger( 12 ).intValue() );
Assert.assertEquals( "Number of right joins errors", 1, row.getInteger( 13 ).intValue() );
// check error step output
row = errors.get( 0 );
Assert.assertEquals( "error composite key is mentioned", "KEY1 = '2' and KEY2 = '1'", row.getString( 0, null ) );
row = errors.get( 1 );
Assert.assertEquals( "error composite key is mentioned", "KEY1 = '2' and KEY2 = '2'", row.getString( 0, null ) );
}
/**
* Test compare table if reference table is empty
*
* @throws IOException
* @throws KettleException
*/
@Test
public void testValueNotExistsReference() throws IOException, KettleException {
executeSqlPrecondition( "compare_only.sql" );
TableCompareMeta meta = getTableCompareMeta();
List<RowMetaAndData> inputData = new ArrayList<RowMetaAndData>();
inputData.add( new RowMetaAndData( getRowMeta(), getData3() ) );
TransMeta trMeta = TransTestFactory.generateTestTransformationError( null, meta, "junit" );
Map<String, RowStepCollector> result = TransTestFactory
.executeTestTransformationError( trMeta, "junit", inputData );
List<RowMetaAndData> read = result.get( TransTestFactory.DUMMY_STEPNAME ).getRowsRead();
List<RowMetaAndData> errors = result.get( TransTestFactory.ERROR_STEPNAME ).getRowsRead();
Assert.assertEquals( "One row passed to positive output", 1, read.size() );
RowMetaAndData row = read.get( 0 );
Assert.assertEquals( "Errors reported", 4, row.getInteger( 8 ).intValue() );
Assert.assertEquals( "Reference table row count", 0, row.getInteger( 9 ).intValue() );
Assert.assertEquals( "Compare table row count", 4, row.getInteger( 10 ).intValue() );
Assert.assertEquals( "Number of left joins errors", 0, row.getInteger( 11 ).intValue() );
Assert.assertEquals( "Number of inner joins errors", 0, row.getInteger( 12 ).intValue() );
Assert.assertEquals( "Number of right joins errors", 4, row.getInteger( 13 ).intValue() );
Assert.assertEquals( "4 error rows passed to error output", 4, errors.size() );
}
/**
* Test compare table reference table is empty
*
* @throws IOException
* @throws KettleException
*/
@Test
public void testValueNotExistedInCompare() throws IOException, KettleException {
executeSqlPrecondition( "reference_only.sql" );
TableCompareMeta meta = getTableCompareMeta();
List<RowMetaAndData> inputData = new ArrayList<RowMetaAndData>();
inputData.add( new RowMetaAndData( getRowMeta(), getData3() ) );
TransMeta trMeta = TransTestFactory.generateTestTransformationError( null, meta, "junit" );
Map<String, RowStepCollector> result = TransTestFactory
.executeTestTransformationError( trMeta, "junit", inputData );
List<RowMetaAndData> read = result.get( TransTestFactory.DUMMY_STEPNAME ).getRowsRead();
List<RowMetaAndData> errors = result.get( TransTestFactory.ERROR_STEPNAME ).getRowsRead();
RowMetaAndData row = read.get( 0 );
Assert.assertEquals( "Errors reported", 4, row.getInteger( 8 ).intValue() );
Assert.assertEquals( "Reference table row count", 4, row.getInteger( 9 ).intValue() );
Assert.assertEquals( "Compare table row count", 0, row.getInteger( 10 ).intValue() );
Assert.assertEquals( "Number of left joins errors", 4, row.getInteger( 11 ).intValue() );
Assert.assertEquals( "Number of inner joins errors", 0, row.getInteger( 12 ).intValue() );
Assert.assertEquals( "Number of right joins errors", 0, row.getInteger( 13 ).intValue() );
Assert.assertEquals( "4 error rows passed to error output", 4, errors.size() );
}
/**
* Test that step reports value comparison errors
*
* @throws IOException
* @throws KettleException
*/
@Test
public void testValueComparsion() throws IOException, KettleException {
executeSqlPrecondition( "complex_key_test.sql" );
TableCompareMeta meta = getTableCompareMeta();
List<RowMetaAndData> inputData = new ArrayList<RowMetaAndData>();
inputData.add( new RowMetaAndData( getRowMeta(), getData3() ) );
TransMeta trMeta = TransTestFactory.generateTestTransformationError( null, meta, "junit" );
Map<String, RowStepCollector> result = TransTestFactory
.executeTestTransformationError( trMeta, "junit", inputData );
List<RowMetaAndData> read = result.get( TransTestFactory.DUMMY_STEPNAME ).getRowsRead();
List<RowMetaAndData> errors = result.get( TransTestFactory.ERROR_STEPNAME ).getRowsRead();
Assert.assertEquals( "One row passed to positive output", 1, read.size() );
Assert.assertEquals( "One row passed to negative output", 1, errors.size() );
// check error is properly reported:
RowMetaAndData row = read.get( 0 );
Assert.assertEquals( "One errors reported", 1, row.getInteger( 8 ).intValue() );
Assert.assertEquals( "Reference table row count", 4, row.getInteger( 9 ).intValue() );
Assert.assertEquals( "Compare table row count", 4, row.getInteger( 10 ).intValue() );
Assert.assertEquals( "Number of left joins errors", 0, row.getInteger( 11 ).intValue() );
Assert.assertEquals( "Number of inner joins errors", 1, row.getInteger( 12 ).intValue() );
Assert.assertEquals( "Number of right joins errors", 0, row.getInteger( 13 ).intValue() );
row = errors.get( 0 );
Assert.assertEquals( "Reported key for not match value", "KEY1 = '2'", row.getString( ehlkd, null ) );
Assert.assertEquals( "Reported reference table value", "2", row.getString( ehlrvi, null ) );
Assert.assertEquals( "Reported compare table value", "1", row.getString( ehlcvif, null ) );
}
/**
* Test that step can ignore excluded values during comparison
*
* @throws IOException
* @throws KettleException
*/
@Test
public void testValueExcludeComparsion() throws IOException, KettleException {
executeSqlPrecondition( "complex_key_test.sql" );
TableCompareMeta meta = getTableCompareMeta();
List<RowMetaAndData> inputData = new ArrayList<RowMetaAndData>();
inputData.add( new RowMetaAndData( getRowMeta(), getData4() ) );
TransMeta trMeta = TransTestFactory.generateTestTransformationError( null, meta, "junit" );
Map<String, RowStepCollector> result = TransTestFactory
.executeTestTransformationError( trMeta, "junit", inputData );
List<RowMetaAndData> read = result.get( TransTestFactory.DUMMY_STEPNAME ).getRowsRead();
List<RowMetaAndData> errors = result.get( TransTestFactory.ERROR_STEPNAME ).getRowsRead();
Assert.assertEquals( "There is no errors reported", 0, errors.size() );
RowMetaAndData row = read.get( 0 );
Assert.assertEquals( "No errors reported", 0, row.getInteger( 8 ).intValue() );
Assert.assertEquals( "Reference table row count", 4, row.getInteger( 9 ).intValue() );
Assert.assertEquals( "Compare table row count", 4, row.getInteger( 10 ).intValue() );
Assert.assertEquals( "Number of left joins errors", 0, row.getInteger( 11 ).intValue() );
Assert.assertEquals( "Number of inner joins errors", 0, row.getInteger( 12 ).intValue() );
Assert.assertEquals( "Number of right joins errors", 0, row.getInteger( 13 ).intValue() );
}
private TableCompareMeta getTableCompareMeta() {
TableCompareMeta meta = new TableCompareMeta();
meta.setDefault();
// configure step
meta.setReferenceConnection( databaseMeta );
meta.setCompareConnection( databaseMeta );
meta.setReferenceSchemaField( schemaName );
meta.setCompareSchemaField( schemaName );
// set table names
meta.setReferenceTableField( reference );
meta.setCompareTableField( compare );
meta.setKeyFieldsField( key );
meta.setExcludeFieldsField( exclude );
meta.setKeyDescriptionField( ehlkd );
meta.setValueReferenceField( ehlrvi );
meta.setValueCompareField( ehlcvif );
return meta;
}
private void executeSqlPrecondition( String sqlFile ) throws IOException, KettleDatabaseException {
String path = PKG + sqlFile;
InputStream input = TableCompareTest.class.getClassLoader().getResourceAsStream( PKG + sqlFile );
if ( input == null ) {
throw new IOException( "Resource not found in classpath: " + path );
}
String sql = TestUtilities.getStringFromInput( input );
Database db = new Database( log, databaseMeta );
db.connect();
db.execStatements( sql );
db.commit( true );
db.disconnect();
}
}