Package dbfit.fixture

Source Code of dbfit.fixture.DbObjectExecutionFixture

package dbfit.fixture;

import dbfit.api.DbObject;
import dbfit.util.*;
import fit.Binding;
import fit.Fixture;
import fit.Parse;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static dbfit.util.Direction.*;

/**
* this class handles all cases where a statement should be executed for each row with
* given inputs and verifying optional outputs or exceptions. it also handles a special case
* when just a single statement is executed without binding parameters to columns. Examples are
* - Inserting data into tables/views
* - Executing statements
* - Updates
* - Stored procedures/functions
* <p/>
* the object under test is defined by overriding getTargetObject. Unfortunately, because of the way FIT
* instantiates fixtures, passing in an object using a constructor and aggregation simply doesn't do the trick
* so users have to extend this fixture.
*/
public abstract class DbObjectExecutionFixture extends Fixture {
    private DbParameterAccessors accessors = new DbParameterAccessors();
    private Map<DbParameterAccessor, Binding> columnBindings;
    private StatementExecution execution;
    private DbObject dbObject; // intentionally private, subclasses should extend getTargetObject

    /**
     * override this method to control whether an exception is expected or not. By default, expects no exception to happen
     */
    protected ExpectedBehaviour getExpectedBehaviour() {
        return ExpectedBehaviour.NO_EXCEPTION;
    }

    /**
     * override this method and supply the expected exception number, if one is expected
     */
    protected int getExpectedErrorCode() {
        return 0;
    }

    /**
     * override this method and supply the dbObject implementation that will be executed for each row
     */
    protected abstract DbObject getTargetDbObject() throws SQLException;

    /**
     * executes the target dbObject for all rows of the table. if no rows are specified, executes
     * the target object only once
     */
    public void doRows(Parse rows) {
        try {
            dbObject = getTargetDbObject();
            if (dbObject == null) throw new Error("DB Object not specified!");
            if (rows == null) {//single execution, no args
                try (StatementExecution preparedStatement =
                        dbObject.buildPreparedStatement(accessors.toArray())) {
                    preparedStatement.run();
                    return;
                }
            }
            List<String> columnNames = getColumnNamesFrom(rows.parts);
            accessors = getAccessors(rows.parts, columnNames);
            if (accessors == null) return;// error reading args
            columnBindings = getColumnBindings();
            try (StatementExecution preparedStatement
                    = dbObject.buildPreparedStatement(accessors.toArray())) {
                execution = preparedStatement;
                Parse row = rows;
                while ((row = row.more) != null) {
                    runRow(row);
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
            if (rows == null) {
                throw new Error(e);
            }
            exception(rows.parts, e);
        }
    }

    /**
     * does the column name map to an output argument
     */
    private static boolean isOutput(String name) {
        return name.endsWith("?");
    }

    private List<String> getColumnNamesFrom(Parse headerCells) {
        List<String> columnNames = new ArrayList<String>();
        for (; headerCells != null; headerCells = headerCells.more) {
            columnNames.add(headerCells.text());
        }
        return columnNames;
    }

    private DbParameterAccessors getAccessors(Parse headerRow, List<String> columnNames) throws SQLException {
        try {
            return getAccessors(columnNames);
        } catch (IllegalArgumentException e) {
            exception(headerRow, e);
            return null;
        }
    }

    /**
     * initialise db parameters for the dbObject based on table header cells
     */
    private DbParameterAccessors getAccessors(List<String> columnNames) throws SQLException {
        DbParameterAccessors accessors = new DbParameterAccessors();
        for (String name : columnNames) {
            DbParameterAccessor accessor = dbObject.getDbParameterAccessor(name, isOutput(name) ? OUTPUT : INPUT);
            if (accessor == null) {
                throw new IllegalArgumentException("Parameter/column " + name + " not found");
            }
            accessors.add(accessor);
        }
        return accessors;
    }

    /**
     * bind db accessors to columns based on the text in the header
     */
    private Map<DbParameterAccessor, Binding> getColumnBindings() throws Exception {
        Map<DbParameterAccessor, Binding> bindings = new HashMap<DbParameterAccessor, Binding>();
        for (DbParameterAccessor accessor : accessors.toArray()) {
            Binding binding = (accessor.hasDirection(INPUT) ? new SymbolAccessSetBinding() : new SymbolAccessQueryBinding());
            binding.adapter = new DbParameterAccessorTypeAdapter(accessor, this);
            bindings.put(accessor, binding);
        }
        return bindings;
    }

    /**
     * execute a single row
     */
    private void runRow(Parse row) throws Throwable {
        //first set input params
        Map<DbParameterAccessor, Parse> cellMap = accessors.zipWith(asCellList(row));
        for (DbParameterAccessor inputAccessor : accessors.getInputAccessors()) {
            Parse cell = cellMap.get(inputAccessor);
            columnBindings.get(inputAccessor).doCell(this, cell);
        }

        if (getExpectedBehaviour() == ExpectedBehaviour.NO_EXCEPTION) {
            executeStatementAndEvaluateOutputs(row);
        } else {
            executeStatementExpectingException(row);
        }
    }

    private void executeStatementExpectingException(Parse row) throws Exception {
        try {
            execution.run();
            wrong(row);
        } catch (SQLException e) {
            e.printStackTrace();
            // all good, exception expected
            if (getExpectedBehaviour() == ExpectedBehaviour.ANY_EXCEPTION) {
                right(row);
            } else {
                int realError = e.getErrorCode();
                if (realError == getExpectedErrorCode())
                    right(row);
                else {
                    wrong(row);
                    row.parts.addToBody(fit.Fixture.gray(" got error code " + realError));
                }
            }
        }
    }

    private void executeStatementAndEvaluateOutputs(Parse row)
            throws SQLException, Throwable {
        execution.run();
        Map<DbParameterAccessor, Parse> cellMap = accessors.zipWith(asCellList(row));
        for (DbParameterAccessor outputAccessor : accessors.getOutputAccessors()) {
            Parse cell = cellMap.get(outputAccessor);
            columnBindings.get(outputAccessor).doCell(this, cell);
        }
    }

    private List<Parse> asCellList(Parse row) {
        List<Parse> cells = new ArrayList<Parse>();
        for (Parse cell = row.parts; cell != null; cell = cell.more) {
            cells.add(cell);
        }
        return cells;
    }
}
TOP

Related Classes of dbfit.fixture.DbObjectExecutionFixture

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.