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(selectcol_arrayref => sub { [$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');

# 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;
