All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.etlunit.feature.database.DbAssertExtender Maven / Gradle / Ivy

There is a newer version: 1.6.9
Show newest version
package org.etlunit.feature.database;

import org.etlunit.*;
import org.etlunit.context.VariableContext;
import org.etlunit.feature.Feature;
import org.etlunit.feature.RuntimeOption;
import org.etlunit.feature.assertion.Assertion;
import org.etlunit.io.file.*;
import org.etlunit.feature.extend.Extender;
import org.etlunit.parser.ETLTestOperation;
import org.etlunit.parser.ETLTestValueObject;
import org.etlunit.parser.ETLTestValueObjectBuilder;
import org.etlunit.util.IOUtils;

import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.io.IOException;
import java.sql.Types;
import java.util.*;

public class DbAssertExtender implements Extender, AssertOperationProcessor
{
	private DataFileManager dataFileManager;

	private DiffManager diffManager;

	private final DatabaseFeatureModule parent;
	private final DatabaseConfiguration databaseConfiguration;
	private DatabaseRuntimeSupport databaseRuntimeSupport;

	private boolean refreshAssertionData = false;

	protected Log applicationLog;
	private Map columnTypes;
	private List columns;

	public DbAssertExtender(DatabaseFeatureModule parent)
	{
		this.parent = parent;
		databaseConfiguration = parent.getDatabaseConfiguration();

		columns = Arrays.asList("ROW_NUMBER", "COLUMN_NAME", "SOURCE", "TARGET");

		columnTypes = new HashMap();

		columnTypes.put("ROW_NUMBER", Types.INTEGER);
		columnTypes.put("COLUMN_NAME", Types.VARCHAR);
		columnTypes.put("SOURCE", Types.VARCHAR);
		columnTypes.put("TARGET", Types.VARCHAR);
	}

	@Inject
	public void receiveDataFileManager(DataFileManager manager)
	{
		dataFileManager = manager;
	}

	@Inject
	public void setExportSAssertionData(@Named("database.refreshAssertionData") RuntimeOption option)
	{
		refreshAssertionData = option.isEnabled();

		applicationLog.info(refreshAssertionData ? "Refreshing assertion data" : "Not refreshing assertion data");
	}

	@Inject
	public void setApplicationLog(@Named("applicationLog") Log log)
	{
		applicationLog = log;
	}

	@Inject
	public void setDatabaseRuntimeSupport(DatabaseRuntimeSupport databaseRuntimeSupport)
	{
		this.databaseRuntimeSupport = databaseRuntimeSupport;
	}

	@Inject
	public void receiveDiffManager(DiffManager reporter)
	{
		diffManager = reporter;
	}

	public boolean canHandleRequest(ETLTestOperation operation, ETLTestValueObject operands, VariableContext context, ExecutionContext executionContext)
	{
		return operands.query("source-table") != null;
	}

	public ClassResponder.action_code process(ETLTestOperation op, ETLTestValueObject operands, VariableContext context, ExecutionContext executionContext) throws TestAssertionFailure, TestExecutionError, TestWarning
	{
		return action_code.defer;
	}

	public Feature getFeature()
	{
		return parent;
	}

