diff --git a/t/Shop/Tax.t b/t/Shop/Tax.t index 7fbb8d359..cb7380105 100644 --- a/t/Shop/Tax.t +++ b/t/Shop/Tax.t @@ -15,691 +15,123 @@ use FindBin; use strict; -use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/lib"; use Test::More; -use Test::Deep; -use Exception::Class; -use Data::Dumper; - use WebGUI::Test; # Must use this before any other WebGUI modules use WebGUI::Session; -use WebGUI::Text; -use WebGUI::Shop::Cart; -use WebGUI::Shop::AddressBook; #---------------------------------------------------------------------------- # Init my $session = WebGUI::Test->session; + #---------------------------------------------------------------------------- # Tests -my $addExceptions = getAddExceptions($session); +my $tests = 10; +plan tests => $tests + 1; # Add initial use_ok test -my $tests = 78 + 2*scalar(@{$addExceptions}); -plan tests => 1 + $tests; #---------------------------------------------------------------------------- -# put your tests here - -my $loaded = use_ok('WebGUI::Shop::TaxDriver::Generic'); - -my $storage; -my ($taxableDonation, $taxFreeDonation); +my $loaded = use_ok('WebGUI::Shop::Tax'); SKIP: { - skip 'Unable to load module WebGUI::Shop::TaxDriver::Generic', $tests unless $loaded; + skip 'Unable to load module WebGUI::Shop::Tax', $tests unless $loaded; ####################################################################### # - # new + # new # ####################################################################### - my $taxer = WebGUI::Shop::TaxDriver::Generic->new($session); + eval { my $tax = WebGUI::Shop::Tax->new( ) }; - isa_ok($taxer, 'WebGUI::Shop::TaxDriver::Generic'); + my $e = Exception::Class->caught(); + isa_ok( $e, 'WebGUI::Error::InvalidParam', 'new: throws error when no session object is passed' ); + is( $e->error, 'Need a session.', 'add: correct message for ommitted session object' ); - isa_ok($taxer->session, 'WebGUI::Session', 'session method returns a session object'); + my $tax = WebGUI::Shop::Tax->new( $session ); - is($session->getId, $taxer->session->getId, 'session method returns OUR session object'); + isa_ok( $tax, 'WebGUI::Shop::Tax', 'constructor returns instance of WebGUI::Shop::Tax' ); ####################################################################### # - # getItems + # session # ####################################################################### - my $taxIterator = $taxer->getItems; + isa_ok( $tax->session, 'WebGUI::Session', 'session method returns a session object'); - isa_ok($taxIterator, 'WebGUI::SQL::ResultSet'); - - is($taxIterator->rows, 0, 'WebGUI ships with no predefined tax data'); + is( $session->getId, $tax->session->getId, 'session method returns OUR session object'); ####################################################################### # - # add + # calculate # ####################################################################### - my $e; - - eval{$taxer->add()}; - - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidParam', 'add: correct type of exception thrown for missing hashref'); - is($e->error, 'Must pass in a hashref of params', 'add: correct message for a missing hashref'); - - foreach my $inputSet ( @{ $addExceptions } ){ - eval{$taxer->add($inputSet->{args})}; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidParam', 'add: '.$inputSet->{comment}); - cmp_deeply( - $e, - methods( - error => $inputSet->{error}, - param => $inputSet->{param}, - ), - 'add: '.$inputSet->{comment}, - ); - } - - my $taxData = { - country => 'USA', - state => 'OR', - taxRate => '0', - }; - - my $oregonTaxId = $taxer->add($taxData); - - ok($session->id->valid($oregonTaxId), 'add method returns a valid GUID'); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 1, 'add added only 1 row to the tax table'); - - my $addedData = $taxIterator->hashRef; - $taxData->{taxId} = $oregonTaxId; - $taxData->{city} = undef; - $taxData->{code} = undef; - - cmp_deeply($addedData, $taxData, 'add put the right data into the database for Oregon'); - - $taxData = { - country => 'USA', - state => 'Wisconsin', - city => 'Madcity', - code => '53702', - taxRate => '5', - }; - - my $wisconsinTaxId = $taxer->add($taxData); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 2, 'add added another row to the tax table'); - - $taxData = { - country => 'USA', - state => 'Oregon', - taxRate => '0.1', - }; - - my $dupId = $taxer->add($taxData); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 3, 'add permits adding duplicate information.'); - - ##Madison zip codes: - ##53701-53709 - ##city rate: 0.5% - ##Wisconsin rate 5.0% + # TODO: Figure out how to test this. ####################################################################### # - # getAllItems + # getDriver # ####################################################################### - my $expectedTaxData = [ - { - country => 'USA', - state => 'OR', - city => undef, - code => undef, - taxRate => 0, - }, - { - country => 'USA', - state => 'Wisconsin', - city => 'Madcity', - code => '53702', - taxRate => 5, - }, - { - country => 'USA', - state => 'Oregon', - city => undef, - code => undef, - taxRate => 0.1, - }, - ]; + # Try to get a non-existing plugin + $session->setting->set( 'activeTaxPlugin', 'WebGUI::Shop::TaxDriver::HairgreaseTaxDeduction' ); - cmp_bag( - $taxer->getAllItems, - $expectedTaxData, - 'getAllItems returns the whole set of tax data', - ); + my $driver = $tax->getDriver; + is( $driver, undef, 'getDriver returns undef when the driver cannot be loaded' ); - ####################################################################### - # - # delete - # - ####################################################################### + # Try to get an existing plugin + $session->setting->set( 'activeTaxPlugin', 'WebGUI::Shop::TaxDriver::Generic' ); + $driver = $tax->getDriver; + isa_ok( $driver, 'WebGUI::Shop::TaxDriver::Generic', 'getDriver returns correct plugin' ); - eval{$taxer->delete()}; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidParam', 'delete: error handling for missing hashref'); - is($e->error, 'Must pass in a hashref of params', 'delete: error message for missing hashref'); + $driver = WebGUI::Shop::Tax->getDriver( $session ); + isa_ok( $driver, 'WebGUI::Shop::TaxDriver::Generic', 'getDriver returns correct plugin when called as class method' ); - eval{$taxer->delete({})}; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidParam', 'delete: error handling for missing key in hashref'); - is($e->error, 'Hash ref must contain a taxId key with a defined value', 'delete: error message for missing key in hashref'); - - eval{$taxer->delete({ taxId => undef })}; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidParam', 'delete: error handling for an undefined taxId value'); - is($e->error, 'Hash ref must contain a taxId key with a defined value', 'delete: error message for an undefined taxId value'); - - $taxer->delete({ taxId => $dupId }); - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 2, 'One row was deleted from the tax table, even though another row has duplicate information'); - - $taxer->delete({ taxId => $oregonTaxId }); - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 1, 'Another row was deleted from the tax table'); - - $taxer->delete({ taxId => $session->id->generate }); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 1, 'No rows were deleted from the table since the requested id does not exist'); - is($taxIterator->hashRef->{taxId}, $wisconsinTaxId, 'The correct tax information was deleted'); - - ######################################################################## - ## - ## exportTaxData - ## - ######################################################################## - - $storage = $taxer->exportTaxData(); - isa_ok($storage, 'WebGUI::Storage', 'exportTaxData returns a WebGUI::Storage object'); - is(substr($storage->getPathFrag, 0, 5), 'temp/', 'The storage object is in the temporary area'); - ok(-e $storage->getPath('siteTaxData.csv'), 'siteTaxData.csv file exists in the storage object'); - cmp_ok($storage->getFileSize('siteTaxData.csv'), '!=', 0, 'CSV file is not empty'); - my @fileLines = split /\n+/, $storage->getFileContentsAsScalar('siteTaxData.csv'); - #my @fileLines = (); - my @header = WebGUI::Text::splitCSV($fileLines[0]); - my @expectedHeader = qw/country state city code taxRate/; - cmp_deeply(\@header, \@expectedHeader, 'exportTaxData: header line is correct'); - my @row1 = WebGUI::Text::splitCSV($fileLines[1]); - my $wiData = $taxer->getItems->hashRef; - ##Need to ignore the taxId from the database - cmp_bag([ @{ $wiData }{ @expectedHeader } ], \@row1, 'exportTaxData: first line of data is correct'); - - my $newTaxId = $taxer->add({ - country => 'USA|U.S.A.', - state => 'washington|WA', - taxRate => '7', - code => '', - city => '', - }); - $taxer->delete({taxId => $wisconsinTaxId}); - $storage = $taxer->exportTaxData(); - @fileLines = split /\n+/, $storage->getFileContentsAsScalar('siteTaxData.csv'); - my @row1 = WebGUI::Text::splitCSV($fileLines[1]); - my $wiData = $taxer->getItems->hashRef; - ##Need to ignore the taxId from the database - cmp_bag([ @{ $wiData }{ @expectedHeader } ], \@row1, 'exportTaxData: first line of data is correct'); - - $taxer->delete({taxId => $newTaxId}); - - ####################################################################### - # - # import - # - ####################################################################### - - eval { $taxer->importTaxData(); }; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidParam', 'importTaxData: error handling for an undefined taxId value'); - is($e->error, 'Must provide the path to a file', 'importTaxData: error handling for an undefined taxId value'); - - eval { $taxer->importTaxData('/path/to/nowhere'); }; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: error handling for file that does not exist in the filesystem'); - is($e->error, 'File could not be found', 'importTaxData: error handling for file that does not exist in the filesystem'); - cmp_deeply( - $e, - methods( - brokenFile => '/path/to/nowhere', - ), - 'importTaxData: error handling for file that does not exist in the filesystem', - ); - - my $taxFile = WebGUI::Test->getTestCollateralPath('taxTables/goodTaxTable.csv'); - - SKIP: { - skip 'Root will cause this test to fail since it does not obey file permissions', 3 - if $< == 0; - - my $originalChmod = (stat $taxFile)[2]; - chmod oct(0000), $taxFile; - - eval { $taxer->importTaxData($taxFile); }; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: error handling for file that cannot be read'); - is($e->error, 'File is not readable', 'importTaxData: error handling for file that that cannot be read'); - cmp_deeply( - $e, - methods( - brokenFile => $taxFile, - ), - 'importTaxData: error handling for file that that cannot be read', - ); - - chmod $originalChmod, $taxFile; - - } - - my $expectedTaxData = [ - { - country => 'USA', - state => '', - city => '', - code => '', - taxRate => 0, - }, - { - country => 'USA', - state => 'Wisconsin', - city => '', - code => '', - taxRate => 5, - }, - { - country => 'USA', - state => 'Wisconsin', - city => 'Madison', - code => '53701', - taxRate => 0.5, - }, - ]; - - ok( - $taxer->importTaxData( - $taxFile - ), - 'Good tax data inserted', - ); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 3, 'import: Old data deleted, new data imported'); - cmp_bag( - $taxer->getAllItems, - $expectedTaxData, - 'Correct data inserted.', - ); - - ok( - $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/orderedTaxTable.csv') - ), - 'Reordered tax data inserted', - ); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 3, 'import: Old data deleted, new data imported again'); - cmp_bag( - $taxer->getAllItems, - $expectedTaxData, - 'Correct data inserted, with CSV in different columnar order.', - ); - - ok( - $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/commentedTaxTable.csv') - ), - 'Commented tax data inserted', - ); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 3, 'import: Old data deleted, new data imported the third time'); - cmp_bag( - $taxer->getAllItems, - $expectedTaxData, - 'Correct data inserted, with comments in the CSV file', - ); - - ok( - ! $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/emptyTaxTable.csv') - ), - 'Empty tax data not inserted', - ); - - $taxIterator = $taxer->getItems; - is($taxIterator->rows, 3, 'import: Old data still exists and was not deleted'); - - my $failure; - eval { - $failure = $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/badTaxTable.csv') - ); - }; - ok (!$failure, 'Tax data not imported'); - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: a file with an error on 1 line'); - cmp_deeply( - $e, - methods( - error => 'Error found in the CSV file', - brokenFile => WebGUI::Test->getTestCollateralPath('taxTables/badTaxTable.csv'), - brokenLine => 1, - ), - 'importTaxData: error handling for file with errors in the CSV data', - ); - - eval { - $failure = $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/missingHeaders.csv') - ); - }; - ok (!$failure, 'Tax data not imported when headers are missing'); - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: a file with a missing header column'); - cmp_deeply( - $e, - methods( - error => 'Bad header found in the CSV file', - brokenFile => WebGUI::Test->getTestCollateralPath('taxTables/missingHeaders.csv'), - ), - 'importTaxData: error handling for a file with a missing header', - ); - - eval { - $failure = $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/badHeaders.csv') - ); - }; - ok (!$failure, 'Tax data not imported when headers are wrong'); - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: a file with a bad header column'); - cmp_deeply( - $e, - methods( - error => 'Bad header found in the CSV file', - brokenFile => WebGUI::Test->getTestCollateralPath('taxTables/badHeaders.csv'), - ), - 'importTaxData: error handling for a file with a bad header', - ); - - ok( - $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/alternations.csv') - ), - 'Tax data with alternations inserted', - ); - - my $altData = $taxer->getItems->hashRef; ##Just 1 row - cmp_deeply( - $altData, - { - taxId => ignore, - country => q{U.S.A.,USA}, - state => q{WI,Wisconsin}, - city => q{Madison}, - code => 53701, - taxRate => 0.5, - }, - 'import: Data correctly loaded with alternations' - ); - - ####################################################################### - # - # getTaxRates - # - ####################################################################### - - ##Set up the tax information - $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/largeTaxTable.csv') - ), - my $book = WebGUI::Shop::AddressBook->create($session); - my $taxingAddress = $book->addAddress({ - label => 'taxing', - city => 'Madison', - state => 'WI', - code => '53701', - country => 'USA', - }); - my $taxFreeAddress = $book->addAddress({ - label => 'no tax', - city => 'Portland', - state => 'OR', - code => '97123', - country => 'USA', - }); - my $alternateAddress = $book->addAddress({ - label => 'using alternations', - city => 'Los Angeles', - state => 'CalifornIA', - code => '92801', - country => 'USA', - }); - - eval { $taxer->getTaxRates(); }; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidObject', 'calculate: error handling for not sending a cart'); - cmp_deeply( - $e, - methods( - error => 'Need an address.', - got => '', - expected => 'WebGUI::Shop::Address', - ), - 'importTaxData: error handling for file that does not exist in the filesystem', - ); - - cmp_deeply( - $taxer->getTaxRates($taxingAddress), - [0, 5, 0.5], - 'getTaxRates: return correct data for a state with tax data' - ); - - cmp_deeply( - $taxer->getTaxRates($taxFreeAddress), - [0,0], - 'getTaxRates: return correct data for a state with no tax data' - ); - - cmp_deeply( - $taxer->getTaxRates($alternateAddress), - [0.0, 8.25], #Hits USA and Los Angeles, California using the alternate spelling of the state - 'getTaxRates: return correct data for a state when the address has alternations' - ); - - ####################################################################### - # - # calculate - # - ####################################################################### - - eval { $taxer->getTaxRate(); }; - $e = Exception::Class->caught(); - isa_ok($e, 'WebGUI::Error::InvalidParam', 'getTaxRate: error handling for not sending a sku'); - is($e->error, 'Must pass in a WebGUI::Asset::Sku object', 'getTaxRate: error handling for not sending a sku'); - - ##Build a cart, add some Donation SKUs to it. Set one to be taxable. - - my $cart = WebGUI::Shop::Cart->newBySession($session); - -# is($taxer->calculate($cart), 0, 'calculate returns 0 if there is no shippingAddressId in the cart'); - -# $cart->update({ shippingAddressId => $taxingAddress->getId}); - - ##Set up the tax information - $taxer->importTaxData( - WebGUI::Test->getTestCollateralPath('taxTables/largeTaxTable.csv') - ), - - $taxableDonation = WebGUI::Asset->getRoot($session)->addChild({ - className => 'WebGUI::Asset::Sku::Donation', - title => 'Taxable donation', - defaultPrice => 100.00, - }); - - is($taxer->getTaxRate($taxableDonation), 0, 'calculate returns 0 if there is no shippingAddressId in the cart'); + eval { my $tax = WebGUI::Shop::Tax->getDriver() }; + my $e = Exception::Class->caught(); + isa_ok( $e, 'WebGUI::Error::InvalidParam', 'getDriver throws error when no session object is passed in class context' ); + is( $e->error, 'Need a session.', 'getDriver passes correct message for ommitted session object' ); -# $cart->addItem($taxableDonation); - -# foreach my $item (@{ $cart->getItems }) { -# $item->setQuantity(1); -# } - - my $tax = $taxer->getTaxRate( $taxableDonation, $taxingAddress ); - is($tax, 5.5, 'calculate: simple tax calculation on 1 item in the cart'); - - $cart->update({ shippingAddressId => $taxFreeAddress->getId}); - is($taxer->getTaxRate( $taxableDonation, $taxFreeAddress ), 0, 'calculate: simple tax calculation on 1 item in the cart, tax free location'); - -# foreach my $item (@{ $cart->getItems }) { -# $item->setQuantity(2); -# } -# -# $cart->update({ shippingAddressId => $taxingAddress->getId}); -# is($taxer->calculate($cart), 11, 'calculate: simple tax calculation on 1 item in the cart, qty 2'); - - $taxFreeDonation = WebGUI::Asset->getRoot($session)->addChild({ - className => 'WebGUI::Asset::Sku::Donation', - title => 'Tax Free Donation', - defaultPrice => 100.00, - }); - $taxFreeDonation->setTaxConfiguration( 'WebGUI::Shop::TaxDriver::Generic', { - overrideTaxRate => 1, - taxRateOverride => 0, - }); - -# $cart->addItem($taxFreeDonation); - -# foreach my $item (@{ $cart->getItems }) { -# $item->setQuantity(1); -# } - is($taxer->getTaxRate( $taxFreeDonation, $taxingAddress), 0, 'getTaxRate: tax rate override should override tax derived from address'); - -# my $remoteItem = $cart->addItem($taxableDonation); -# $remoteItem->update({shippingAddressId => $taxFreeAddress->getId}); -# -# foreach my $item (@{ $cart->getItems }) { -# $item->setQuantity(1); -# } -# is($taxer->calculate($cart), 5.5, 'calculate: simple tax calculation on 2 items in the cart, 1 without taxes, 1 shipped to a location with no taxes'); - - ####################################################################### - # - # www_getTaxesAsJson - # - ####################################################################### - - $session->user({userId=>3}); - my $json = $taxer->www_getTaxesAsJson(); - ok($json, 'www_getTaxesAsJson returned something'); - is($session->http->getMimeType, 'application/json', 'MIME type set to application/json'); - my $jsonTax = JSON::from_json($json); - cmp_deeply( - $jsonTax, - { - sort => undef, - startIndex => 0, - totalRecords => 1778, - recordsReturned => 25, - dir => 'asc', - records => array_each({ - taxId=>ignore, - country => 'USA', - state=>ignore, - city=>ignore, - code=>ignore, - taxRate=>re('^\d+(\.\d+)?$') - }), - }, - 'Check major elements of tax JSON', - ); TODO: { - local $TODO = 'More getTaxesAsJson tests'; - ok(0, 'test group privileges to this method'); - ok(0, 'test startIndex variable'); - ok(0, 'test results form variable'); - ok(0, 'test keywords'); + local $TODO = 'test www_ methods'; + ####################################################################### + # + # www_do + # + ####################################################################### + + ####################################################################### + # + # www_manage + # + ####################################################################### + + ####################################################################### + # + # www_setActivePlugin + # + ####################################################################### + + ####################################################################### + # + # www_setActivePluginConfirm + # + ####################################################################### } - - $cart->delete; - $book->delete; - $taxableDonation->purge; - $taxFreeDonation->purge; } -sub getAddExceptions { - my $session = shift; - my $inputValidion = [ - { - args => {}, - error => q{Missing required information.}, - param => q{country}, - comment => q{missing country}, - }, - { - args => {country => undef}, - error => q{Missing required information.}, - param => q{country}, - comment => q{undef country}, - }, - { - args => {country => ''}, - error => q{Missing required information.}, - param => q{country}, - comment => q{empty country}, - }, - { - args => {country => 'USA'}, - error => q{Missing required information.}, - param => q{taxRate}, - comment => q{missing taxRate}, - }, - { - args => {country => 'USA', taxRate => undef}, - error => q{Missing required information.}, - param => q{taxRate}, - comment => q{empty taxRate}, - }, - ]; -} #---------------------------------------------------------------------------- # Cleanup END { - $session->db->write('delete from tax_generic_rates'); - $session->db->write('delete from cart'); - $session->db->write('delete from addressBook'); - $session->db->write('delete from address'); - $storage->delete; - if (defined $taxableDonation and ref $taxableDonation eq 'WebGUI::Sku::Donation') { - $taxableDonation->purge; - } - - if (defined $taxFreeDonation and ref $taxFreeDonation eq 'WebGUI::Sku::Donation') { - $taxFreeDonation->purge; - } } +#vim:ft=perl diff --git a/t/Shop/TaxDriver/Generic.t b/t/Shop/TaxDriver/Generic.t new file mode 100644 index 000000000..7fbb8d359 --- /dev/null +++ b/t/Shop/TaxDriver/Generic.t @@ -0,0 +1,705 @@ +# vim:syntax=perl +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2009 Plain Black Corporation. +#------------------------------------------------------------------- +# Please read the legal notices (docs/legal.txt) and the license +# (docs/license.txt) that came with this distribution before using +# this software. +#------------------------------------------------------------------ +# http://www.plainblack.com info@plainblack.com +#------------------------------------------------------------------ + +# Write a little about what this script tests. +# +# + +use FindBin; +use strict; +use lib "$FindBin::Bin/../lib"; +use Test::More; +use Test::Deep; +use Exception::Class; +use Data::Dumper; + +use WebGUI::Test; # Must use this before any other WebGUI modules +use WebGUI::Session; +use WebGUI::Text; +use WebGUI::Shop::Cart; +use WebGUI::Shop::AddressBook; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; + +#---------------------------------------------------------------------------- +# Tests + +my $addExceptions = getAddExceptions($session); + +my $tests = 78 + 2*scalar(@{$addExceptions}); +plan tests => 1 + $tests; + +#---------------------------------------------------------------------------- +# put your tests here + +my $loaded = use_ok('WebGUI::Shop::TaxDriver::Generic'); + +my $storage; +my ($taxableDonation, $taxFreeDonation); + +SKIP: { + + skip 'Unable to load module WebGUI::Shop::TaxDriver::Generic', $tests unless $loaded; + + ####################################################################### + # + # new + # + ####################################################################### + + my $taxer = WebGUI::Shop::TaxDriver::Generic->new($session); + + isa_ok($taxer, 'WebGUI::Shop::TaxDriver::Generic'); + + isa_ok($taxer->session, 'WebGUI::Session', 'session method returns a session object'); + + is($session->getId, $taxer->session->getId, 'session method returns OUR session object'); + + ####################################################################### + # + # getItems + # + ####################################################################### + + my $taxIterator = $taxer->getItems; + + isa_ok($taxIterator, 'WebGUI::SQL::ResultSet'); + + is($taxIterator->rows, 0, 'WebGUI ships with no predefined tax data'); + + ####################################################################### + # + # add + # + ####################################################################### + + my $e; + + eval{$taxer->add()}; + + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidParam', 'add: correct type of exception thrown for missing hashref'); + is($e->error, 'Must pass in a hashref of params', 'add: correct message for a missing hashref'); + + foreach my $inputSet ( @{ $addExceptions } ){ + eval{$taxer->add($inputSet->{args})}; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidParam', 'add: '.$inputSet->{comment}); + cmp_deeply( + $e, + methods( + error => $inputSet->{error}, + param => $inputSet->{param}, + ), + 'add: '.$inputSet->{comment}, + ); + } + + my $taxData = { + country => 'USA', + state => 'OR', + taxRate => '0', + }; + + my $oregonTaxId = $taxer->add($taxData); + + ok($session->id->valid($oregonTaxId), 'add method returns a valid GUID'); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 1, 'add added only 1 row to the tax table'); + + my $addedData = $taxIterator->hashRef; + $taxData->{taxId} = $oregonTaxId; + $taxData->{city} = undef; + $taxData->{code} = undef; + + cmp_deeply($addedData, $taxData, 'add put the right data into the database for Oregon'); + + $taxData = { + country => 'USA', + state => 'Wisconsin', + city => 'Madcity', + code => '53702', + taxRate => '5', + }; + + my $wisconsinTaxId = $taxer->add($taxData); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 2, 'add added another row to the tax table'); + + $taxData = { + country => 'USA', + state => 'Oregon', + taxRate => '0.1', + }; + + my $dupId = $taxer->add($taxData); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 3, 'add permits adding duplicate information.'); + + ##Madison zip codes: + ##53701-53709 + ##city rate: 0.5% + ##Wisconsin rate 5.0% + + ####################################################################### + # + # getAllItems + # + ####################################################################### + + my $expectedTaxData = [ + { + country => 'USA', + state => 'OR', + city => undef, + code => undef, + taxRate => 0, + }, + { + country => 'USA', + state => 'Wisconsin', + city => 'Madcity', + code => '53702', + taxRate => 5, + }, + { + country => 'USA', + state => 'Oregon', + city => undef, + code => undef, + taxRate => 0.1, + }, + ]; + + cmp_bag( + $taxer->getAllItems, + $expectedTaxData, + 'getAllItems returns the whole set of tax data', + ); + + ####################################################################### + # + # delete + # + ####################################################################### + + eval{$taxer->delete()}; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidParam', 'delete: error handling for missing hashref'); + is($e->error, 'Must pass in a hashref of params', 'delete: error message for missing hashref'); + + eval{$taxer->delete({})}; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidParam', 'delete: error handling for missing key in hashref'); + is($e->error, 'Hash ref must contain a taxId key with a defined value', 'delete: error message for missing key in hashref'); + + eval{$taxer->delete({ taxId => undef })}; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidParam', 'delete: error handling for an undefined taxId value'); + is($e->error, 'Hash ref must contain a taxId key with a defined value', 'delete: error message for an undefined taxId value'); + + $taxer->delete({ taxId => $dupId }); + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 2, 'One row was deleted from the tax table, even though another row has duplicate information'); + + $taxer->delete({ taxId => $oregonTaxId }); + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 1, 'Another row was deleted from the tax table'); + + $taxer->delete({ taxId => $session->id->generate }); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 1, 'No rows were deleted from the table since the requested id does not exist'); + is($taxIterator->hashRef->{taxId}, $wisconsinTaxId, 'The correct tax information was deleted'); + + ######################################################################## + ## + ## exportTaxData + ## + ######################################################################## + + $storage = $taxer->exportTaxData(); + isa_ok($storage, 'WebGUI::Storage', 'exportTaxData returns a WebGUI::Storage object'); + is(substr($storage->getPathFrag, 0, 5), 'temp/', 'The storage object is in the temporary area'); + ok(-e $storage->getPath('siteTaxData.csv'), 'siteTaxData.csv file exists in the storage object'); + cmp_ok($storage->getFileSize('siteTaxData.csv'), '!=', 0, 'CSV file is not empty'); + my @fileLines = split /\n+/, $storage->getFileContentsAsScalar('siteTaxData.csv'); + #my @fileLines = (); + my @header = WebGUI::Text::splitCSV($fileLines[0]); + my @expectedHeader = qw/country state city code taxRate/; + cmp_deeply(\@header, \@expectedHeader, 'exportTaxData: header line is correct'); + my @row1 = WebGUI::Text::splitCSV($fileLines[1]); + my $wiData = $taxer->getItems->hashRef; + ##Need to ignore the taxId from the database + cmp_bag([ @{ $wiData }{ @expectedHeader } ], \@row1, 'exportTaxData: first line of data is correct'); + + my $newTaxId = $taxer->add({ + country => 'USA|U.S.A.', + state => 'washington|WA', + taxRate => '7', + code => '', + city => '', + }); + $taxer->delete({taxId => $wisconsinTaxId}); + $storage = $taxer->exportTaxData(); + @fileLines = split /\n+/, $storage->getFileContentsAsScalar('siteTaxData.csv'); + my @row1 = WebGUI::Text::splitCSV($fileLines[1]); + my $wiData = $taxer->getItems->hashRef; + ##Need to ignore the taxId from the database + cmp_bag([ @{ $wiData }{ @expectedHeader } ], \@row1, 'exportTaxData: first line of data is correct'); + + $taxer->delete({taxId => $newTaxId}); + + ####################################################################### + # + # import + # + ####################################################################### + + eval { $taxer->importTaxData(); }; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidParam', 'importTaxData: error handling for an undefined taxId value'); + is($e->error, 'Must provide the path to a file', 'importTaxData: error handling for an undefined taxId value'); + + eval { $taxer->importTaxData('/path/to/nowhere'); }; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: error handling for file that does not exist in the filesystem'); + is($e->error, 'File could not be found', 'importTaxData: error handling for file that does not exist in the filesystem'); + cmp_deeply( + $e, + methods( + brokenFile => '/path/to/nowhere', + ), + 'importTaxData: error handling for file that does not exist in the filesystem', + ); + + my $taxFile = WebGUI::Test->getTestCollateralPath('taxTables/goodTaxTable.csv'); + + SKIP: { + skip 'Root will cause this test to fail since it does not obey file permissions', 3 + if $< == 0; + + my $originalChmod = (stat $taxFile)[2]; + chmod oct(0000), $taxFile; + + eval { $taxer->importTaxData($taxFile); }; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: error handling for file that cannot be read'); + is($e->error, 'File is not readable', 'importTaxData: error handling for file that that cannot be read'); + cmp_deeply( + $e, + methods( + brokenFile => $taxFile, + ), + 'importTaxData: error handling for file that that cannot be read', + ); + + chmod $originalChmod, $taxFile; + + } + + my $expectedTaxData = [ + { + country => 'USA', + state => '', + city => '', + code => '', + taxRate => 0, + }, + { + country => 'USA', + state => 'Wisconsin', + city => '', + code => '', + taxRate => 5, + }, + { + country => 'USA', + state => 'Wisconsin', + city => 'Madison', + code => '53701', + taxRate => 0.5, + }, + ]; + + ok( + $taxer->importTaxData( + $taxFile + ), + 'Good tax data inserted', + ); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 3, 'import: Old data deleted, new data imported'); + cmp_bag( + $taxer->getAllItems, + $expectedTaxData, + 'Correct data inserted.', + ); + + ok( + $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/orderedTaxTable.csv') + ), + 'Reordered tax data inserted', + ); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 3, 'import: Old data deleted, new data imported again'); + cmp_bag( + $taxer->getAllItems, + $expectedTaxData, + 'Correct data inserted, with CSV in different columnar order.', + ); + + ok( + $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/commentedTaxTable.csv') + ), + 'Commented tax data inserted', + ); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 3, 'import: Old data deleted, new data imported the third time'); + cmp_bag( + $taxer->getAllItems, + $expectedTaxData, + 'Correct data inserted, with comments in the CSV file', + ); + + ok( + ! $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/emptyTaxTable.csv') + ), + 'Empty tax data not inserted', + ); + + $taxIterator = $taxer->getItems; + is($taxIterator->rows, 3, 'import: Old data still exists and was not deleted'); + + my $failure; + eval { + $failure = $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/badTaxTable.csv') + ); + }; + ok (!$failure, 'Tax data not imported'); + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: a file with an error on 1 line'); + cmp_deeply( + $e, + methods( + error => 'Error found in the CSV file', + brokenFile => WebGUI::Test->getTestCollateralPath('taxTables/badTaxTable.csv'), + brokenLine => 1, + ), + 'importTaxData: error handling for file with errors in the CSV data', + ); + + eval { + $failure = $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/missingHeaders.csv') + ); + }; + ok (!$failure, 'Tax data not imported when headers are missing'); + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: a file with a missing header column'); + cmp_deeply( + $e, + methods( + error => 'Bad header found in the CSV file', + brokenFile => WebGUI::Test->getTestCollateralPath('taxTables/missingHeaders.csv'), + ), + 'importTaxData: error handling for a file with a missing header', + ); + + eval { + $failure = $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/badHeaders.csv') + ); + }; + ok (!$failure, 'Tax data not imported when headers are wrong'); + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidFile', 'importTaxData: a file with a bad header column'); + cmp_deeply( + $e, + methods( + error => 'Bad header found in the CSV file', + brokenFile => WebGUI::Test->getTestCollateralPath('taxTables/badHeaders.csv'), + ), + 'importTaxData: error handling for a file with a bad header', + ); + + ok( + $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/alternations.csv') + ), + 'Tax data with alternations inserted', + ); + + my $altData = $taxer->getItems->hashRef; ##Just 1 row + cmp_deeply( + $altData, + { + taxId => ignore, + country => q{U.S.A.,USA}, + state => q{WI,Wisconsin}, + city => q{Madison}, + code => 53701, + taxRate => 0.5, + }, + 'import: Data correctly loaded with alternations' + ); + + ####################################################################### + # + # getTaxRates + # + ####################################################################### + + ##Set up the tax information + $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/largeTaxTable.csv') + ), + my $book = WebGUI::Shop::AddressBook->create($session); + my $taxingAddress = $book->addAddress({ + label => 'taxing', + city => 'Madison', + state => 'WI', + code => '53701', + country => 'USA', + }); + my $taxFreeAddress = $book->addAddress({ + label => 'no tax', + city => 'Portland', + state => 'OR', + code => '97123', + country => 'USA', + }); + my $alternateAddress = $book->addAddress({ + label => 'using alternations', + city => 'Los Angeles', + state => 'CalifornIA', + code => '92801', + country => 'USA', + }); + + eval { $taxer->getTaxRates(); }; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidObject', 'calculate: error handling for not sending a cart'); + cmp_deeply( + $e, + methods( + error => 'Need an address.', + got => '', + expected => 'WebGUI::Shop::Address', + ), + 'importTaxData: error handling for file that does not exist in the filesystem', + ); + + cmp_deeply( + $taxer->getTaxRates($taxingAddress), + [0, 5, 0.5], + 'getTaxRates: return correct data for a state with tax data' + ); + + cmp_deeply( + $taxer->getTaxRates($taxFreeAddress), + [0,0], + 'getTaxRates: return correct data for a state with no tax data' + ); + + cmp_deeply( + $taxer->getTaxRates($alternateAddress), + [0.0, 8.25], #Hits USA and Los Angeles, California using the alternate spelling of the state + 'getTaxRates: return correct data for a state when the address has alternations' + ); + + ####################################################################### + # + # calculate + # + ####################################################################### + + eval { $taxer->getTaxRate(); }; + $e = Exception::Class->caught(); + isa_ok($e, 'WebGUI::Error::InvalidParam', 'getTaxRate: error handling for not sending a sku'); + is($e->error, 'Must pass in a WebGUI::Asset::Sku object', 'getTaxRate: error handling for not sending a sku'); + + ##Build a cart, add some Donation SKUs to it. Set one to be taxable. + + my $cart = WebGUI::Shop::Cart->newBySession($session); + +# is($taxer->calculate($cart), 0, 'calculate returns 0 if there is no shippingAddressId in the cart'); + +# $cart->update({ shippingAddressId => $taxingAddress->getId}); + + ##Set up the tax information + $taxer->importTaxData( + WebGUI::Test->getTestCollateralPath('taxTables/largeTaxTable.csv') + ), + + $taxableDonation = WebGUI::Asset->getRoot($session)->addChild({ + className => 'WebGUI::Asset::Sku::Donation', + title => 'Taxable donation', + defaultPrice => 100.00, + }); + + is($taxer->getTaxRate($taxableDonation), 0, 'calculate returns 0 if there is no shippingAddressId in the cart'); + + +# $cart->addItem($taxableDonation); + +# foreach my $item (@{ $cart->getItems }) { +# $item->setQuantity(1); +# } + + my $tax = $taxer->getTaxRate( $taxableDonation, $taxingAddress ); + is($tax, 5.5, 'calculate: simple tax calculation on 1 item in the cart'); + + $cart->update({ shippingAddressId => $taxFreeAddress->getId}); + is($taxer->getTaxRate( $taxableDonation, $taxFreeAddress ), 0, 'calculate: simple tax calculation on 1 item in the cart, tax free location'); + +# foreach my $item (@{ $cart->getItems }) { +# $item->setQuantity(2); +# } +# +# $cart->update({ shippingAddressId => $taxingAddress->getId}); +# is($taxer->calculate($cart), 11, 'calculate: simple tax calculation on 1 item in the cart, qty 2'); + + $taxFreeDonation = WebGUI::Asset->getRoot($session)->addChild({ + className => 'WebGUI::Asset::Sku::Donation', + title => 'Tax Free Donation', + defaultPrice => 100.00, + }); + $taxFreeDonation->setTaxConfiguration( 'WebGUI::Shop::TaxDriver::Generic', { + overrideTaxRate => 1, + taxRateOverride => 0, + }); + +# $cart->addItem($taxFreeDonation); + +# foreach my $item (@{ $cart->getItems }) { +# $item->setQuantity(1); +# } + is($taxer->getTaxRate( $taxFreeDonation, $taxingAddress), 0, 'getTaxRate: tax rate override should override tax derived from address'); + +# my $remoteItem = $cart->addItem($taxableDonation); +# $remoteItem->update({shippingAddressId => $taxFreeAddress->getId}); +# +# foreach my $item (@{ $cart->getItems }) { +# $item->setQuantity(1); +# } +# is($taxer->calculate($cart), 5.5, 'calculate: simple tax calculation on 2 items in the cart, 1 without taxes, 1 shipped to a location with no taxes'); + + ####################################################################### + # + # www_getTaxesAsJson + # + ####################################################################### + + $session->user({userId=>3}); + my $json = $taxer->www_getTaxesAsJson(); + ok($json, 'www_getTaxesAsJson returned something'); + is($session->http->getMimeType, 'application/json', 'MIME type set to application/json'); + my $jsonTax = JSON::from_json($json); + cmp_deeply( + $jsonTax, + { + sort => undef, + startIndex => 0, + totalRecords => 1778, + recordsReturned => 25, + dir => 'asc', + records => array_each({ + taxId=>ignore, + country => 'USA', + state=>ignore, + city=>ignore, + code=>ignore, + taxRate=>re('^\d+(\.\d+)?$') + }), + }, + 'Check major elements of tax JSON', + ); + + TODO: { + local $TODO = 'More getTaxesAsJson tests'; + ok(0, 'test group privileges to this method'); + ok(0, 'test startIndex variable'); + ok(0, 'test results form variable'); + ok(0, 'test keywords'); + } + + $cart->delete; + $book->delete; + $taxableDonation->purge; + $taxFreeDonation->purge; +} + +sub getAddExceptions { + my $session = shift; + my $inputValidion = [ + { + args => {}, + error => q{Missing required information.}, + param => q{country}, + comment => q{missing country}, + }, + { + args => {country => undef}, + error => q{Missing required information.}, + param => q{country}, + comment => q{undef country}, + }, + { + args => {country => ''}, + error => q{Missing required information.}, + param => q{country}, + comment => q{empty country}, + }, + { + args => {country => 'USA'}, + error => q{Missing required information.}, + param => q{taxRate}, + comment => q{missing taxRate}, + }, + { + args => {country => 'USA', taxRate => undef}, + error => q{Missing required information.}, + param => q{taxRate}, + comment => q{empty taxRate}, + }, + ]; +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $session->db->write('delete from tax_generic_rates'); + $session->db->write('delete from cart'); + $session->db->write('delete from addressBook'); + $session->db->write('delete from address'); + $storage->delete; + + if (defined $taxableDonation and ref $taxableDonation eq 'WebGUI::Sku::Donation') { + $taxableDonation->purge; + } + + if (defined $taxFreeDonation and ref $taxFreeDonation eq 'WebGUI::Sku::Donation') { + $taxFreeDonation->purge; + } +}