#!perl -wT
# $Id: currency.t 1731 2007-02-11 20:35:41Z claco $
use strict;
use warnings;

BEGIN {
    use lib 't/lib';
    use Test::More tests => 118;
    use Scalar::Util qw/refaddr/;

    eval 'use Test::MockObject 1.07';
    if (!$@) {
        Test::MockObject->fake_module('Finance::Currency::Convert::WebserviceX' => (
            new => sub {return bless {}, shift},
            convert => sub {return $_[1]+1.00}
        ));

        Test::MockObject->fake_module('MyConverterClass' => (
            new => sub {return bless {}, shift},
            convert => sub {return $_[1]+2.00}
        ));
    };

    use_ok('Data::Currency');
};


## check overloads
{
    my $currency = Data::Currency->new(1);
    is($currency + 1, 2, 'overloads as numeric');
    ok($currency, 'overloads boolean');
    is("$currency", '$1.00', 'overloads string');
    cmp_ok($currency, '==', 1, 'overloads ==');
};


## as_string/stringify with format
{
    my $currency = Data::Currency->new;
    isa_ok($currency, 'Data::Currency');
    is($currency->value, 0, 'value was set');
    is($currency->stringify('FMT_STANDARD'), '0.00 USD', 'stringify to value');
    is($currency->code, 'USD', 'code is set');
    is($currency->format, 'FMT_COMMON', 'format is not set');
    is($currency->name, 'US Dollar', 'got name');
    is($currency->converter, undef, 'converter no defined');
};


## croak as_string/stringify with no code
{
    my $currency = Data::Currency->new;
    isa_ok($currency, 'Data::Currency');
    delete $currency->{'code'};
    local $Data::Currency::__cag_code = undef;
    
    eval {
        $currency->stringify;
    };
    like($@, qr/invalid currency code/i);
};


## croak as_string/stringify with bad code
{
    my $currency = Data::Currency->new;
    isa_ok($currency, 'Data::Currency');
    $currency->{'code'} = 'BAD';

    eval {
        $currency->stringify;
    };
    like($@, qr/invalid currency code/i);
};


## croak convert with bad target code
{
    my $currency = Data::Currency->new;
    isa_ok($currency, 'Data::Currency');
    
    eval {
        $currency->convert('BAD');
    };
    like($@, qr/invalid currency code/i);
};


## croak convert with bad source code
{
    my $currency = Data::Currency->new;
    isa_ok($currency, 'Data::Currency');
    $currency->{'code'} = 'BAD';
    
    eval {
        $currency->convert('CAD');
    };
    like($@, qr/invalid currency code/i);
};


## default to FMT_COMMON when no format is set in stringify
{
    my $currency = Data::Currency->new(1.23);
    $currency->format(undef);
    Data::Currency->format(undef);
    is($currency->format, undef);
    delete $currency->{'format'};
    is($currency->format, undef);

    is($currency->stringify, '$1.23');
    Data::Currency->format('FMT_COMMON');
    is($currency->format, 'FMT_COMMON');
};


## create with no value
{
    my $currency = Data::Currency->new;
    isa_ok($currency, 'Data::Currency');
    is($currency->value, 0, 'value was set');
    is($currency->stringify, '$0.00', 'stringify to value');
    is($currency->code, 'USD', 'code is set');
    is($currency->format, 'FMT_COMMON', 'format is not set');
    is($currency->name, 'US Dollar', 'got name');
    is($currency->converter, undef, 'converter no defined');
};


## create new with no options
{
    my $currency = Data::Currency->new(1.23);
    isa_ok($currency, 'Data::Currency');
    is($currency->value, 1.23, 'value was set');
    is($currency->stringify, '$1.23', 'stringify to value');
    is($currency->code, 'USD', 'code is set');
    is($currency->format, 'FMT_COMMON', 'format is set');
    is($currency->name, 'US Dollar', 'got name');
    is($currency->converter, undef, 'converter no defined');
};


## create new with code/format
{
    my $currency = Data::Currency->new(1.23, 'CAD', 'FMT_STANDARD');
    isa_ok($currency, 'Data::Currency');
    is($currency->value, 1.23, 'value was set');
    is($currency->stringify, '1.23 CAD', 'stringify to string');
    is($currency->code, 'CAD', 'code was set');
    is($currency->format, 'FMT_STANDARD', 'format was set');
    is($currency->converter, undef, 'converter no defined');
};


## create new with code/format as a hash
{
    my $currency = Data::Currency->new({
        value  => 1.23,
        code   => 'CAD',
        format => 'FMT_STANDARD'
    });
    isa_ok($currency, 'Data::Currency');
    is($currency->value, 1.23, 'value was set');
    is($currency->stringify, '1.23 CAD', 'stringify to string');
    is($currency->code, 'CAD', 'code was set');
    is($currency->format, 'FMT_STANDARD', 'format was set');
    is($currency->converter, undef, 'converter no defined');
};


## create new with code/format as a hash w/ bad data
{
    my $currency = Data::Currency->new({
        value  => undef,
        code   => undef,
        format => undef
    });
    isa_ok($currency, 'Data::Currency');
    is($currency->value, 0, 'value was set');
    is($currency->stringify, '$0.00', 'stringify to string');
    is($currency->code, 'USD', 'code was set');
    is($currency->format, 'FMT_COMMON', 'format was set');
    is($currency->converter, undef, 'converter no defined');
};


## croak when bad currency code is set in new
{
    eval {
        my $currency = Data::Currency->new(1.23, 'BAD');
    };
    like($@, qr/invalid currency code/i);
};


