The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# vim: set ts=2 sts=2 sw=2 expandtab smarttab:
use strict;
use warnings;
use Test::More 0.96;
use Test::MockObject 1.09;

my $mod = 'DBIx::Schema::UpToDate';
eval "require $mod" or die $@;

my ($table_info, $db_ver);
my $sth = Test::MockObject->new()
  ->mock(fetchall_arrayref => sub { $table_info })
  ->mock(fetchall_hashref  => sub { $table_info });

my @dbh_done;
my ($begun, $committed, $begin, $commit, $do, $dbi_last) = (0, 0, 1, 1, 1, 'unknown');
my $dbh = Test::MockObject->new()
  ->mock(begin_work => sub { $dbi_last = 'begin_work'; ++$begun;     $begin  })
  ->mock(commit     => sub { $dbi_last = 'commit';     ++$committed; $commit })
  ->mock(do         => sub { $dbi_last = 'do ' . ($_[1] =~ /^(\S+)/)[0]; push(@dbh_done, $_[1]); $do })
  ->mock(errstr     => sub { "failure: $dbi_last" })
  ->mock(quote_identifier => sub { qq{"$_[0]"} })
  ->mock(selectcol_arrayref => sub { push(@dbh_done, $_[1]); [$db_ver] })
  ->mock(table_info => sub { $sth });

my $schema = new_ok($mod, [dbh => $dbh, auto_update => 0]);

# current_version
$table_info = [];
is($schema->current_version, undef, 'not built');
$table_info = [version => {}];
$db_ver = 1;
is($schema->current_version, 1, 'version fetched');

# sql_limit
like($dbh_done[-1], qr/DESC LIMIT 1$/, 'used limit');
$schema->{sql_limit} = 0;
is($schema->current_version, 1, 'version fetched');
# limit attribute
like($dbh_done[-1], qr/DESC$/, 'limit not used');
$schema->{sql_limit} = 1;

# latest_version
$schema->{updates} = [1, 2, 3, 4];
is($schema->latest_version, 4, 'fake latest_version');
delete $schema->{updates};
# this one's a little silly
is($schema->latest_version, @{ $schema->updates }, 'latest version');

# up_to_date()
my $updated = 0;
$db_ver = 0;
$schema->{updates} = [sub { $updated++ }, sub { $updated++ }];

$sth->set_series('fetchall_arrayref', [], [1]);
$schema->up_to_date();
is($updated, 2, 'correct number of updates');

$sth->mock('fetchall_arrayref', sub { [1] });

$updated = 0;
$db_ver = 1;
$schema->up_to_date();
is($updated, 1, 'correct number of updates');

$updated = 0;
$db_ver = 2;
$schema->up_to_date();
is($updated, 0, 'correct number of updates');

$sth->set_series('fetchall_arrayref', [], []);
is(eval { $schema->up_to_date(); }, undef, 'up_to_date() dies w/o current version');
like($@, qr/version table/, 'up_to_date() died w/o version table');

# update_to_version transactions
foreach my $test (
  [1, 1],
  [0, 0],
){
  my ($tr, $exp) = @$test;
  $schema->{transactions} = $tr;
  $updated = $begun = $committed = 0;
  $schema->update_to_version(1);
  is($updated,      1, 'updated');
  is($begun,     $exp, "transaction: $tr - begin");
  is($committed, $exp, "transaction: $tr - commit");
}

# DBI errors
{
  $schema->{transactions} = 1;
  $begin = $commit = undef;
  eval { $schema->update_to_version(1) };
  like($@, qr/failure: begin_work/,  'raise begin_work failure');
  $begin = 1;
  eval { $schema->update_to_version(1) };
  like($@, qr/failure: commit/,       'raise commit failure');
  $commit = 1;

  $do = undef;
  eval { $schema->initialize_version_table };
  like($@, qr/failure: do CREATE/, 'raise do() failure');
  eval { $schema->set_version(0) };
  like($@, qr/failure: do INSERT/, 'raise do() failure');

  # reset vars
  $begin = $commit = $do = 1;
}

# update_to_version
my $inst = [0, 0];
$schema->{updates} = [sub { $inst->[0]++ }, sub { $inst->[1]++ }];
$schema->update_to_version(2);
is_deeply($inst, [0,1], 'correct update executed');
$schema->update_to_version(1);
is_deeply($inst, [1,1], 'correct update executed');
$schema->update_to_version(1);
is_deeply($inst, [2,1], 'correct update executed');

done_testing;