The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package TestUtil;

# $Id: TestUtil.pm 400 2008-12-17 13:43:29Z jonasbn $

use strict;
use vars qw($VERSION);
use DateTime;
use Test::More;
use List::MoreUtils qw(all);

$VERSION = '0.02';

my ( $original_dir );

END {
    chdir( $original_dir );
}

my $LOG_FILE  = 'workflow_tests.log';
my $CONF_FILE = 'log4perl.conf';

########################################
# TICKET INFO

# Data for the initial ticket

my %TICKET = (
    current_user => 'Test User',
    creator      => 'Test User',
    subject      => 'A test ticket',
    description  => 'This is a test ticket used by the unit tests',
    type         => 'Feature',
    due_date     => DateTime->now,
);

sub get_new_ticket_info {
    return %TICKET;
}

sub set_new_ticket_context {
    my ( $class, $wf ) = @_;
    for ( keys %TICKET ) {
        $wf->context->param( $_ => $TICKET{ $_ } );
    }
}

sub check_workflow_history {
    my ( $class, $tracker, $values ) = @_;
    $class->check_tracker(
        $tracker, 'create workflow history',
        qr/^INSERT INTO workflow_history \( workflow_id, action, description, state, workflow_user, history_date, workflow_hist_id \)/,
        [ 'workflow ID', 'action', 'description',
          'state', 'user', 'current date',
          'random ID of correct length' ],
        $values );
}

sub check_tracker {
    my ( $class, $tracker, $tracker_desc, $sql_pattern, $names, $values ) = @_;
    like( $tracker->statement, $sql_pattern,
          "Statement matches ($tracker_desc)" );
    my $track_params = $tracker->bound_params;
    my $num_params = scalar @{ $names };
    is( scalar @{ $track_params }, $num_params,
        "Correct number ($num_params) of parameters bound ($tracker_desc)" );
    for ( my $i = 0; $i < $num_params; $i++ ) {
        my $this_name = ( $i == 0 )
                        ? "Bound parameter for '$names->[ $i ]' matches"
                        : "...for '$names->[ $i ]' matches";
        my @to_compare = ( ref( $values->[ $i ] ) eq 'CODE' )
                           ? $values->[ $i ]->( $track_params->[ $i ] )
                           : ( $track_params->[ $i ], $values->[ $i ] );
        is( $to_compare[0], $to_compare[1], $this_name );
    }
}

# Tests call this to initialize the workflow factory with common
# information. This tests the xml config files.

sub init_factory {
    require Workflow::Factory;
    my $factory = Workflow::Factory->instance;
    $factory->add_config_from_file(
        workflow  => [ 'workflow.xml', 'workflow_type.xml', 'workflow_evaluate_condition.xml' ],
        action    => [ 'workflow_action.xml', 'workflow_action_type.xml' ],
        condition => [ 'workflow_condition.xml', 'workflow_condition_type.xml'],
        validator => "workflow_validator.xml"
    );
    return $factory;
}

# Initialize with perl config files.

sub init_factory_perl_config {
    require Workflow::Factory;
    my $factory = Workflow::Factory->instance;
    $factory->add_config_from_file(
        workflow  => [ 'workflow.perl', 'workflow_type.perl' ],
        action    => [ 'workflow_action.perl', 'workflow_action_type.perl' ],
        condition => [ 'workflow_condition.perl', 'workflow_condition_type.perl' ],
        validator => 'workflow_validator.perl'
    );
    return $factory;
}

sub init_mock_persister {
    require Workflow::Factory;
    my $factory = Workflow::Factory->instance;
    my %persister = (
        name  => 'TestPersister',
        class => 'Workflow::Persister::DBI',
        dsn   => 'DBI:Mock:',
	user => 'DBTester',
    );
    $factory->add_config( persister => [ \%persister ] );
}



# Initialize the logger and other resources; called when module
# required by test

sub init {
    if ( -f $LOG_FILE ) {
        unlink( $LOG_FILE );
    }
    elsif ( -f "t/$LOG_FILE" ) {
        unlink( "t/$LOG_FILE" );
    }

    require Cwd;
    $original_dir = Cwd::cwd();
    chdir( 't' )  if ( -d 't' );

    require Log::Log4perl;
    Log::Log4perl::init( $CONF_FILE );

}

init();

# Used with state tests with various configs.
sub run_state_tests{
  my $factory = shift;

  # Call Type2 first. It gets loaded second, so this
  # should verify that both types are available.
  my $wf2 = $factory->create_workflow( 'Type2' );

  my $wf_state = $wf2->_get_workflow_state();
  is( $wf_state->state(), 'INITIAL', 'In INITIAL state.');

  my @actions = $wf_state->get_available_action_names( $wf2 );
  is( (scalar @actions), 1, 'Got back one available action.');
  is( $actions[0], 'TIX_NEW', 'Got TIX_NEW as available action.');

  # Verify the correct action and class.
  my $wf_action = $wf2->_get_action('TIX_NEW');
  is( $wf_action->name(), 'TIX_NEW', 'Got TIX_NEW action for Type2.');
  is( $wf_action->class(), 'TestApp::Action::TicketCreateType',
      'Got TicketCreateType class.');

  TestUtil->set_new_ticket_context( $wf2 );
  ok( $wf2->execute_action('TIX_NEW'), 'Ran TIX_NEW action.');

  $wf_state = $wf2->_get_workflow_state();
  is( $wf_state->state(), 'Ticket_Created', 'In Ticket_Created state.');

  @actions = $wf_state->get_available_action_names( $wf2 );
  is( (scalar @actions), 1, 'Got back one available action.');
  is( $actions[0], 'Ticket_Close', 'Got Ticket_Close as available action.');

  # Repeat on the Ticket workflow where the actions have no type.

  my $wf = $factory->create_workflow( 'Ticket' );

  $wf_state = $wf->_get_workflow_state();
  is( $wf_state->state(), 'INITIAL', 'In INITIAL state for Ticket type.');

  @actions = $wf_state->get_available_action_names( $wf );
  is( (scalar @actions), 1, 'Got back one available action.');
  is( $actions[0], 'TIX_NEW', 'Got TIX_NEW as available action.');

  # Verify the correct action and class.
  $wf_action = $wf->_get_action('TIX_NEW');
  is( $wf_action->name(), 'TIX_NEW', 'Got TIX_NEW action for Ticket.');
  is( $wf_action->class(), 'TestApp::Action::TicketCreate',
      'Got TicketCreate class.');

  TestUtil->set_new_ticket_context( $wf );
  ok( $wf->execute_action('TIX_NEW'), 'Ran TIX_NEW action.');

  $wf_state = $wf->_get_workflow_state();
  is( $wf_state->state(), 'TIX_CREATED', 'In TIX_CREATED state.');

  @actions = $wf_state->get_available_action_names( $wf2 );
  is( (scalar @actions), 2, 'Got back two available actions.');
  ok(all { defined $_ } qw(TIX_EDIT TIX_COMMENT), 'Got TIX_EDIT and TIX_COMMENT as available actions.');
}

'I am true!';