## create and set code/format
{
    my $currency = Data::Currency->new(1.23);
    isa_ok($currency, 'Data::Currency');
    is($currency->value, 1.23, 'value was set');
    is($currency->stringify, '$1.23', 'stringify to string');
    is($currency->code, 'USD', 'code is set');
    is($currency->converter, undef, 'converter no defined');

    $currency->code('CAD');
    is($currency->code, 'CAD', 'code set');

    $currency->format('FMT_STANDARD');
    is($currency->format, 'FMT_STANDARD', 'format set');

    is($currency->stringify, '1.23 CAD', 'stringify to string');
};


## tcroak when bad currency code is set
{
    my $currency = Data::Currency->new(1.23);

    eval {
        $currency->code('BAD');
    };
    like($@, qr/invalid currency code/i);
};


## get name
{
    my $currency = Data::Currency->new(1.23, 'JPY');
    isa_ok($currency, 'Data::Currency');
    is($currency->code, 'JPY', 'code was set');
    is($currency->name, 'Yen', 'got name');
};


## throw exception when converter_class can't be loaded
{
    eval {
        Data::Currency->converter_class('Bogus');
    };
    like($@, qr/Bogus could not be loaded/i);
};


## test convert
SKIP: {
    eval 'use Test::MockObject 1.07';
    skip 'Test::MockObject 1.07 not installed', 44 if $@;


    ## convert with code
    {
        my $currency = Data::Currency->new(1.23, 'USD');
        isa_ok($currency, 'Data::Currency');
        is($currency->value, 1.23, 'value was set');
        is($currency->stringify, '$1.23', 'stringify to string');
        is($currency->code, 'USD', 'code is set');
        is($currency->format, 'FMT_COMMON', 'format is not set');
        is($currency->converter, undef, 'converter not defined');

        my $converted = $currency->convert('CAD');
        isa_ok($currency->converter, 'Finance::Currency::Convert::WebserviceX');
        isa_ok($converted, 'Data::Currency');
        is($converted->value, 2.23, 'value was set');
        is($converted->stringify, '$2.23', 'stringify to value');
        is($converted->code, 'CAD', 'code is set');
        is($converted->format, 'FMT_COMMON', 'format is set');
    };


    ## use an existing converter
    {
        my $currency = Data::Currency->new(1.23, 'USD');
        isa_ok($currency, 'Data::Currency');
        is($currency->value, 1.23, 'value was set');
        is($currency->stringify, '$1.23', 'stringify to string');
        is($currency->code, 'USD', 'code is set');
        is($currency->format, 'FMT_COMMON', 'format is not set');
        is($currency->converter, undef, 'converter not defined');
        $currency->converter(MyConverterClass->new);

        my $converted = $currency->convert('CAD');
        isa_ok($currency->converter, 'MyConverterClass');
        isa_ok($converted, 'Data::Currency');
        is($converted->value, 3.23, 'value was set');
        is($converted->stringify, '$3.23', 'stringify to value');
        is($converted->code, 'CAD', 'code is set');
        is($converted->format, 'FMT_COMMON', 'format is set');
    };


    ## test when converter returns nothing
    {
        Test::MockObject->fake_module('Finance::Currency::Convert::WebserviceX' => (
            new => sub {return bless {}, shift},
            convert => sub {}
        ));

        my $currency = Data::Currency->new(1.23, 'USD');
        my $converted = $currency->convert('CAD');
        isa_ok($converted, 'Data::Currency');
        is($converted->value, 0, 'value is 0 when converter fails');
        is($converted->stringify, '$0.00', 'stringify to value');
        is($converted->code, 'CAD', 'code is set');
        is($converted->format, 'FMT_COMMON', 'format is not set');
    };


    ## throw exception when no code is set during convert
    {
        my $currency = Data::Currency->new(1.23);

        eval {
            $currency->{'code'} = undef;
            $currency->convert('CAD');
        };
        like($@, qr/invalid currency code source/i);
    };


    ## throw exception when no code is passed to convert
    {
        my $currency = Data::Currency->new(1.23, 'CAD');

        eval {
            $currency->convert;
        };
        like($@, qr/invalid currency code target/i);
    };


    ## return self if to is same as from
    {
        my $currency = Data::Currency->new(1.23, 'USD');
        isa_ok($currency, 'Data::Currency');
        is($currency->value, 1.23, 'value was set');
        is($currency->stringify, '$1.23', 'stringify to string');
        is($currency->code, 'USD', 'code is set');
        is($currency->format, 'FMT_COMMON', 'format is set');
        is($currency->converter, undef, 'converter not defined');

        my $converted = $currency->convert('USD');
        is($currency->converter, undef, 'converter not defined');
        is(refaddr $converted, refaddr $currency, 'return self if codes are the same');
        isa_ok($converted, 'Data::Currency');
        is($converted->value, 1.23, 'value was set');
        is($converted->stringify, '$1.23', 'stringify to string');
        is($converted->code, 'USD', 'code is set');
        is($converted->format, 'FMT_COMMON', 'format is not set');
    };
};


## test loading of utf8
{
    local $] = 5.007999;

    my $currency = Data::Currency->new(1.23);
    isa_ok($currency, 'Data::Currency');
    is($currency->stringify, '$1.23', 'still got format');
};


## set converter_class to nothing, and put it back
{
    Data::Currency->converter_class(undef);
    is(Data::Currency->converter_class, undef, 'unset converter_class');

    Data::Currency->converter_class('Data::Currency');
    is(Data::Currency->converter_class, 'Data::Currency', 'set converter_class');
};
