#!perl -wT
# $Id: /local/Handel/trunk/t/storage_dbic_constraints.t 1569 2007-06-24T15:35:46.298350Z claco  $
use strict;
use warnings;

BEGIN {
    use lib 't/lib';
    use Handel::Test;

    eval 'require DBD::SQLite';
    if($@) {
        plan skip_all => 'DBD::SQLite not installed';
    } else {
        plan tests => 79;
    };

    use_ok('Handel::Storage::DBIC');
    use_ok('Handel::Exception', ':try');
};

my $constraints = {
    'name'        => {'check_name' => \&check_name, 'no_coderef' => undef},
    'description' => {'check_description' => \&check_description}
};

my $storage = Handel::Storage::DBIC->new({
    schema_class    => 'Handel::Cart::Schema',
    schema_source   => 'Carts',
    constraints     => $constraints,
    connection_info => [
        Handel::Test->init_schema(no_populate => 1)->dsn
    ]
});


{
    isa_ok($storage, 'Handel::Storage');

    is_deeply($storage->constraints, $constraints, 'constraints were set');

    my $schema = $storage->schema_instance;
    isa_ok($schema, 'Handel::Cart::Schema');

    my $class = $schema->class('Carts');
    ok($class->isa('Handel::Components::Constraints'), 'constraints component is loaded');

    is_deeply($class->constraints, $constraints, 'constraints were loaded');

    my $cart = $schema->resultset('Carts')->create({
        id => 1,
        shopper => 2,
        name => 'test',
        description => 'Christopher Laco'
    });
    is($cart->name, 'test', 'got name');
    is($cart->description, 'ChristopherLaco', 'got altered description');


    ## throw exception for bogus name
    {
        try {
            local $ENV{'LANG'} = 'en';
            $cart->name('12345');
            $cart->update;

            fail('no exception thrown');
        } catch Handel::Exception::Constraint with {
            pass('constraint exception caught');
            like(shift, qr/failed.*check_name/, 'check_name failed in message');
        } otherwise {
            fail('other exception caught');
        };
    };


    ## update works just dandy with the right data
    $cart->name('123');
    $cart->update;
    is($cart->name, '123', 'name was updated');


    ## remove the constraints from source, and try again
    $schema->class('Carts')->constraints(undef);
    $cart->name('123456');
    $cart->update;
    is($cart->name, '123456', 'name was updated');


    my $constraint = sub{};
    $class->add_constraint('id', 'check id' => $constraint);
    is_deeply($class->constraints, {id => {'check id' => $constraint}}, 'constraints were set');

    my $new_constraint = sub{};
    $class->add_constraint('name', 'first' => $new_constraint);
    $class->add_constraint('name', 'second' => $new_constraint);

    is_deeply($class->constraints, {'id' => {'check id' => $constraint}, 'name' => {first => $new_constraint, second => $new_constraint}}, 'constraints were loaded');


    ## throw exception when no column is passed
    {
        try {
            local $ENV{'LANG'} = 'en';
            $class->add_constraint(undef, second => sub{});

            fail('no exception thrown');
        } catch Handel::Exception::Argument with {
            pass('argument exception caught');
            like(shift, qr/no column/i, 'no column in message');
        } otherwise {
            fail('other exception caught');
        };
    };

    ## throw exception when no name is passed
    {
        try {
            local $ENV{'LANG'} = 'en';
            $class->add_constraint('id', undef, sub{});

            fail('no exception thrown');
        } catch Handel::Exception::Argument with {
            pass('argument exception caught');
            like(shift, qr/no constraint name/i, 'no constraint in message');
        } otherwise {
            fail('other exception caught');
        };
    };

    ## throw exception when no constraint is passed
    {
        try {
            local $ENV{'LANG'} = 'en';
            $class->add_constraint('id', 'second' => undef);

            fail('no exception thrown');
        } catch Handel::Exception::Argument with {
            pass('argument exception caught');
            like(shift, qr/no constraint/i, 'no constraint in message');
        } otherwise {
            fail('other exception caught');
        };
    };

    ## throw exception when non-CODEREF is passed
    {
        try {
            local $ENV{'LANG'} = 'en';
            $class->add_constraint('id', 'second' => []);

            fail('no exception thrown');
        } catch Handel::Exception::Argument with {
            pass('argument exception caught');
            like(shift, qr/no constraint/i, 'no constraint in message');
        } otherwise {
            fail('caught other exception');
        };
    };


    ## throw exception when setting a constraints with open schema_instance
    {
        try {
            local $ENV{'LANG'} = 'en';
            $storage->constraints({
                field => {'do_field' => sub{}}
            });

            fail('no exception thrown');
        } catch Handel::Exception::Storage with {
            pass('caught storage exception');
            like(shift, qr/existing schema/, 'existing schema instance in message');
        } otherwise {
            fail('other exception thrown');
        };
    };


    ## throw exception when setting a bogus constraint class
    {
        try {
            local $ENV{'LANG'} = 'en';
            $storage->constraints_class('Funklebean');

            fail('no exception thrown');
        } catch Handel::Exception::Storage with {
            pass('storage exception thrown');
            like(shift, qr/constraints_class.*could not be loaded/, 'constraint class in message');
        } otherwise {
            fail('other exception caught');
        };
    };

    ## reset it all, and try a custom constraints class
    $storage->schema_instance(undef);
    is($storage->_schema_instance, undef, 'unset schema instance');

    $storage->constraints_class('Handel::TestComponents::Constraints');
    is($storage->constraints_class, 'Handel::TestComponents::Constraints', 'set constraints class');

    my $new_schema = $storage->schema_instance;
    isa_ok($new_schema, 'Handel::Cart::Schema');
    ok($new_schema->class('Carts')->isa('Handel::TestComponents::Constraints'), 'constraints class is set');
    ok(!$schema->class('Carts')->isa('Handel::TestComponents::Constraints'), 'Constraints component is loaded');
};


