# $Id: db-common.pl 58 2006-05-04 00:04:10Z sky $

use strict;
use warnings;
use Test::More;

#diag "executing common transaction tests";
use Data::ObjectDriver::BaseObject;

## testing basic rollback
{
    Data::ObjectDriver::BaseObject->begin_work;

    my $recipe = Recipe->new;
    $recipe->title('gratin dauphinois');
    ok($recipe->save, 'Object saved successfully');
    ok(my $recipe_id = $recipe->recipe_id, 'Recipe has an ID');
    is($recipe->title, 'gratin dauphinois', 'Title is set');

    my $ingredient = Ingredient->new;
    $ingredient->recipe_id($recipe->recipe_id);
    $ingredient->name('cheese');
    $ingredient->quantity(10);
    ok($ingredient->save, 'Ingredient saved successfully');
    ok(my $ingredient_pk = $ingredient->primary_key, 'Ingredient has an ID');
    ok($ingredient->id, 'ID is defined');
    is($ingredient->name, 'cheese', 'got a name for the ingredient');

    #use YAML; warn Dump (Data::ObjectDriver::BaseObject->txn_debug);
    Data::ObjectDriver::BaseObject->rollback;
    
    ## check that we don't have a trace of all the good stuff we cooked
    is(Recipe->lookup($recipe_id), undef, "no trace of object");
    is(eval { Ingredient->lookup($ingredient_pk) }, undef, "no trace of object");
    is(Recipe->lookup_multi([ $recipe_id ])->[0], undef);
}

## testing basic commit
{
    Data::ObjectDriver::BaseObject->begin_work;

    my $recipe = Recipe->new;
    $recipe->title('gratin dauphinois');
    ok($recipe->save, 'Object saved successfully');
    ok(my $recipe_id = $recipe->recipe_id, 'Recipe has an ID');
    is($recipe->title, 'gratin dauphinois', 'Title is set');

    my $ingredient = Ingredient->new;
    $ingredient->recipe_id($recipe->recipe_id);
    $ingredient->name('cheese');
    $ingredient->quantity(10);
    ok($ingredient->save, 'Ingredient saved successfully');
    ok(my $ingredient_pk = $ingredient->primary_key, 'Ingredient has an ID');
    ok($ingredient->id, 'ID is defined');
    is($ingredient->name, 'cheese', 'got a name for the ingredient');

    Data::ObjectDriver::BaseObject->commit;
    
    ## check that we don't have a trace of all the good stuff we cooked
    ok(Recipe->lookup($recipe_id), "still here");
    ok(Ingredient->lookup($ingredient_pk), "still here");
    ok defined Recipe->lookup_multi([ $recipe_id ])->[0];
    
    ## and now test a rollback of a remove
    Data::ObjectDriver::BaseObject->begin_work;
    $ingredient->remove;
    Data::ObjectDriver::BaseObject->rollback;
    ok(Ingredient->lookup($ingredient_pk), "still here");
    
    ## finally let's delete it
    Data::ObjectDriver::BaseObject->begin_work;
    $ingredient->remove;
    Data::ObjectDriver::BaseObject->commit;
    ok(! Ingredient->lookup($ingredient_pk), "finally deleted");
}

sub warns_ok (&;$) {
    my ($sub, $msg) = @_;

    my $warn = 0;
    local $SIG{__WARN__} = sub { $warn++ };
    $sub->();

    $warn ? pass($msg) : fail($msg);
}

## nested transactions
{
    ## if there is no transaction active this will just warn
    is( Data::ObjectDriver::BaseObject->txn_active, 0);
    warns_ok { Data::ObjectDriver::BaseObject->commit() }
        'committing with no active transaction caused warning';
    is( Data::ObjectDriver::BaseObject->txn_active, 0);
    
    ## do a commit in the end
    Data::ObjectDriver::BaseObject->begin_work;
    is( Data::ObjectDriver::BaseObject->txn_active, 1);

    my $recipe = Recipe->new;
    $recipe->title('lasagnes');
    ok($recipe->save, 'Object saved successfully');

    warns_ok { Data::ObjectDriver::BaseObject->begin_work() }
        'beginning new transaction with a transaction already open '
        . 'causes warning';
    warns_ok { Data::ObjectDriver::BaseObject->begin_work() }
        'beginning new transaction with two transactions already open '
        . 'causes warning';
    is( Data::ObjectDriver::BaseObject->txn_active, 3);

    
    my $ingredient = Ingredient->new;
    $ingredient->recipe_id($recipe->recipe_id);
    $ingredient->name("pasta");
    ok $ingredient->insert;

    Data::ObjectDriver::BaseObject->rollback;
    Data::ObjectDriver::BaseObject->commit;
    Data::ObjectDriver::BaseObject->commit;
    is( Data::ObjectDriver::BaseObject->txn_active, 0);
    
    $recipe = Recipe->lookup($recipe->primary_key);
    $ingredient = Ingredient->lookup($ingredient->primary_key);
    ok $recipe, "got committed";
    ok $ingredient, "got committed";
    is $ingredient->name, "pasta";
    
    ## now test the same thing with a rollback in the end
    Data::ObjectDriver::BaseObject->begin_work;

    $recipe = Recipe->new;
    $recipe->title('lasagnes');
    ok($recipe->save, 'Object saved successfully');

    warns_ok { Data::ObjectDriver::BaseObject->begin_work() }
        'beginning new transaction with a transaction already open '
        . 'still causes warning';
    
    $ingredient = Ingredient->new;
    $ingredient->recipe_id($recipe->recipe_id);
    $ingredient->name("more layers");
    ok $ingredient->insert;

    Data::ObjectDriver::BaseObject->commit;
    Data::ObjectDriver::BaseObject->rollback;
    
    $recipe = Recipe->lookup($recipe->primary_key);
    $ingredient = eval { Ingredient->lookup($ingredient->primary_key) };
    ok ! $recipe, "rollback";
    ok ! $ingredient, "rollback";
}

1;
