Bug 29869: Unit tests
[koha.git] / t / db_dependent / Koha / Hold.t
1 #!/usr/bin/perl
2
3 # Copyright 2020 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 => 4;
23
24 use Test::Exception;
25 use Test::MockModule;
26
27 use t::lib::TestBuilder;
28
29 use Koha::ActionLogs;
30 use Koha::Holds;
31 use Koha::Libraries;
32
33 my $schema  = Koha::Database->new->schema;
34 my $builder = t::lib::TestBuilder->new;
35
36 subtest 'fill() tests' => sub {
37
38     plan tests => 11;
39
40     $schema->storage->txn_begin;
41
42     my $fee = 15;
43
44     my $category = $builder->build_object(
45         {
46             class => 'Koha::Patron::Categories',
47             value => { reservefee => $fee }
48         }
49     );
50     my $patron = $builder->build_object(
51         {
52             class => 'Koha::Patrons',
53             value => { categorycode => $category->id }
54         }
55     );
56     my $manager = $builder->build_object( { class => 'Koha::Patrons' } );
57
58     my $title  = 'Do what you want';
59     my $biblio = $builder->build_sample_biblio( { title => $title } );
60     my $item   = $builder->build_sample_item( { biblionumber => $biblio->id } );
61     my $hold   = $builder->build_object(
62         {
63             class => 'Koha::Holds',
64             value => {
65                 biblionumber   => $biblio->id,
66                 borrowernumber => $patron->id,
67                 itemnumber     => $item->id,
68                 priority       => 10,
69             }
70         }
71     );
72
73     t::lib::Mocks::mock_preference( 'HoldFeeMode', 'any_time_is_collected' );
74     t::lib::Mocks::mock_preference( 'HoldsLog',    1 );
75     t::lib::Mocks::mock_userenv(
76         { patron => $manager, branchcode => $manager->branchcode } );
77
78     my $interface = 'api';
79     C4::Context->interface($interface);
80
81     my $ret = $hold->fill;
82
83     is( ref($ret), 'Koha::Hold', '->fill returns the object type' );
84     is( $ret->id, $hold->id, '->fill returns the object' );
85
86     is( Koha::Holds->find($hold->id), undef, 'Hold no longer current' );
87     my $old_hold = Koha::Old::Holds->find( $hold->id );
88
89     is( $old_hold->id, $hold->id, 'reserve_id retained' );
90     is( $old_hold->priority, 0, 'priority set to 0' );
91     is( $old_hold->found, 'F', 'found set to F' );
92
93     subtest 'fee applied tests' => sub {
94
95         plan tests => 9;
96
97         my $account = $patron->account;
98         is( $account->balance, $fee, 'Charge applied correctly' );
99
100         my $debits = $account->outstanding_debits;
101         is( $debits->count, 1, 'Only one fee charged' );
102
103         my $fee_debit = $debits->next;
104         is( $fee_debit->amount * 1, $fee, 'Fee amount stored correctly' );
105         is( $fee_debit->description, $title,
106             'Fee description stored correctly' );
107         is( $fee_debit->manager_id, $manager->id,
108             'Fee manager_id stored correctly' );
109         is( $fee_debit->branchcode, $manager->branchcode,
110             'Fee branchcode stored correctly' );
111         is( $fee_debit->interface, $interface,
112             'Fee interface stored correctly' );
113         is( $fee_debit->debit_type_code,
114             'RESERVE', 'Fee debit_type_code stored correctly' );
115         is( $fee_debit->itemnumber, $item->id,
116             'Fee itemnumber stored correctly' );
117     };
118
119     my $logs = Koha::ActionLogs->search(
120         {
121             action => 'FILL',
122             module => 'HOLDS',
123             object => $hold->id
124         }
125     );
126
127     is( $logs->count, 1, '1 log line added' );
128
129     # Set HoldFeeMode to something other than any_time_is_collected
130     t::lib::Mocks::mock_preference( 'HoldFeeMode', 'not_always' );
131     # Disable logging
132     t::lib::Mocks::mock_preference( 'HoldsLog',    0 );
133
134     $hold = $builder->build_object(
135         {
136             class => 'Koha::Holds',
137             value => {
138                 biblionumber   => $biblio->id,
139                 borrowernumber => $patron->id,
140                 itemnumber     => $item->id,
141                 priority       => 10,
142             }
143         }
144     );
145
146     $hold->fill;
147
148     my $account = $patron->account;
149     is( $account->balance, $fee, 'No new charge applied' );
150
151     my $debits = $account->outstanding_debits;
152     is( $debits->count, 1, 'Only one fee charged, because of HoldFeeMode' );
153
154     $logs = Koha::ActionLogs->search(
155         {
156             action => 'FILL',
157             module => 'HOLDS',
158             object => $hold->id
159         }
160     );
161
162     is( $logs->count, 0, 'HoldsLog disabled, no logs added' );
163
164     $schema->storage->txn_rollback;
165 };
166
167 subtest 'patron() tests' => sub {
168
169     plan tests => 2;
170
171     $schema->storage->txn_begin;
172
173     my $patron = $builder->build_object({ class => 'Koha::Patrons' });
174     my $hold   = $builder->build_object(
175         {
176             class => 'Koha::Holds',
177             value => {
178                 borrowernumber => $patron->borrowernumber
179             }
180         }
181     );
182
183     my $hold_patron = $hold->patron;
184     is( ref($hold_patron), 'Koha::Patron', 'Right type' );
185     is( $hold_patron->id, $patron->id, 'Right object' );
186
187     $schema->storage->txn_rollback;
188 };
189
190 subtest 'set_pickup_location() tests' => sub {
191
192     plan tests => 11;
193
194     $schema->storage->txn_begin;
195
196     my $mock_biblio = Test::MockModule->new('Koha::Biblio');
197     my $mock_item   = Test::MockModule->new('Koha::Item');
198
199     my $library_1 = $builder->build_object({ class => 'Koha::Libraries' });
200     my $library_2 = $builder->build_object({ class => 'Koha::Libraries' });
201     my $library_3 = $builder->build_object({ class => 'Koha::Libraries' });
202
203     # let's control what Koha::Biblio->pickup_locations returns, for testing
204     $mock_biblio->mock( 'pickup_locations', sub {
205         return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
206     });
207     # let's mock what Koha::Item->pickup_locations returns, for testing
208     $mock_item->mock( 'pickup_locations', sub {
209         return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
210     });
211
212     my $biblio = $builder->build_sample_biblio;
213     my $item   = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
214
215     # Test biblio-level holds
216     my $biblio_hold = $builder->build_object(
217         {
218             class => "Koha::Holds",
219             value => {
220                 biblionumber => $biblio->biblionumber,
221                 branchcode   => $library_3->branchcode,
222                 itemnumber   => undef,
223             }
224         }
225     );
226
227     throws_ok
228         { $biblio_hold->set_pickup_location({ library_id => $library_1->branchcode }); }
229         'Koha::Exceptions::Hold::InvalidPickupLocation',
230         'Exception thrown on invalid pickup location';
231
232     $biblio_hold->discard_changes;
233     is( $biblio_hold->branchcode, $library_3->branchcode, 'branchcode remains untouched' );
234
235     my $ret = $biblio_hold->set_pickup_location({ library_id => $library_2->id });
236     is( ref($ret), 'Koha::Hold', 'self is returned' );
237
238     $biblio_hold->discard_changes;
239     is( $biblio_hold->branchcode, $library_2->id, 'Pickup location changed correctly' );
240
241     # Test item-level holds
242     my $item_hold = $builder->build_object(
243         {
244             class => "Koha::Holds",
245             value => {
246                 biblionumber => $biblio->biblionumber,
247                 branchcode   => $library_3->branchcode,
248                 itemnumber   => $item->itemnumber,
249             }
250         }
251     );
252
253     throws_ok
254         { $item_hold->set_pickup_location({ library_id => $library_1->branchcode }); }
255         'Koha::Exceptions::Hold::InvalidPickupLocation',
256         'Exception thrown on invalid pickup location';
257
258     $item_hold->discard_changes;
259     is( $item_hold->branchcode, $library_3->branchcode, 'branchcode remains untouched' );
260
261     $item_hold->set_pickup_location({ library_id => $library_1->branchcode, force => 1 });
262     $item_hold->discard_changes;
263     is( $item_hold->branchcode, $library_1->branchcode, 'branchcode changed because of \'force\'' );
264
265     $ret = $item_hold->set_pickup_location({ library_id => $library_2->id });
266     is( ref($ret), 'Koha::Hold', 'self is returned' );
267
268     $item_hold->discard_changes;
269     is( $item_hold->branchcode, $library_2->id, 'Pickup location changed correctly' );
270
271     throws_ok
272         { $item_hold->set_pickup_location({ library_id => undef }); }
273         'Koha::Exceptions::MissingParameter',
274         'Exception thrown if missing parameter';
275
276     is( "$@", 'The library_id parameter is mandatory', 'Exception message is clear' );
277
278     $schema->storage->txn_rollback;
279 };
280
281 subtest 'is_pickup_location_valid() tests' => sub {
282
283     plan tests => 5;
284
285     $schema->storage->txn_begin;
286
287     my $mock_biblio = Test::MockModule->new('Koha::Biblio');
288     my $mock_item   = Test::MockModule->new('Koha::Item');
289
290     my $library_1 = $builder->build_object({ class => 'Koha::Libraries' });
291     my $library_2 = $builder->build_object({ class => 'Koha::Libraries' });
292     my $library_3 = $builder->build_object({ class => 'Koha::Libraries' });
293
294     # let's control what Koha::Biblio->pickup_locations returns, for testing
295     $mock_biblio->mock( 'pickup_locations', sub {
296         return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
297     });
298     # let's mock what Koha::Item->pickup_locations returns, for testing
299     $mock_item->mock( 'pickup_locations', sub {
300         return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
301     });
302
303     my $biblio = $builder->build_sample_biblio;
304     my $item   = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
305
306     # Test biblio-level holds
307     my $biblio_hold = $builder->build_object(
308         {
309             class => "Koha::Holds",
310             value => {
311                 biblionumber => $biblio->biblionumber,
312                 branchcode   => $library_3->branchcode,
313                 itemnumber   => undef,
314             }
315         }
316     );
317
318     ok( !$biblio_hold->is_pickup_location_valid({ library_id => $library_1->branchcode }), 'Pickup location invalid');
319     ok( $biblio_hold->is_pickup_location_valid({ library_id => $library_2->id }), 'Pickup location valid');
320
321     # Test item-level holds
322     my $item_hold = $builder->build_object(
323         {
324             class => "Koha::Holds",
325             value => {
326                 biblionumber => $biblio->biblionumber,
327                 branchcode   => $library_3->branchcode,
328                 itemnumber   => $item->itemnumber,
329             }
330         }
331     );
332
333     ok( !$item_hold->is_pickup_location_valid({ library_id => $library_1->branchcode }), 'Pickup location invalid');
334     ok( $item_hold->is_pickup_location_valid({ library_id => $library_2->id }), 'Pickup location valid' );
335
336     subtest 'pickup_locations() returning ->empty' => sub {
337
338         plan tests => 2;
339
340         $schema->storage->txn_begin;
341
342         my $library = $builder->build_object({ class => 'Koha::Libraries' });
343
344         my $mock_item = Test::MockModule->new('Koha::Item');
345         $mock_item->mock( 'pickup_locations', sub { return Koha::Libraries->new->empty; } );
346
347         my $mock_biblio = Test::MockModule->new('Koha::Biblio');
348         $mock_biblio->mock( 'pickup_locations', sub { return Koha::Libraries->new->empty; } );
349
350         my $item   = $builder->build_sample_item();
351         my $biblio = $item->biblio;
352
353         # Test biblio-level holds
354         my $biblio_hold = $builder->build_object(
355             {
356                 class => "Koha::Holds",
357                 value => {
358                     biblionumber => $biblio->biblionumber,
359                     itemnumber   => undef,
360                 }
361             }
362         );
363
364         ok( !$biblio_hold->is_pickup_location_valid({ library_id => $library->branchcode }), 'Pickup location invalid');
365
366         # Test item-level holds
367         my $item_hold = $builder->build_object(
368             {
369                 class => "Koha::Holds",
370                 value => {
371                     biblionumber => $biblio->biblionumber,
372                     itemnumber   => $item->itemnumber,
373                 }
374             }
375         );
376
377         ok( !$item_hold->is_pickup_location_valid({ library_id => $library->branchcode }), 'Pickup location invalid');
378
379         $schema->storage->txn_rollback;
380     };
381
382     $schema->storage->txn_rollback;
383 };