From 97c860704f6be972947f630fb3fadab812a396b2 Mon Sep 17 00:00:00 2001
From: Olli-Antti Kivilahti <olli-antti.kivilahti@jns.fi>
Date: Mon, 2 Nov 2015 20:25:56 +0200
Subject: [PATCH] Bug 13906 - TestObjectFactory(ies) for Koha objects. Holds
 test factory for v3.16

---
 t/lib/TestObjects/BiblioFactory.pm  |   2 +
 t/lib/TestObjects/HoldFactory.pm    | 206 ++++++++++++++++++++++++++++++++++++
 t/lib/TestObjects/ItemFactory.pm    |  18 ++--
 t/lib/TestObjects/ObjectFactory.pm  |   6 ++
 t/lib/TestObjects/objectFactories.t |  56 ++++++++++
 5 files changed, 279 insertions(+), 9 deletions(-)
 create mode 100644 t/lib/TestObjects/HoldFactory.pm

diff --git a/t/lib/TestObjects/BiblioFactory.pm b/t/lib/TestObjects/BiblioFactory.pm
index 11874e7..e981f6d 100644
--- a/t/lib/TestObjects/BiblioFactory.pm
+++ b/t/lib/TestObjects/BiblioFactory.pm
@@ -41,6 +41,7 @@ sub getObjectType {
                         {'biblio.title' => 'I wish I met your mother',
                          'biblio.author'   => 'Pertti Kurikka',
                          'biblio.copyrightdate' => '1960',
+                         'biblio.biblionumber' => 1212,
                          'biblioitems.isbn'     => '9519671580',
                          'biblioitems.itemtype' => 'BK',
                         },
@@ -71,6 +72,7 @@ sub handleTestObject {
     ##First see if the given Record already exists in the DB. For testing purposes we use the isbn as the UNIQUE identifier.
     my $resultset = Koha::Database->new()->schema()->resultset('Biblioitem');
     my $existingBiblio = $resultset->search({isbn => $object->{"biblioitems.isbn"}})->next();
+    $existingBiblio = $resultset->search({biblionumber => $object->{"biblio.biblionumber"}})->next() unless $existingBiblio;
     my ($record, $biblionumber, $biblioitemnumber);
     unless ($existingBiblio) {
         $record = C4::Biblio::TransformKohaToMarc($object);
diff --git a/t/lib/TestObjects/HoldFactory.pm b/t/lib/TestObjects/HoldFactory.pm
new file mode 100644
index 0000000..89cb84b
--- /dev/null
+++ b/t/lib/TestObjects/HoldFactory.pm
@@ -0,0 +1,206 @@
+package t::lib::TestObjects::HoldFactory;
+
+# Copyright KohaSuomi 2015
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+use Modern::Perl;
+use Carp;
+use DateTime;
+
+use C4::Context;
+use Koha::Database;
+use C4::Circulation;
+use C4::Members;
+use C4::Items;
+use C4::Reserves;
+use Koha::Borrowers;
+use Koha::Biblios;
+use Koha::BiblioItems;
+use Koha::Items;
+use Koha::Checkouts;
+
+use t::lib::TestObjects::BorrowerFactory;
+use t::lib::TestObjects::ItemFactory;
+use t::lib::TestObjects::BiblioFactory;
+
+use base qw(t::lib::TestObjects::ObjectFactory);
+
+sub new {
+    my ($class) = @_;
+
+    my $self = {};
+    bless($self, $class);
+    return $self;
+}
+
+sub getDefaultHashKey {
+    return ['reservenotes'];
+}
+sub getObjectType {
+    return 'HASH';
+}
+
+=head t::lib::TestObjects::HoldFactory::createTestGroup( $data [, $hashKey], @stashes )
+
+    my $holds = t::lib::TestObjects::HoldFactory->createTestGroup([
+                        {#Hold params
+                        },
+                        {#More hold params
+                        },
+                    ], undef, $testContext1, $testContext2, $testContext3);
+
+    t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext1);
+
+@PARAM1, ARRAY of HASHes.
+  [ {
+        cardnumber        => '167Azava0001', #Borrower's cardnumber
+        isbn              => '971040323123', #ISBN of the Biblio, even if the record normally doesn't have a ISBN, you must mock one on it.
+        barcode           => undef || '911N12032',    #Item's barcode, if this is an Item-level hold.
+        branchcode        => 'CPL',
+        waitingdate       => undef || '2015-01-15', #Since when has this hold been waiting for pickup?
+    },
+    {
+        ...
+    }
+  ]
+@PARAM2, String, the HASH-element to use as the returning HASHes key.
+@PARAM3-5 HASHRef of test contexts. You can save the given objects to multiple
+                test contexts. Usually one is enough. These test contexts are
+                used to help tear down DB changes.
+@RETURNS HASHRef of $hashKey => HASH-objects representing koha.reserves-table columns
+                The HASH is keyed with <reservenotes>, or the given $hashKey.
+    Example: {
+        '11A001-971040323123-43' => {...},
+        'my-nice-hold' => {...},
+        ...
+    }
+}
+=cut
+
+sub handleTestObject {
+    my ($class, $object, $stashes) = @_;
+
+    C4::Reserves::AddReserve($object->{branchcode} || 'CPL',
+                                        $object->{borrower}->borrowernumber,
+                                        $object->{biblio}->{biblionumber},
+                                        undef, #constraint
+                                        undef, #bibitems
+                                        undef, #priority
+                                        $object->{reservedate}, #resdate
+                                        $object->{expirationdate}, #expdate
+                                        $object->{reservenotes}, #notes
+                                        undef, #title
+                                        ($object->{item} ? $object->{item}->itemnumber : undef), #checkitem
+                                        undef, #found
+                            );
+    my $reserve_id = C4::Reserves::GetReserveId({biblionumber   => $object->{biblio}->{biblionumber},
+                                            itemnumber     => ($object->{item} ? $object->{item}->itemnumber : undef),
+                                            borrowernumber => $object->{borrower}->borrowernumber,
+                                        });
+    unless ($reserve_id) {
+        die "HoldFactory->handleTestObject():> Couldn't create a reserve. for isbn => ".$object->{isbn}.", barcode => ".$object->{barcode}.
+            ", item => ".($object->{barcode} ? $object->{barcode} : '')."\n";
+    }
+    my $hold = C4::Reserves::GetReserve($reserve_id);
+    foreach my $key (keys $object) {
+        $hold->{$key} = $object->{$key};
+    }
+
+    if ($object->{waitingdate}) {
+        eval {
+            C4::Reserves::ModReserveAffect($hold->{item}->itemnumber, $hold->{borrower}->borrowernumber);
+
+            #Modify the waitingdate. An ugly hack for a ugly module. Bear with me my men!
+            my $dbh = C4::Context->dbh;
+            my $query = "UPDATE reserves SET waitingdate = ? WHERE  reserve_id = ?";
+            my $sth = $dbh->prepare($query);
+            $sth->execute( $hold->{waitingdate}, $hold->{reserve_id} );
+        };
+        if ($@) {
+            warn "HoldFactory->handleTestObject():> Error when setting the waitingdate for ".$class->getHashKey($object)."$@";
+        }
+    }
+
+    return $hold;
+}
+
+=head validateAndPopulateDefaultValues
+@OVERLOAD
+
+Validates given Object parameters and makes sure that critical fields are given
+and populates defaults for missing values.
+=cut
+
+sub validateAndPopulateDefaultValues {
+    my ($self, $object, $hashKey, $stashes) = @_;
+    $self->SUPER::validateAndPopulateDefaultValues($object, $hashKey);
+
+    unless ($object->{cardnumber}) {
+        croak __PACKAGE__.":> Mandatory parameter 'cardnumber' missing.";
+    }
+    unless ($object->{isbn}) {
+        croak __PACKAGE__.":> Mandatory parameter 'isbn' missing.";
+    }
+
+    my $borrowers = t::lib::TestObjects::BorrowerFactory->createTestGroup(
+                                {cardnumber => $object->{cardnumber}},
+                                undef, @$stashes);
+    my $borrower = $borrowers->{ $object->{cardnumber} };
+    $object->{borrower} = $borrower if $borrower;
+
+    my $biblios = t::lib::TestObjects::BiblioFactory->createTestGroup({"biblio.title" => "Test holds' Biblio",
+                                                                           "biblioitems.isbn" => $object->{isbn}},
+                                                                          undef, @$stashes);
+    my $biblio = $biblios->{ $object->{isbn} || '971-972-call-me' }; #Get default isbn or the given one
+    $object->{biblio} = $biblio if $biblio;
+
+    #Get test Item
+    if ($object->{barcode}) {
+        my $items = t::lib::TestObjects::ItemFactory->createTestGroup({barcode => $object->{barcode}, isbn => $object->{isbn}}, undef, @$stashes);
+        my $item  = $items->{ $object->{barcode} };
+        $object->{item} = $item;
+    }
+
+    return $object;
+}
+
+=head
+
+    my $objects = createTestGroup();
+    ##Do funky stuff
+    deleteTestGroup($records);
+
+Removes the given test group from the DB.
+
+=cut
+
+sub deleteTestGroup {
+    my ($self, $objects) = @_;
+
+    #For some reason DBIx cannot delete from OldReserves-table so using DBI and not losing sleep
+    my $dbh = C4::Context->dbh();
+    my $del_old_sth = $dbh->prepare("DELETE FROM old_reserves WHERE reserve_id = ?");
+    my $del_sth = $dbh->prepare("DELETE FROM reserves WHERE reserve_id = ?");
+
+    while( my ($key, $object) = each %$objects) {
+        $del_sth->execute($object->{reserve_id});
+        $del_old_sth->execute($object->{reserve_id});
+    }
+}
+
+1;
diff --git a/t/lib/TestObjects/ItemFactory.pm b/t/lib/TestObjects/ItemFactory.pm
index f75fb97..4481817 100644
--- a/t/lib/TestObjects/ItemFactory.pm
+++ b/t/lib/TestObjects/ItemFactory.pm
@@ -23,6 +23,7 @@ use Carp;
 
 use C4::Items;
 use Koha::Biblios;
+use Koha::BiblioItems;
 use Koha::Items;
 use Koha::Checkouts;
 
@@ -64,17 +65,16 @@ sub handleTestObject {
 
     #Look for the parent biblio, if we don't find one, create a default one.
     my ($biblionumber, $biblioitemnumber, $itemnumber);
-    my $biblio;
-    $biblio = Koha::Biblios->find({biblionumber => $object->{biblionumber}}) if $object->{biblionumber};
-    unless ($biblio) {
-        my $biblios = t::lib::TestObjects::BiblioFactory->createTestGroup({"biblio.title" => "Test Items' Biblio",},
+    my $biblios = t::lib::TestObjects::BiblioFactory->createTestGroup({"biblio.title" => "Test Items' Biblio",
+                                                                       "biblioitems.isbn" => $object->{isbn},
+                                                                       "biblio.biblionumber" => $object->{biblionumber}},
                                                                           undef, @$stashes);
-        $biblio = $biblios->{'971-972-call-me'};
-        $object->{biblionumber} = $biblio->{biblionumber};
-    }
-    else {
-        $object->{biblionumber} = $biblio->biblionumber;
+    my $biblio;
+    foreach my $k (keys %$biblios) {
+        $biblio = $biblios->{$k};
     }
+    $object->{biblionumber} = $biblio->{biblionumber};
+    $object->{biblio} = $biblio;
 
     #Ok we got a biblio, now we can add an Item for it. First see if the Item already exists.
     my $item;
diff --git a/t/lib/TestObjects/ObjectFactory.pm b/t/lib/TestObjects/ObjectFactory.pm
index 791085d..f84a607 100644
--- a/t/lib/TestObjects/ObjectFactory.pm
+++ b/t/lib/TestObjects/ObjectFactory.pm
@@ -171,6 +171,11 @@ sub tearDownTestContext {
         t::lib::TestObjects::BorrowerFactory->deleteTestGroup($stash->{borrower});
         delete $stash->{borrower};
     }
+    if ($stash->{hold}) {
+        require t::lib::TestObjects::HoldFactory;
+        t::lib::TestObjects::HoldFactory->deleteTestGroup($stash->{hold});
+        delete $stash->{hold};
+    }
     if ($stash->{letterTemplate}) {
         require t::lib::TestObjects::LetterTemplateFactory;
         t::lib::TestObjects::LetterTemplateFactory->deleteTestGroup($stash->{letterTemplate});
@@ -199,6 +204,7 @@ sub getHashKey {
     my ($class, $object, $primaryKey, $hashKeys) = @_;
 
     my @collectedHashKeys;
+    $hashKeys = $class->getDefaultHashKey unless $hashKeys;
     $hashKeys = [$hashKeys] unless ref($hashKeys) eq 'ARRAY';
     foreach my $hashKey (@$hashKeys) {
         if (ref($object) eq 'HASH') {
diff --git a/t/lib/TestObjects/objectFactories.t b/t/lib/TestObjects/objectFactories.t
index d29923a..2c73db3 100644
--- a/t/lib/TestObjects/objectFactories.t
+++ b/t/lib/TestObjects/objectFactories.t
@@ -49,6 +49,7 @@ use Koha::Serial::Subscription::Frequencies;
 use Koha::Serial::Subscription::Numberpatterns;
 use Koha::Serial::Serials;
 use t::lib::TestObjects::SystemPreferenceFactory;
+use t::lib::TestObjects::HoldFactory
 use C4::Context;
 use t::lib::TestObjects::MessageQueueFactory;
 use C4::Letters;
@@ -58,6 +59,61 @@ my $testContext = {}; #Gather all created Objects here so we can finally remove
 
 
 
+########## HoldFactory subtests ##########
+subtest 't::lib::TestObjects::HoldFactory' => \&testHoldFactory;
+sub testHoldFactory {
+    my $subtestContext = {};
+    ##Create and Delete using dependencies in the $testContext instantiated in previous subtests.
+    my $holds = t::lib::TestObjects::HoldFactory->createTestGroup(
+                        {cardnumber        => '1A01',
+                         isbn              => '971',
+                         barcode           => '1N01',
+                         branchcode        => 'CPL',
+                         waitingdate       => '2015-01-15',
+                        },
+                        ['cardnumber','isbn','barcode'], $subtestContext);
+
+    is($holds->{'1A01-971-1N01'}->{branchcode},
+       'CPL',
+       "Hold '1A01-971-1N01' pickup location is 'CPL'.");
+    is($holds->{'1A01-971-1N01'}->{waitingdate},
+       '2015-01-15',
+       "Hold '1A01-971-1N01' waiting date is '2015-01-15'.");
+
+    #using the default test hold identifier reservenotes to distinguish hard-to-identify holds.
+    my $holds2 = t::lib::TestObjects::HoldFactory->createTestGroup([
+                        {cardnumber        => '1A01',
+                         isbn              => '971',
+                         barcode           => '1N02',
+                         branchcode        => 'CPL',
+                         reservenotes      => 'nice hold',
+                        },
+                        {cardnumber        => '1A01',
+                         barcode           => '1N03',
+                         isbn              => '971',
+                         branchcode        => 'CPL',
+                         reservenotes      => 'better hold',
+                        },
+                    ], undef, $subtestContext);
+
+    is($holds2->{'nice hold'}->{branchcode},
+        'CPL',
+        "Hold 'nice hold' pickup location is 'CPL'.");
+    is($holds2->{'nice hold'}->{borrower}->cardnumber,
+        '1A01',
+        "Hold 'nice hold' cardnumber is '1A01'.");
+    is($holds2->{'better hold'}->{isbn},
+        '971',
+        "Hold 'better hold' isbn '971'.");
+
+    t::lib::TestObjects::HoldFactory->deleteTestGroup($subtestContext->{hold});
+
+    my $holds_deleted = C4::Reserves::GetReservesFromBiblionumber({biblionumber => $holds->{'1A01-971-1N01'}->{biblio}->{biblionumber}});
+    ok (not(@$holds_deleted), "Holds deleted");
+};
+
+
+
 ########## FileFactory subtests ##########
 subtest 't::lib::TestObjects::FileFactory' => \&testFileFactory;
 sub testFileFactory {
-- 
1.9.1