## do the item_storage default_values too
{
    my $constraints = {
        'sku' => {'check_sku' => \&check_sku, 'no_coderef' => undef}
    };

    $storage->schema_instance(undef);
    my $item_storage = Handel::Storage::DBIC->new({
        schema_class    => 'Handel::Cart::Schema',
        schema_source   => 'Items',
        constraints     => $constraints
    });
    $storage->item_storage($item_storage);

    is_deeply($storage->item_storage->constraints, $constraints, 'constraints were loaded');

    my $schema = $storage->schema_instance;
    isa_ok($schema, 'Handel::Cart::Schema');

    my $class = $schema->class('Items');
    ok($class->isa('Handel::Components::Constraints'), 'Constraints component is loaded');

    is_deeply($class->constraints, $constraints, 'constraints were loaded');

    my $item = $schema->resultset('Items')->create({
        id => 1,
        cart => 1,
        sku => 'ABC'
    });
    is($item->sku, 'ABC', 'got sku');


    ## throw exception for bogus sku
    {
        try {
            local $ENV{'LANG'} = 'en';
            $item->sku('12345');
            $item->update;

            fail('no exception thrown');
        } catch Handel::Exception::Constraint with {
            pass('constraint exception caught');
            like(shift, qr/failed.*constraint.*check_sku/, 'check_sku failed in message');
        } otherwise {
            fail('other exception caught');
        };
    };


    ## update works just dandy with the right data
    $item->sku('DEF');
    $item->update;
    is($item->sku, 'DEF', 'sku was updated');


    ## remove the constraints from source, and try again
    $schema->class('Items')->constraints(undef);
    $item->sku('123456');
    $item->update;
    is($item->sku, '123456', 'sku was updated');



    ## item_storage without default_values
    $storage->schema_instance(undef);
    $item_storage->schema_instance(undef);
    $item_storage->constraints(undef);
    
    $schema = $storage->schema_instance;

    $class = $schema->class('Items');
    ok(!$class->isa('Handel::Components::Constraints'), 'Constraints component is not loaded');
    is($storage->item_storage->constraints, undef, 'constraints are still undefined');
};


sub check_name {
    my $value = defined $_[0] ? shift : '';
    my ($object, $column, $changing) = @_;

    ok($value, 'got value');
    isa_ok($object, 'DBIx::Class::ResultSource::Table');
    is($column, 'name', 'got column name');
    isa_ok($changing, 'HASH');

    return $value =~ /^.{2,4}$/;
};

sub check_sku {
    my $value = defined $_[0] ? shift : '';
    my ($object, $column, $changing) = @_;

    ok($value, 'got value');
    isa_ok($object, 'DBIx::Class::ResultSource::Table');
    is($column, 'sku', 'got column sku');
    isa_ok($changing, 'HASH');

    return $value =~ /^.{2,4}$/;
};

sub check_description {
    my $value = defined $_[0] ? shift : '';
    my ($object, $column, $changing) = @_;

    ok($value, 'got value');
    isa_ok($object, 'DBIx::Class::ResultSource::Table');
    is($column, 'description', 'got column description');
    isa_ok($changing, 'HASH');

    $value =~ s/\s+//g;
    $changing->{$column} = $value;

    return 1;
};
