Bug 25303: Add a test for ->reset
[koha.git] / t / db_dependent / Koha / Objects.t
1 #!/usr/bin/perl
2
3 # Copyright 2015 Koha Development team
4 #
5 # This file is part of Koha
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Test::More tests => 23;
23 use Test::Exception;
24 use Test::MockModule;
25 use Test::Warn;
26
27 use Koha::Authority::Types;
28 use Koha::Cities;
29 use Koha::Biblios;
30 use Koha::Patron::Category;
31 use Koha::Patron::Categories;
32 use Koha::Patrons;
33 use Koha::Database;
34
35 use t::lib::TestBuilder;
36
37 use Try::Tiny;
38
39 my $schema = Koha::Database->new->schema;
40 $schema->storage->txn_begin;
41 my $builder = t::lib::TestBuilder->new;
42
43 is( ref(Koha::Authority::Types->find('')), 'Koha::Authority::Type', 'Koha::Objects->find should work if the primary key is an empty string' );
44
45 my @columns = Koha::Patrons->columns;
46 my $borrowernumber_exists = grep { /^borrowernumber$/ } @columns;
47 is( $borrowernumber_exists, 1, 'Koha::Objects->columns should return the table columns' );
48
49 subtest 'find' => sub {
50     plan tests => 6;
51     my $patron = $builder->build({source => 'Borrower'});
52     my $patron_object = Koha::Patrons->find( $patron->{borrowernumber} );
53     is( $patron_object->borrowernumber, $patron->{borrowernumber}, '->find should return the correct object' );
54
55     my @patrons = Koha::Patrons->find( $patron->{borrowernumber} );
56     is(scalar @patrons, 1, '->find in list context returns a value');
57     is($patrons[0]->borrowernumber, $patron->{borrowernumber}, '->find in list context returns the same value as in scalar context');
58
59     my $patrons = {
60         foo => Koha::Patrons->find('foo'),
61         bar => 'baz',
62     };
63     is ($patrons->{foo}, undef, '->find in list context returns undef when no record is found');
64
65     # Test sending undef to find; should not generate a warning
66     warning_is { $patron = Koha::Patrons->find( undef ); }
67         "", "Sending undef does not trigger a DBIx warning";
68     warning_is { $patron = Koha::Patrons->find( undef, undef ); }
69         "", "Sending two undefs does not trigger a DBIx warning too";
70 };
71
72 subtest 'update' => sub {
73     plan tests => 2;
74
75     $builder->build( { source => 'City', value => { city_country => 'UK' } } );
76     $builder->build( { source => 'City', value => { city_country => 'UK' } } );
77     $builder->build( { source => 'City', value => { city_country => 'UK' } } );
78     $builder->build( { source => 'City', value => { city_country => 'France' } } );
79     $builder->build( { source => 'City', value => { city_country => 'France' } } );
80     $builder->build( { source => 'City', value => { city_country => 'Germany' } } );
81     Koha::Cities->search( { city_country => 'UK' } )->update( { city_country => 'EU' } );
82     is( Koha::Cities->search( { city_country => 'EU' } )->count, 3, 'Koha::Objects->update should have updated the 3 rows' );
83     is( Koha::Cities->search( { city_country => 'UK' } )->count, 0, 'Koha::Objects->update should have updated the 3 rows' );
84 };
85
86 subtest 'reset' => sub {
87     plan tests => 3;
88
89     my $patrons = Koha::Patrons->search;
90     my $first_borrowernumber = $patrons->next->borrowernumber;
91     my $second_borrowernumber = $patrons->next->borrowernumber;
92     is( ref( $patrons->reset ), 'Koha::Patrons', 'Koha::Objects->reset should allow chaining' );
93     is( ref( $patrons->reset->next ), 'Koha::Patron', 'Koha::Objects->reset should allow chaining' );
94     is( $patrons->reset->next->borrowernumber, $first_borrowernumber, 'Koha::Objects->reset should work as expected');
95 };
96
97 subtest 'delete' => sub {
98     plan tests => 2;
99
100     my $patron_1 = $builder->build({source => 'Borrower'});
101     my $patron_2 = $builder->build({source => 'Borrower'});
102     is( Koha::Patrons->search({ -or => { borrowernumber => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber}]}})->delete, 2, '');
103     is( Koha::Patrons->search({ -or => { borrowernumber => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber}]}})->count, 0, '');
104 };
105
106 subtest 'new' => sub {
107     plan tests => 2;
108     my $a_cat_code = 'A_CAT_CODE';
109     my $patron_category = Koha::Patron::Category->new( { categorycode => $a_cat_code } )->store;
110     is( Koha::Patron::Categories->find($a_cat_code)->category_type, 'A', 'Koha::Object->new should set the default value' );
111     Koha::Patron::Categories->find($a_cat_code)->delete;
112     $patron_category = Koha::Patron::Category->new( { categorycode => $a_cat_code, category_type => undef } )->store;
113     is( Koha::Patron::Categories->find($a_cat_code)->category_type, 'A', 'Koha::Object->new should set the default value even if the argument exists but is not defined' );
114     Koha::Patron::Categories->find($a_cat_code)->delete;
115 };
116
117 subtest 'find' => sub {
118     plan tests => 4;
119
120     # check find on a single PK
121     my $patron = $builder->build({ source => 'Borrower' });
122     is( Koha::Patrons->find($patron->{borrowernumber})->surname,
123         $patron->{surname}, "Checking an arbitrary patron column after find"
124     );
125     # check find with unique column
126     my $obj = Koha::Patrons->find($patron->{cardnumber}, { key => 'cardnumber' });
127     is( $obj->borrowernumber, $patron->{borrowernumber},
128         'Find with unique column and key specified' );
129     # check find with an additional where clause in the attrs hash
130     # we do not expect to find something now
131     is( Koha::Patrons->find(
132         $patron->{borrowernumber},
133         { where => { surname => { '!=', $patron->{surname} }}},
134     ), undef, 'Additional where clause in find call' );
135
136     is( Koha::Patrons->find(), undef, 'Find returns undef if no params passed' );
137 };
138
139 subtest 'search_related' => sub {
140     plan tests => 8;
141     my $builder   = t::lib::TestBuilder->new;
142     my $patron_1  = $builder->build( { source => 'Borrower' } );
143     my $patron_2  = $builder->build( { source => 'Borrower' } );
144     my $libraries = Koha::Patrons->search( { -or => { borrowernumber => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber} ] } } )->search_related('branchcode');
145     is( ref( $libraries ), 'Koha::Libraries', 'Koha::Objects->search_related should return an instanciated Koha::Objects-based object' );
146     is( $libraries->count,            2,                       'Koha::Objects->search_related should work as expected' );
147     is( $libraries->next->branchcode, $patron_1->{branchcode}, 'Koha::Objects->search_related should work as expected' );
148     is( $libraries->next->branchcode, $patron_2->{branchcode}, 'Koha::Objects->search_related should work as expected' );
149
150     my @libraries = Koha::Patrons->search( { -or => { borrowernumber => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber} ] } } )->search_related('branchcode');
151     is( ref( $libraries[0] ),      'Koha::Library',         'Koha::Objects->search_related should return a list of Koha::Object-based objects' );
152     is( scalar(@libraries),        2,                       'Koha::Objects->search_related should work as expected' );
153     is( $libraries[0]->branchcode, $patron_1->{branchcode}, 'Koha::Objects->search_related should work as expected' );
154     is( $libraries[1]->branchcode, $patron_2->{branchcode}, 'Koha::Objects->search_related should work as expected' );
155 };
156
157 subtest 'single' => sub {
158     plan tests => 2;
159     my $builder   = t::lib::TestBuilder->new;
160     my $patron_1  = $builder->build( { source => 'Borrower' } );
161     my $patron_2  = $builder->build( { source => 'Borrower' } );
162     my $patron = Koha::Patrons->search({}, { rows => 1 })->single;
163     is(ref($patron), 'Koha::Patron', 'Koha::Objects->single returns a single Koha::Patron object.');
164     warning_like { Koha::Patrons->search->single } qr/SQL that returns multiple rows/,
165     "Warning is presented if single is used for a result with multiple rows.";
166 };
167
168 subtest 'last' => sub {
169     plan tests => 3;
170     my $builder = t::lib::TestBuilder->new;
171     my $patron_1  = $builder->build( { source => 'Borrower' } );
172     my $patron_2  = $builder->build( { source => 'Borrower' } );
173     my $last_patron = Koha::Patrons->search->last;
174     is( $last_patron->borrowernumber, $patron_2->{borrowernumber}, '->last should return the last inserted patron' );
175     $last_patron = Koha::Patrons->search({ borrowernumber => $patron_1->{borrowernumber} })->last;
176     is( $last_patron->borrowernumber, $patron_1->{borrowernumber}, '->last should work even if there is only 1 result' );
177     $last_patron = Koha::Patrons->search({ surname => 'should_not_exist' })->last;
178     is( $last_patron, undef, '->last should return undef if search does not return any results' );
179 };
180
181 subtest 'get_column' => sub {
182     plan tests => 1;
183     my @cities = Koha::Cities->search;
184     my @city_names = map { $_->city_name } @cities;
185     is_deeply( [ Koha::Cities->search->get_column('city_name') ], \@city_names, 'Koha::Objects->get_column should be allowed' );
186 };
187
188 subtest 'Exceptions' => sub {
189     plan tests => 7;
190
191     my $patron_borrowernumber = $builder->build({ source => 'Borrower' })->{ borrowernumber };
192     my $patron = Koha::Patrons->find( $patron_borrowernumber );
193
194     # Koha::Object
195     try {
196         $patron->blah('blah');
197     } catch {
198         ok( $_->isa('Koha::Exceptions::Object::MethodNotCoveredByTests'),
199             'Calling a non-covered method should raise a Koha::Exceptions::Object::MethodNotCoveredByTests exception' );
200         is( $_->message, 'The method Koha::Patron->blah is not covered by tests!', 'The message raised should contain the package and the method' );
201     };
202
203     try {
204         $patron->set({ blah => 'blah' });
205     } catch {
206         ok( $_->isa('Koha::Exceptions::Object::PropertyNotFound'),
207             'Setting a non-existent property should raise a Koha::Exceptions::Object::PropertyNotFound exception' );
208     };
209
210     # Koha::Objects
211     try {
212         Koha::Patrons->search->not_covered_yet;
213     } catch {
214         ok( $_->isa('Koha::Exceptions::Object::MethodNotCoveredByTests'),
215             'Calling a non-covered method should raise a Koha::Exceptions::Object::MethodNotCoveredByTests exception' );
216         is( $_->message, 'The method Koha::Patrons->not_covered_yet is not covered by tests!', 'The message raised should contain the package and the method' );
217     };
218
219     try {
220         Koha::Patrons->not_covered_yet;
221     } catch {
222         ok( $_->isa('Koha::Exceptions::Object::MethodNotCoveredByTests'),
223             'Calling a non-covered method should raise a Koha::Exceptions::Object::MethodNotCoveredByTests exception' );
224         is( $_->message, 'The method Koha::Patrons->not_covered_yet is not covered by tests!', 'The message raised should contain the package and the method' );
225     };
226 };
227
228 $schema->storage->txn_rollback;
229
230 subtest '->is_paged and ->pager tests' => sub {
231
232     plan tests => 5;
233
234     $schema->storage->txn_begin;
235
236     # Delete existing patrons
237     Koha::Checkouts->delete;
238     Koha::Patrons->delete;
239     # Create 10 patrons
240     foreach (1..10) {
241         $builder->build_object({ class => 'Koha::Patrons' });
242     }
243
244     # Non-paginated search
245     my $patrons = Koha::Patrons->search();
246     is( $patrons->count, 10, 'Search returns all patrons' );
247     ok( !$patrons->is_paged, 'Search is not paged' );
248
249     # Paginated search
250     $patrons = Koha::Patrons->search( undef, { 'page' => 1, 'rows' => 3 } );
251     is( $patrons->count, 3, 'Search returns only one page, 3 patrons' );
252     ok( $patrons->is_paged, 'Search is paged' );
253     my $pager = $patrons->pager;
254     is( ref($patrons->pager), 'DBIx::Class::ResultSet::Pager',
255        'Koha::Objects->pager returns a valid DBIx::Class object' );
256
257     $schema->storage->txn_rollback;
258 };
259
260 subtest '->search() tests' => sub {
261
262     plan tests => 12;
263
264     $schema->storage->txn_begin;
265
266     my $count = Koha::Patrons->search->count;
267
268     # Create 10 patrons
269     foreach (1..10) {
270         $builder->build_object({ class => 'Koha::Patrons' });
271     }
272
273     my $patrons = Koha::Patrons->search();
274     is( ref($patrons), 'Koha::Patrons', 'search in scalar context returns the Koha::Object-based type' );
275     my @patrons = Koha::Patrons->search();
276     is( scalar @patrons, $count + 10, 'search in list context returns a list of objects' );
277     my $i = 0;
278     foreach (1..10) {
279         is( ref($patrons[$i]), 'Koha::Patron', 'Objects in the list have the singular type' );
280         $i++;
281     }
282
283     $schema->storage->txn_rollback;
284 };
285
286 subtest "to_api() tests" => sub {
287
288     plan tests => 18;
289
290     $schema->storage->txn_begin;
291
292     my $city_1 = $builder->build_object( { class => 'Koha::Cities' } );
293     my $city_2 = $builder->build_object( { class => 'Koha::Cities' } );
294
295     my $cities = Koha::Cities->search(
296         {
297             cityid => [ $city_1->cityid, $city_2->cityid ]
298         },
299         { -orderby => { -desc => 'cityid' } }
300     );
301
302     is( $cities->count, 2, 'Count is correct' );
303     my $cities_api = $cities->to_api;
304     is( ref( $cities_api ), 'ARRAY', 'to_api returns an array' );
305     is_deeply( $cities_api->[0], $city_1->to_api, 'to_api returns the individual objects with ->to_api' );
306     is_deeply( $cities_api->[1], $city_2->to_api, 'to_api returns the individual objects with ->to_api' );
307
308     my $biblio_1 = $builder->build_sample_biblio();
309     my $item_1   = $builder->build_sample_item({ biblionumber => $biblio_1->biblionumber });
310     my $hold_1   = $builder->build_object(
311         {
312             class => 'Koha::Holds',
313             value => { itemnumber => $item_1->itemnumber }
314         }
315     );
316
317     my $biblio_2 = $builder->build_sample_biblio();
318     my $item_2   = $builder->build_sample_item({ biblionumber => $biblio_2->biblionumber });
319     my $hold_2   = $builder->build_object(
320         {
321             class => 'Koha::Holds',
322             value => { itemnumber => $item_2->itemnumber }
323         }
324     );
325
326     my $embed = { 'items' => {} };
327
328     my $i = 0;
329     my @items = ( $item_1, $item_2 );
330     my @holds = ( $hold_1, $hold_2 );
331
332     my $biblios_api = Koha::Biblios->search(
333         {
334             biblionumber => [ $biblio_1->biblionumber, $biblio_2->biblionumber ]
335         }
336     )->to_api( { embed => $embed } );
337
338     foreach my $biblio_api ( @{ $biblios_api } ) {
339         ok(exists $biblio_api->{items}, 'Items where embedded in biblio results');
340         is($biblio_api->{items}->[0]->{item_id}, $items[$i]->itemnumber, 'Item matches');
341         ok(!exists $biblio_api->{items}->[0]->{holds}, 'No holds info should be embedded yet');
342
343         $i++;
344     }
345
346     # One more level
347     $embed = {
348         'items' => {
349             children => { 'holds' => {} }
350         }
351     };
352
353     $i = 0;
354
355     $biblios_api = Koha::Biblios->search(
356         {
357             biblionumber => [ $biblio_1->biblionumber, $biblio_2->biblionumber ]
358         }
359     )->to_api( { embed => $embed } );
360
361     foreach my $biblio_api ( @{ $biblios_api } ) {
362
363         ok(exists $biblio_api->{items}, 'Items where embedded in biblio results');
364         is($biblio_api->{items}->[0]->{item_id}, $items[$i]->itemnumber, 'Item still matches');
365         ok(exists $biblio_api->{items}->[0]->{holds}, 'Holds info should be embedded');
366         is($biblio_api->{items}->[0]->{holds}->[0]->{hold_id}, $holds[$i]->reserve_id, 'Hold matches');
367
368         $i++;
369     }
370
371     $schema->storage->txn_rollback;
372 };
373
374 subtest "TO_JSON() tests" => sub {
375
376     plan tests => 4;
377
378     $schema->storage->txn_begin;
379
380     my $city_1 = $builder->build_object( { class => 'Koha::Cities' } );
381     my $city_2 = $builder->build_object( { class => 'Koha::Cities' } );
382
383     my $cities = Koha::Cities->search(
384         {
385             cityid => [ $city_1->cityid, $city_2->cityid ]
386         },
387         { -orderby => { -desc => 'cityid' } }
388     );
389
390     is( $cities->count, 2, 'Count is correct' );
391     my $cities_json = $cities->TO_JSON;
392     is( ref($cities_json), 'ARRAY', 'to_api returns an array' );
393     is_deeply( $cities_json->[0], $city_1->TO_JSON, 'TO_JSON returns the individual objects with ->TO_JSON' );
394     is_deeply( $cities_json->[1], $city_2->TO_JSON,'TO_JSON returns the individual objects with ->TO_JSON' );
395
396     $schema->storage->txn_rollback;
397 };
398
399 # Koha::Object[s] must behave the same as DBIx::Class
400 subtest 'Return same values as DBIx::Class' => sub {
401     plan tests => 1;
402
403     subtest 'Delete' => sub {
404         plan tests => 2;
405
406         $schema->storage->txn_begin;
407
408         subtest 'Simple Koha::Objects - Koha::Cities' => sub {
409             plan tests => 2;
410
411             subtest 'Koha::Object->delete' => sub {
412
413                 plan tests => 5;
414
415                 my ( $r_us, $e_us, $r_them, $e_them );
416
417                 # CASE 1 - Delete an existing object
418                 my $c = Koha::City->new( { city_name => 'city4test' } )->store;
419                 try { $r_us = $c->delete; } catch { $e_us = $_ };
420                 $c = $schema->resultset('City')->new( { city_name => 'city4test_2' } )->update_or_insert;
421                 try { $r_them = $c->delete; } catch { $e_them = $_ };
422                 ok( ref($r_us) && ref($r_them),
423                     'Successful delete should return the object ' );
424                 ok( !defined $e_us && !defined $e_them,
425                     'Successful delete should not raise an exception' );
426                 is( ref($r_us), 'Koha::City', 'Successful delete should return our Koha::Obect based object' );
427
428                 # CASE 2 - Delete an object that is not in storage
429                 try { $r_us   = $r_us->delete;   } catch { $e_us   = $_ };
430                 try { $r_them = $r_them->delete; } catch { $e_them = $_ };
431                 ok(
432                     defined $e_us && defined $e_them,
433                     'Delete an object that is not in storage should raise an exception'
434                 );
435                 is( ref($e_us), 'DBIx::Class::Exception' )
436                   ; # FIXME This needs adjustement, we want to throw a Koha::Exception
437
438             };
439
440             subtest 'Koha::Objects->delete' => sub {
441
442                 plan tests => 4;
443
444                 my ( $r_us, $e_us, $r_them, $e_them );
445
446                 # CASE 1 - Delete existing objects
447                 my $city_1 = $builder->build_object({ class => 'Koha::Cities' });
448                 my $city_2 = $builder->build_object({ class => 'Koha::Cities' });
449                 my $city_3 = $builder->build_object({ class => 'Koha::Cities' });
450                 my $cities = Koha::Cities->search(
451                     {
452                         cityid => {
453                             -in => [
454                                 $city_1->cityid,
455                                 $city_2->cityid,
456                                 $city_3->cityid,
457                             ]
458                         }
459                     }
460                 );
461
462                 try { $r_us = $cities->delete; } catch { $e_us = $_ };
463
464                 $city_1 = $builder->build_object({ class => 'Koha::Cities' });
465                 $city_2 = $builder->build_object({ class => 'Koha::Cities' });
466                 $city_3 = $builder->build_object({ class => 'Koha::Cities' });
467                 $cities = $schema->resultset('City')->search(
468                     {
469                         cityid => {
470                             -in => [
471                                 $city_1->cityid,
472                                 $city_2->cityid,
473                                 $city_3->cityid,
474                             ]
475                         }
476                     }
477                 );
478
479                 try { $r_them = $cities->delete; } catch { $e_them = $_ };
480
481                 ok( $r_us == 3 && $r_them == 3 );
482                 ok (!defined($e_us) && !defined($e_them));
483
484                 # CASE 2 - One of the object is not in storage
485                 $city_1 = $builder->build_object({ class => 'Koha::Cities' });
486                 $city_2 = $builder->build_object({ class => 'Koha::Cities' });
487                 $city_3 = $builder->build_object({ class => 'Koha::Cities' });
488                 $cities = Koha::Cities->search(
489                     {
490                         cityid => {
491                             -in => [
492                                 $city_1->cityid,
493                                 $city_2->cityid,
494                                 $city_3->cityid,
495                             ]
496                         }
497                     }
498                 );
499
500                 $city_2->delete; # We delete one of the object
501                 try { $r_us = $cities->delete; } catch { $e_us = $_ };
502
503                 $city_1 = $builder->build_object({ class => 'Koha::Cities' });
504                 $city_2 = $builder->build_object({ class => 'Koha::Cities' });
505                 $city_3 = $builder->build_object({ class => 'Koha::Cities' });
506                 $cities = $schema->resultset('City')->search(
507                     {
508                         cityid => {
509                             -in => [
510                                 $city_1->cityid,
511                                 $city_2->cityid,
512                                 $city_3->cityid,
513                             ]
514                         }
515                     }
516                 );
517
518                 $city_2->delete; # We delete one of the object
519                 try { $r_them = $cities->delete; } catch { $e_them = $_ };
520
521                 ok( $r_us == 2 && $r_them == 2 );
522                 ok (!defined($e_us) && !defined($e_them));
523             };
524         };
525
526         subtest 'Overwritten Koha::Objects->delete - Koha::Patrons' => sub {
527
528             plan tests => 2;
529
530             subtest 'Koha::Object->delete' => sub {
531
532                 plan tests => 7;
533
534                 my ( $r_us, $e_us, $r_them, $e_them );
535
536                 # CASE 1 - Delete an existing patron
537                 my $patron = $builder->build_object({ class => 'Koha::Patrons' });
538                 my $patron_data = $patron->unblessed;
539                 $patron->delete;
540
541                 $patron = Koha::Patron->new( $patron_data )->store;
542                 try {$r_us = $patron->delete;} catch { $e_us = $_ };
543                 $patron = $schema->resultset('Borrower')->new( $patron_data )->update_or_insert;
544                 try {$r_them = $patron->delete;} catch { $e_them = $_ };
545                 ok( ref($r_us) && ref($r_them),
546                     'Successful delete should return the patron object' );
547                 ok( !defined $e_us && !defined $e_them,
548                     'Successful delete should not raise an exception' );
549                 is( ref($r_us), 'Koha::Patron',
550                     'Successful delete should return our Koha::Obect based object' );
551
552                 # CASE 2 - Delete a patron that is not in storage
553                 try { $r_us   = $r_us->delete;   } catch { $e_us   = $_ };
554                 try { $r_them = $r_them->delete; } catch { $e_them = $_ };
555                 ok(
556                     defined $e_us && defined $e_them,
557                     'Delete a patron that is not in storage should raise an exception'
558                 );
559                 is( ref($e_us), 'DBIx::Class::Exception' )
560                   ; # FIXME This needs adjustement, we want to throw a Koha::Exception
561
562                 # CASE 3 - Delete a patron that cannot be deleted (as a checkout)
563                 $patron = Koha::Patron->new($patron_data)->store;
564                 $builder->build_object(
565                     {
566                         class => 'Koha::Checkouts',
567                         value => { borrowernumber => $patron->borrowernumber }
568                     }
569                 );
570                 try { $r_us = $r_us->delete; } catch { $e_us = $_ };
571                 $patron = $schema->resultset('Borrower')->find( $patron->borrowernumber );
572                 try { $r_them = $r_them->delete; } catch { $e_them = $_ };
573                 ok(
574                     defined $e_us && defined $e_them,
575                     'Delete a patron that cannot be deleted should raise an exception'
576                 );
577                 is( ref($e_us), 'DBIx::Class::Exception' )
578                   ; # FIXME This needs adjustement, we want to throw a Koha::Exception
579             };
580
581             subtest 'Koha::Objects->delete' => sub {
582
583                 plan tests => 7;
584
585                 my ( $r_us, $e_us, $r_them, $e_them );
586
587                 # CASE 1 - Delete existing objects
588                 my $patron_1 = $builder->build_object({ class => 'Koha::Patrons' });
589                 my $patron_2 = $builder->build_object({ class => 'Koha::Patrons' });
590                 my $patron_3 = $builder->build_object({ class => 'Koha::Patrons' });
591                 my $patrons = Koha::Patrons->search(
592                     {
593                         borrowernumber => {
594                             -in => [
595                                 $patron_1->borrowernumber,
596                                 $patron_2->borrowernumber,
597                                 $patron_3->borrowernumber
598                             ]
599                         }
600                     }
601                 );
602
603                 try { $r_us = $patrons->delete; } catch { $e_us = $_ };
604
605                 $patron_1 = $builder->build_object({ class => 'Koha::Patrons' });
606                 $patron_2 = $builder->build_object({ class => 'Koha::Patrons' });
607                 $patron_3 = $builder->build_object({ class => 'Koha::Patrons' });
608                 $patrons = $schema->resultset('Borrower')->search(
609                     {
610                         borrowernumber => {
611                             -in => [
612                                 $patron_1->borrowernumber,
613                                 $patron_2->borrowernumber,
614                                 $patron_3->borrowernumber
615                             ]
616                         }
617                     }
618                 );
619
620                 try { $r_them = $patrons->delete; } catch { $e_them = $_ };
621
622                 ok( $r_us == 3 && $r_them == 3, '->delete should return the number of deleted patrons' );
623                 ok (!defined($e_us) && !defined($e_them), '->delete should not raise exception if everything went well');
624
625                 # CASE 2 - One of the patrons is not in storage
626                 undef $_ for $r_us, $e_us, $r_them, $e_them;
627                 $patron_1 = $builder->build_object({ class => 'Koha::Patrons' });
628                 $patron_2 = $builder->build_object({ class => 'Koha::Patrons' });
629                 $patron_3 = $builder->build_object({ class => 'Koha::Patrons' });
630                 $patrons = Koha::Patrons->search(
631                     {
632                         borrowernumber => {
633                             -in => [
634                                 $patron_1->borrowernumber,
635                                 $patron_2->borrowernumber,
636                                 $patron_3->borrowernumber
637                             ]
638                         }
639                     }
640                 );
641
642                 $patron_2->delete; # We delete one of the patron
643                 try { $r_us = $patrons->delete; } catch { $e_us = $_ };
644
645                 $patron_1 = $builder->build_object({ class => 'Koha::Patrons' });
646                 $patron_2 = $builder->build_object({ class => 'Koha::Patrons' });
647                 $patron_3 = $builder->build_object({ class => 'Koha::Patrons' });
648                 $patrons = $schema->resultset('Borrower')->search(
649                     {
650                         borrowernumber => {
651                             -in => [
652                                 $patron_1->borrowernumber,
653                                 $patron_2->borrowernumber,
654                                 $patron_3->borrowernumber
655                             ]
656                         }
657                     }
658                 );
659
660                 $patron_2->delete; # We delete one of the patron
661                 try { $r_them = $patrons->delete; } catch { $e_them = $_ };
662
663                 ok( $r_us == 2 && $r_them == 2, 'Delete patrons with one that was not in storage should delete the patrons' );
664                 ok (!defined($e_us) && !defined($e_them), 'no exception should be raised if at least one patron was not in storage');
665
666                 # CASE 3 - Delete a set of patrons with one that that cannot be deleted (as a checkout)
667                 undef $_ for $r_us, $e_us, $r_them, $e_them;
668                 $patron_1 = $builder->build_object({ class => 'Koha::Patrons' });
669                 $patron_2 = $builder->build_object({ class => 'Koha::Patrons' });
670                 $patron_3 = $builder->build_object({ class => 'Koha::Patrons' });
671                 $patrons = Koha::Patrons->search(
672                     {
673                         borrowernumber => {
674                             -in => [
675                                 $patron_1->borrowernumber,
676                                 $patron_2->borrowernumber,
677                                 $patron_3->borrowernumber
678                             ]
679                         }
680                     }
681                 );
682
683                 # Adding a checkout to patron_2
684                 $builder->build_object(
685                     {
686                         class => 'Koha::Checkouts',
687                         value => { borrowernumber => $patron_2->borrowernumber }
688                     }
689                 );
690
691                 try { $r_us = $patrons->delete; } catch { $e_us = $_ };
692                 my $not_deleted_us = $patron_1->in_storage + $patron_2->in_storage + $patron_3->in_storage;
693
694                 $patron_1 = $builder->build_object({ class => 'Koha::Patrons' });
695                 $patron_2 = $builder->build_object({ class => 'Koha::Patrons' });
696                 $patron_3 = $builder->build_object({ class => 'Koha::Patrons' });
697                 $patrons = $schema->resultset('Borrower')->search(
698                     {
699                         borrowernumber => {
700                             -in => [
701                                 $patron_1->borrowernumber,
702                                 $patron_2->borrowernumber,
703                                 $patron_3->borrowernumber
704                             ]
705                         }
706                     }
707                 );
708
709                 # Adding a checkout to patron_2
710                 $builder->build_object(
711                     {
712                         class => 'Koha::Checkouts',
713                         value => { borrowernumber => $patron_2->borrowernumber }
714                     }
715                 );
716
717                 try { $r_them = $patrons->delete; } catch { $e_them = $_ };
718
719                 my $not_deleted_them = $patron_1->in_storage + $patron_2->in_storage + $patron_3->in_storage;
720                 ok(
721                     defined $e_us && defined $e_them,
722                     'Delete patrons with one that cannot be deleted should raise an exception'
723                 );
724                 is( ref($e_us), 'DBIx::Class::Exception' )
725                   ; # FIXME This needs adjustement, we want to throw a Koha::Exception
726
727                 ok($not_deleted_us == 3 && $not_deleted_them == 3, 'If one patron cannot be deleted, none should have been deleted');
728             };
729         };
730
731         $schema->storage->txn_rollback;
732
733     };
734 };
735
736 subtest "attributes_from_api() tests" => sub {
737
738     plan tests => 1;
739
740     $schema->storage->txn_begin;
741
742     my $cities_rs = Koha::Cities->new;
743     my $city      = Koha::City->new;
744
745     my $api_attributes = {
746         name        => 'Cordoba',
747         postal_code => 5000
748     };
749
750     is_deeply(
751         $cities_rs->attributes_from_api($api_attributes),
752         $city->attributes_from_api($api_attributes)
753     );
754
755     $schema->storage->txn_rollback;
756 };
757
758 subtest "from_api_mapping() tests" => sub {
759
760     plan tests => 1;
761
762     $schema->storage->txn_begin;
763
764     my $cities_rs = Koha::Cities->new;
765     my $city      = Koha::City->new;
766
767     is_deeply(
768         $cities_rs->from_api_mapping,
769         $city->from_api_mapping
770     );
771
772     $schema->storage->txn_rollback;
773 };
774
775 subtest 'prefetch_whitelist() tests' => sub {
776
777     plan tests => 3;
778
779     $schema->storage->txn_begin;
780
781     my $biblios = Koha::Biblios->new;
782
783     my $prefetch_whitelist = $biblios->prefetch_whitelist;
784
785     ok(
786         exists $prefetch_whitelist->{orders},
787         'Relationship matching method name is listed'
788     );
789     is(
790         $prefetch_whitelist->{orders},
791         'Koha::Acquisition::Order',
792         'Guessed the non-standard object class correctly'
793     );
794
795     is(
796         $prefetch_whitelist->{items},
797         'Koha::Item',
798         'Guessed the standard object class correctly'
799     );
800
801     $schema->storage->txn_rollback;
802 };
803
804 subtest 'empty() tests' => sub {
805
806     plan tests => 5;
807
808     $schema->storage->txn_begin;
809
810     # Add a patron, we need more than 0
811     $builder->build_object({ class => 'Koha::Patrons' });
812     ok( Koha::Patrons->count > 0, 'There is more than one Koha::Patron on the resultset' );
813
814     my $empty = Koha::Patrons->new->empty;
815     is( ref($empty), 'Koha::Patrons', '->empty returns a Koha::Patrons iterator' );
816     is( $empty->count, 0, 'The empty resultset is, well, empty :-D' );
817
818     $empty = Koha::Patrons->empty;
819     is( ref($empty), 'Koha::Patrons', 'without being instantiated, ->empty still returns a Koha::Patrons iterator' );
820     is( $empty->count, 0, 'The empty resultset is, well, empty :-D' );
821
822     $schema->storage->txn_rollback;
823 };
824
825 subtest 'delete() tests' => sub {
826
827     plan tests => 2;
828
829     $schema->storage->txn_begin;
830
831     # Make sure no cities
832     warnings_are { Koha::Cities->delete }[],
833       "No warnings, no Koha::City->delete called as it doesn't exist";
834
835     # Mock Koha::City
836     my $mocked_city = Test::MockModule->new('Koha::City');
837     $mocked_city->mock(
838         'delete',
839         sub {
840             shift->_result->delete;
841             warn "delete called!";
842         }
843     );
844
845     # Add two cities
846     $builder->build_object( { class => 'Koha::Cities' } );
847     $builder->build_object( { class => 'Koha::Cities' } );
848
849     my $cities = Koha::Cities->search;
850     $cities->next;
851     warnings_are { $cities->delete }
852         [ "delete called!", "delete called!" ],
853         "No warnings, no Koha::City->delete called as it doesn't exist";
854
855     $schema->storage->txn_rollback;
856 };