	public action_code processAssert(AssertOperation operation, ETLTestOperation op, ETLTestValueObject obj, VariableContext context, ExecutionContext econtext) throws TestAssertionFailure, TestExecutionError, TestWarning
	{
		AssertOperation.assertionMode_enum a_mode = operation.getAssertionMode();
		final Assertion.assertion_mode comparisonMode;

		if (a_mode == null)
		{
			comparisonMode = Assertion.assertion_mode.equals;
		}
		else
		{
			switch (a_mode)
			{
				case equals:
					comparisonMode = Assertion.assertion_mode.equals;
					break;
				case empty:
					comparisonMode = Assertion.assertion_mode.empty;
					break;
				case notEmpty:
					comparisonMode = Assertion.assertion_mode.not_empty;
					break;
				default:
					throw new TestExecutionError("Invalid assertion mode: " + a_mode, DatabaseConstants.ERR_INVALID_ASSERTION_MODE);
			}
		}

		// check the runtime option for refresh data.  If so, convert this call into an extract operation for the assert equals case
		if (refreshAssertionData && comparisonMode == Assertion.assertion_mode.equals)
		{
			ETLTestValueObject copy = obj.copy();

			ETLTestValueObjectBuilder cbuilder = new ETLTestValueObjectBuilder(copy);

			// get rid of assertion mode and failure-id
			if (copy.query(AssertOperation.ASSERTIONMODE_JSON_NAME) != null)
			{
				cbuilder.removeKey(AssertOperation.ASSERTIONMODE_JSON_NAME);
			}

			if (copy.query(AssertOperation.FAILUREID_JSON_NAME) != null)
			{
				cbuilder.removeKey(AssertOperation.FAILUREID_JSON_NAME);
			}

			copy = cbuilder.toObject();

			ETLTestOperation extract_op = op.createSibling("extract", copy);

			econtext.process(extract_op, context);

			return ClassResponder.action_code.handled;
		}

		String sourceTable = operation.getSourceTable();
		String failureId = operation.getFailureId();

		String qualifiedName = sourceTable;

		String schema = operation.getSourceSchema();

		if (schema != null)
		{
			qualifiedName = schema + "." + sourceTable;
		}

		DatabaseClassListener.ConnectionMode connMode = DatabaseClassListener.getConnection(parent, obj, context, op.getTestMethod()).get(0);

		final DatabaseConnection conn = connMode.conn;

		final String mode = connMode.getMode();

		// now send it off to the implementation
		DatabaseImplementation impl = DatabaseClassListener.getDatabaseImplementation(parent, conn);

		// dispatch this as an operation (even though this feature will ultimately process)
		ETLTestValueObject copy = obj.copy();

		String tar = operation.getTarget();

		File target;

		// create a target file attribute for our extract
		if (tar != null)
		{
			target = databaseRuntimeSupport.getGeneratedDataSet(conn, connMode.getPrettyMode(), op.getTestMethod().getQualifiedName() + "_tgt_" + tar, DatabaseImplementation.data_format.delimited);
		}
		else
		{
			target = databaseRuntimeSupport.getGeneratedDataSet(conn, connMode.getPrettyMode(), sourceTable + "_" + conn.getDatabaseName(mode) + "_" + op.getTestMethod().getName(), DatabaseImplementation.data_format.delimited);
		}

		// remove the assertion-mode attribute since it does not pass validation
		// for the extract operation

		ETLTestValueObjectBuilder cbuilder = new ETLTestValueObjectBuilder(copy);
		if (copy.query("assertion-mode") != null)
		{
			cbuilder = cbuilder.removeKey("assertion-mode");
		}

		if (copy.query("failure-id") != null)
		{
			cbuilder = cbuilder.removeKey("failure-id");
		}
		cbuilder = cbuilder.key("target-file")
				.value(target.getAbsolutePath());

		copy = cbuilder.toObject();

		ETLTestOperation extract_op = op.createSibling("extract", copy);

		econtext.process(extract_op, context);

		DataFileSchema ffs = null;

		// grab the schema the extract produced
		if (context.hasVariableBeenDeclared(DatabaseClassListener.LAST_EXTRACT_FILE_SCHEMA))
		{
			ffs = (DataFileSchema) context.getValue(DatabaseClassListener.LAST_EXTRACT_FILE_SCHEMA).getValueAsPojo();
		}

		switch (comparisonMode)
		{
			case equals:
			{
				String synthFailureId = connMode.connectionId + "." + connMode.getPrettyMode() + "." + schema + "." + operation.getTarget();

				if (tar == null)
				{
					throw new TestExecutionError("Assert equals requires a target argument", DatabaseConstants.ERR_ASSERT_MISSING_TARGET);
				}

				// compare to the file in the test folder
				File testFile = databaseRuntimeSupport.getSourceDataSet(op.getTestClass().getPackage(), tar, DatabaseImplementation.data_format.delimited);

				boolean matching = false;

				List diffReport = null;

				try
				{
					DataFile fSource = dataFileManager.loadDataFile(testFile, ffs);

					File dataSetLocal = databaseRuntimeSupport.getGeneratedDataSet(conn, connMode.getPrettyMode(), op.getTestMethod().getQualifiedName() + "_src_" + IOUtils.removeExtension(testFile.getName()), DatabaseImplementation.data_format.delimited);

					applicationLog.info("Creating cache copy of " + testFile.getAbsolutePath() + "] to [" + dataSetLocal.getAbsolutePath() + "]");

					DataFile lSource = dataFileManager.loadDataFile(dataSetLocal, ffs);

					// copy to local cache
					dataFileManager.copyDataFile(fSource, lSource);

					DataFile fTarget = dataFileManager.loadDataFile(target, ffs);

					applicationLog.info("Asserting equality of [" + lSource.getSource().getAbsolutePath() + "] and [" + fTarget.getSource().getAbsolutePath() + "]");

					String variableName = "extract.columns." + tar;
					applicationLog.info("Reading extract column list for " + variableName);
					ETLTestValueObject value = context.getValue(variableName);
					if (value == null)
					{
						throw new TestExecutionError("", DatabaseConstants.ERR_MISSING_COL_SPECIFICATION);
					}

					List columns = (List) value.getValueAsPojo();

					diffReport = dataFileManager.diff(lSource, fTarget);

					if (diffReport.size() != 0)
					{
						dataFileManager.report(diffManager, op, failureId != null ? failureId : synthFailureId, diffReport);

						throw new TestAssertionFailure("Table <" + sourceTable + "> does not match <" + operation.getTarget() + ">", failureId != null ? failureId : synthFailureId);
					}
				}
				catch (TestExecutionError e)
				{
					throw e;
				}
				catch (TestAssertionFailure e)
				{
					throw e;
				}
				catch (Exception e)
				{
					throw new TestExecutionError("", e);
				}

				break;
			}
			case empty:
				// for these two, just dump the table to a file and assert file size is or isn't 0
				// visit the file, skipping the header.
				// if there are any non-blank or non-comment rows then this is a fail

			{
				String synthFailureId = connMode.connectionId + "." + connMode.getPrettyMode() + "." + qualifiedName;

				try
				{
					DataFile fSource = dataFileManager.loadDataFile(target, ffs);

					int lines = 0;

					DataFile.FileData fdata = fSource.getFileData();

					try
					{
						Iterator it = fdata.iterator();

						while (it.hasNext())
						{
							it.next();
							lines++;
						}
					}
					finally
					{
						fdata.dispose();
					}

					if (lines != 0)
					{
						throw new TestAssertionFailure("Object " + synthFailureId + " not empty", failureId != null ? failureId : synthFailureId);
					}
				}
				catch (IOException e)
				{
					throw new TestExecutionError("", e);
				}

				break;
			}
			case not_empty:
			{
				String synthFailureId = connMode.connectionId + "." + connMode.getPrettyMode() + "." + qualifiedName;

				try
				{
					DataFile fSource = dataFileManager.loadDataFile(target, ffs);

					int lines = 0;

					DataFile.FileData fdata = fSource.getFileData();

					try
					{
						Iterator it = fdata.iterator();

						while (it.hasNext())
						{
							it.next();
							lines++;
						}
					}
					finally
					{
						fdata.dispose();
					}

					if (lines == 0)
					{
						throw new TestAssertionFailure("Object " + synthFailureId + " is empty", failureId != null ? failureId : synthFailureId);
					}
				}
				catch (IOException e)
				{
					throw new TestExecutionError("", e);
				}

				break;
			}
		}

		return ClassResponder.action_code.handled;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy