Bug 35322: Add unit tests
[koha.git] / t / db_dependent / Calendar.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use Test::More tests => 6;
21 use Time::Fake;
22
23 use t::lib::Mocks;
24 use t::lib::TestBuilder;
25
26 use DateTime;
27 use DateTime::Duration;
28 use Koha::Caches;
29 use Koha::Calendar;
30 use Koha::Database;
31 use Koha::DateUtils qw( dt_from_string );
32
33 my $schema  = Koha::Database->new->schema;
34 my $builder = t::lib::TestBuilder->new;
35 $schema->storage->txn_begin;
36
37 my $today      = dt_from_string();
38 my $holiday_dt = $today->clone;
39 $holiday_dt->add( days => 3 );
40
41 Koha::Caches->get_instance()->flush_all();
42
43 subtest 'Original tests from t' => sub {
44
45     # We need to mock the C4::Context->preference method for
46     # simplicity and re-usability of the session definition. Any
47     # syspref fits for syspref-agnostic tests.
48     my $module_context = Test::MockModule->new('C4::Context');
49     $module_context->mock(
50         'preference',
51         sub {
52             return 'Calendar';
53         }
54     );
55
56     my $mpl = $builder->build_object( { class => 'Koha::Libraries' } )->branchcode;
57     my $cpl = $builder->build_object( { class => 'Koha::Libraries' } )->branchcode;
58     my $rows = [    # add weekly holidays
59         { branchcode => $mpl, weekday => 0 },                  # sundays
60         { branchcode => $mpl, weekday => 6 },                  # saturdays
61         { branchcode => $mpl, day     => 1, month => 1 },      # new year's day
62         { branchcode => $mpl, day     => 25, month => 12 },    # chrismas
63     ];
64     $schema->resultset('RepeatableHoliday')->delete_all;
65     $schema->resultset('RepeatableHoliday')->create( { %$_, description => q{} } ) for @$rows;
66
67     $rows = [                                                  # exception holidays
68         { branchcode => $mpl, day => 11, month => 11, year => 2012, isexception => 1 },    # sunday exception
69         { branchcode => $mpl, day => 1,  month => 6,  year => 2011, isexception => 0 },
70         { branchcode => $mpl, day => 4,  month => 7,  year => 2012, isexception => 0 },
71         { branchcode => $cpl, day => 6,  month => 8,  year => 2012, isexception => 0 },
72         { branchcode => $mpl, day => 7,  month => 7,  year => 2012, isexception => 1 },    # holiday exception
73         { branchcode => $mpl, day => 7,  month => 7,  year => 2012, isexception => 0 },    # holiday
74     ];
75     $schema->resultset('SpecialHoliday')->delete_all;
76     $schema->resultset('SpecialHoliday')->create( { %$_, description => q{} } ) for @$rows;
77
78     my $cache = Koha::Caches->get_instance();
79     $cache->clear_from_cache( $mpl . '_holidays' );
80     $cache->clear_from_cache( $cpl . '_holidays' );
81
82     # $mpl branch is arbitrary, is not used at all but is needed for initialization
83     my $cal = Koha::Calendar->new( branchcode => $mpl );
84
85     isa_ok( $cal, 'Koha::Calendar', 'Calendar class returned' );
86
87     my $saturday = DateTime->new(
88         year  => 2012,
89         month => 11,
90         day   => 24,
91     );
92
93     my $sunday = DateTime->new(
94         year  => 2012,
95         month => 11,
96         day   => 25,
97     );
98
99     my $monday = DateTime->new(
100         year  => 2012,
101         month => 11,
102         day   => 26,
103     );
104
105     my $new_year = DateTime->new(
106         year  => 2013,
107         month => 1,
108         day   => 1,
109     );
110
111     my $single_holiday = DateTime->new(
112         year  => 2011,
113         month => 6,
114         day   => 1,
115     );    # should be a holiday
116
117     my $notspecial = DateTime->new(
118         year  => 2011,
119         month => 6,
120         day   => 2
121     );    # should NOT be a holiday
122
123     my $sunday_exception = DateTime->new(
124         year  => 2012,
125         month => 11,
126         day   => 11
127     );
128
129     my $day_after_christmas = DateTime->new(
130         year  => 2012,
131         month => 12,
132         day   => 26
133     );    # for testing negative addDuration
134
135     my $holiday_for_another_branch = DateTime->new(
136         year  => 2012,
137         month => 8,
138         day   => 6,      # This is a monday
139     );
140
141     my $holiday_excepted = DateTime->new(
142         year  => 2012,
143         month => 7,
144         day   => 7,      # Both a holiday and exception
145     );
146
147     {                    # Syspref-agnostic tests
148         is( $saturday->day_of_week,              6, '\'$saturday\' is actually a saturday (6th day of week)' );
149         is( $sunday->day_of_week,                7, '\'$sunday\' is actually a sunday (7th day of week)' );
150         is( $monday->day_of_week,                1, '\'$monday\' is actually a monday (1st day of week)' );
151         is( $cal->is_holiday($saturday),         1, 'Saturday is a closed day' );
152         is( $cal->is_holiday($sunday),           1, 'Sunday is a closed day' );
153         is( $cal->is_holiday($monday),           0, 'Monday is not a closed day' );
154         is( $cal->is_holiday($new_year),         1, 'Month/Day closed day test (New year\'s day)' );
155         is( $cal->is_holiday($single_holiday),   1, 'Single holiday closed day test' );
156         is( $cal->is_holiday($notspecial),       0, 'Fixed single date that is not a holiday test' );
157         is( $cal->is_holiday($sunday_exception), 0, 'Exception holiday is not a closed day test' );
158         is(
159             $cal->is_holiday($holiday_for_another_branch), 0,
160             'Holiday defined for another branch should not be defined as an holiday'
161         );
162         is( $cal->is_holiday($holiday_excepted), 0, 'Holiday defined and excepted should not be a holiday' );
163     }
164
165     {    # Bugzilla #8966 - is_holiday truncates referenced date
166         my $later_dt = DateTime->new(    # Monday
167             year      => 2012,
168             month     => 9,
169             day       => 17,
170             hour      => 17,
171             minute    => 30,
172             time_zone => 'Europe/London',
173         );
174
175         is( $cal->is_holiday($later_dt), 0, 'bz-8966 (1/2) Apply is_holiday for the next test' );
176         cmp_ok( $later_dt, 'eq', '2012-09-17T17:30:00', 'bz-8966 (2/2) Date should be the same after is_holiday' );
177     }
178
179     {    # Bugzilla #8800 - is_holiday should use truncated date for 'contains' call
180         my $single_holiday_time = DateTime->new(
181             year   => 2011,
182             month  => 6,
183             day    => 1,
184             hour   => 11,
185             minute => 2
186         );
187
188         is(
189             $cal->is_holiday($single_holiday_time),
190             $cal->is_holiday($single_holiday),
191             'bz-8800 is_holiday should truncate the date for holiday validation'
192         );
193     }
194
195     my $one_day_dur   = DateTime::Duration->new( days => 1 );
196     my $two_day_dur   = DateTime::Duration->new( days => 2 );
197     my $seven_day_dur = DateTime::Duration->new( days => 7 );
198
199     my $dt = dt_from_string( '2012-07-03', 'iso' );    #tuesday
200
201     my $test_dt = DateTime->new(                       # Monday
202         year   => 2012,
203         month  => 7,
204         day    => 23,
205         hour   => 11,
206         minute => 53,
207     );
208
209     my $later_dt = DateTime->new(                      # Monday
210         year      => 2012,
211         month     => 9,
212         day       => 17,
213         hour      => 17,
214         minute    => 30,
215         time_zone => 'Europe/London',
216     );
217
218     {                                                  ## 'Datedue' tests
219
220         $cal = Koha::Calendar->new( branchcode => $mpl, days_mode => 'Datedue' );
221
222         is(
223             $cal->addDuration( $dt, $one_day_dur, 'days' ),    # tuesday
224             dt_from_string( '2012-07-05', 'iso' ),
225             'Single day add (Datedue, matches holiday, shift)'
226         );
227
228         is(
229             $cal->addDuration( $dt, $two_day_dur, 'days' ),
230             dt_from_string( '2012-07-05', 'iso' ),
231             'Two days add, skips holiday (Datedue)'
232         );
233
234         cmp_ok(
235             $cal->addDuration( $test_dt, $seven_day_dur, 'days' ), 'eq',
236             '2012-07-30T11:53:00',
237             'Add 7 days (Datedue)'
238         );
239
240         is(
241             $cal->addDuration( $saturday, $one_day_dur, 'days' )->day_of_week, 1,
242             'addDuration skips closed Sunday (Datedue)'
243         );
244
245         is(
246             $cal->addDuration( $day_after_christmas, -1, 'days' )->ymd(), '2012-12-24',
247             'Negative call to addDuration (Datedue)'
248         );
249
250         ## Note that the days_between API says closed days are not considered.
251         ## This tests are here as an API test.
252         cmp_ok(
253             $cal->days_between( $test_dt, $later_dt )->in_units('days'),
254             '==', 40, 'days_between calculates correctly (Days)'
255         );
256
257         cmp_ok(
258             $cal->days_between( $later_dt, $test_dt )->in_units('days'),
259             '==', 40, 'Test parameter order not relevant (Days)'
260         );
261     }
262
263     {    ## 'Calendar' tests'
264
265         $cal = Koha::Calendar->new( branchcode => $mpl, days_mode => 'Calendar' );
266
267         $dt = dt_from_string( '2012-07-03', 'iso' );
268
269         is(
270             $cal->addDuration( $dt, $one_day_dur, 'days' ),
271             dt_from_string( '2012-07-05', 'iso' ),
272             'Single day add (Calendar)'
273         );
274
275         cmp_ok(
276             $cal->addDuration( $test_dt, $seven_day_dur, 'days' ), 'eq',
277             '2012-08-01T11:53:00',
278             'Add 7 days (Calendar)'
279         );
280
281         is(
282             $cal->addDuration( $saturday, $one_day_dur, 'days' )->day_of_week, 1,
283             'addDuration skips closed Sunday (Calendar)'
284         );
285
286         is(
287             $cal->addDuration( $day_after_christmas, -1, 'days' )->ymd(), '2012-12-24',
288             'Negative call to addDuration (Calendar)'
289         );
290
291         cmp_ok(
292             $cal->days_between( $test_dt, $later_dt )->in_units('days'),
293             '==', 40, 'days_between calculates correctly (Calendar)'
294         );
295
296         cmp_ok(
297             $cal->days_between( $later_dt, $test_dt )->in_units('days'),
298             '==', 40, 'Test parameter order not relevant (Calendar)'
299         );
300     }
301
302     {    ## 'Days' tests
303
304         $cal = Koha::Calendar->new( branchcode => $mpl, days_mode => 'Days' );
305
306         $dt = dt_from_string( '2012-07-03', 'iso' );
307
308         is(
309             $cal->addDuration( $dt, $one_day_dur, 'days' ),
310             dt_from_string( '2012-07-04', 'iso' ),
311             'Single day add (Days)'
312         );
313
314         cmp_ok(
315             $cal->addDuration( $test_dt, $seven_day_dur, 'days' ), 'eq',
316             '2012-07-30T11:53:00',
317             'Add 7 days (Days)'
318         );
319
320         is(
321             $cal->addDuration( $saturday, $one_day_dur, 'days' )->day_of_week, 7,
322             'addDuration doesn\'t skip closed Sunday (Days)'
323         );
324
325         is(
326             $cal->addDuration( $day_after_christmas, -1, 'days' )->ymd(), '2012-12-25',
327             'Negative call to addDuration (Days)'
328         );
329
330         ## Note that the days_between API says closed days are not considered.
331         ## This tests are here as an API test.
332         cmp_ok(
333             $cal->days_between( $test_dt, $later_dt )->in_units('days'),
334             '==', 40, 'days_between calculates correctly (Days)'
335         );
336
337         cmp_ok(
338             $cal->days_between( $later_dt, $test_dt )->in_units('days'),
339             '==', 40, 'Test parameter order not relevant (Days)'
340         );
341
342     }
343
344     {
345         $cal = Koha::Calendar->new( branchcode => $cpl );
346         is( $cal->is_holiday($single_holiday), 0, 'Single holiday for MPL, not CPL' );
347         is(
348             $cal->is_holiday($holiday_for_another_branch), 1,
349             'Holiday defined for CPL should be defined as an holiday'
350         );
351     }
352
353     subtest 'days_mode parameter' => sub {
354         plan tests => 1;
355
356         t::lib::Mocks::mock_preference( 'useDaysMode', 'Days' );
357
358         $cal = Koha::Calendar->new( branchcode => $cpl, days_mode => 'Calendar' );
359         is( $cal->{days_mode}, 'Calendar', q|If set, days_mode is correctly set| );
360     };
361
362     $cache->clear_from_cache( $mpl . '_holidays' );
363     $cache->clear_from_cache( $cpl . '_holidays' );
364 };
365
366 my $library  = $builder->build_object( { class => 'Koha::Libraries' } );
367 my $calendar = Koha::Calendar->new( branchcode => $library->branchcode, days_mode => 'Calendar' );
368 my $holiday  = $builder->build(
369     {
370         source => 'SpecialHoliday',
371         value  => {
372             branchcode  => $library->branchcode,
373             day         => $holiday_dt->day,
374             month       => $holiday_dt->month,
375             year        => $holiday_dt->year,
376             title       => 'My holiday',
377             isexception => 0
378         },
379     }
380 );
381
382 subtest 'days_forward' => sub {
383     plan tests => 4;
384
385     my $forwarded_dt = $calendar->days_forward( $today, 2 );
386     my $expected     = $today->clone->add( days => 2 );
387     is( $forwarded_dt->ymd, $expected->ymd, 'With no holiday on the perioddays_forward should add 2 days' );
388
389     $forwarded_dt = $calendar->days_forward( $today, 5 );
390     $expected     = $today->clone->add( days => 6 );
391     is(
392         $forwarded_dt->ymd, $expected->ymd,
393         'With holiday on the perioddays_forward should add 5 days + 1 day for holiday'
394     );
395
396     $forwarded_dt = $calendar->days_forward( $today, 0 );
397     is( $forwarded_dt->ymd, $today->ymd, '0 day should return start dt' );
398
399     $forwarded_dt = $calendar->days_forward( $today, -2 );
400     is( $forwarded_dt->ymd, $today->ymd, 'negative day should return start dt' );
401 };
402
403 subtest 'crossing_DST' => sub {
404
405     plan tests => 3;
406
407     my $tz           = DateTime::TimeZone->new( name => 'America/New_York' );
408     my $start_date   = dt_from_string( "2016-03-09 02:29:00", undef, $tz );
409     my $end_date     = dt_from_string( "2017-01-01 00:00:00", undef, $tz );
410     my $days_between = $calendar->days_between( $start_date, $end_date );
411     is( $days_between->delta_days, 298, "Days calculated correctly" );
412     $days_between = $calendar->days_between( $end_date, $start_date );
413     is( $days_between->delta_days, 298, "Swapping returns the same" );
414     my $hours_between = $calendar->hours_between( $start_date, $end_date );
415     is(
416         $hours_between->delta_minutes,
417         298 * 24 * 60 - 149,
418         "Hours (in minutes) calculated correctly"
419     );
420 };
421
422 subtest 'hours_between | days_between' => sub {
423
424     plan tests => 2;
425
426     #    November 2019
427     # Su Mo Tu We Th Fr Sa
428     #                 1  2
429     #  3  4 *5* 6  7  8  9
430     # 10 11 12 13 14 15 16
431     # 17 18 19 20 21 22 23
432     # 24 25 26 27 28 29 30
433
434     my $now    = dt_from_string('2019-11-05 12:34:56');    # Today is 2019 Nov 5th
435     my $nov_6  = dt_from_string('2019-11-06 12:34:56');
436     my $nov_7  = dt_from_string('2019-11-07 12:34:56');
437     my $nov_12 = dt_from_string('2019-11-12 12:34:56');
438     my $nov_13 = dt_from_string('2019-11-13 12:34:56');
439     my $nov_15 = dt_from_string('2019-11-15 12:34:56');
440     Time::Fake->offset( $now->epoch );
441
442     subtest 'No holiday' => sub {
443
444         plan tests => 2;
445
446         my $library  = $builder->build_object( { class => 'Koha::Libraries' } );
447         my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
448
449         subtest 'Same hours' => sub {
450
451             plan tests => 8;
452
453             # Between 5th and 6th
454             my $diff_hours = $calendar->hours_between( $now, $nov_6 )->hours;
455             is( $diff_hours, 1 * 24, 'hours: 1 day, no holiday' );
456             my $diff_days = $calendar->days_between( $now, $nov_6 )->delta_days;
457             is( $diff_days, 1, 'days: 1 day, no holiday' );
458
459             # Between 5th and 7th
460             $diff_hours = $calendar->hours_between( $now, $nov_7 )->hours;
461             is( $diff_hours, 2 * 24, 'hours: 2 days, no holiday' );
462             $diff_days = $calendar->days_between( $now, $nov_7 )->delta_days;
463             is( $diff_days, 2, 'days: 2 days, no holiday' );
464
465             # Between 5th and 12th
466             $diff_hours = $calendar->hours_between( $now, $nov_12 )->hours;
467             is( $diff_hours, 7 * 24, 'hours: 7 days, no holiday' );
468             $diff_days = $calendar->days_between( $now, $nov_12 )->delta_days;
469             is( $diff_days, 7, 'days: 7 days, no holiday' );
470
471             # Between 5th and 15th
472             $diff_hours = $calendar->hours_between( $now, $nov_15 )->hours;
473             is( $diff_hours, 10 * 24, 'hours: 10 days, no holiday' );
474             $diff_days = $calendar->days_between( $now, $nov_15 )->delta_days;
475             is( $diff_days, 10, 'days: 10 days, no holiday' );
476         };
477
478         subtest 'Different hours' => sub {
479
480             plan tests => 10;
481
482             # Between 5th and 5th (Same day short hours loan)
483             my $diff_hours = $calendar->hours_between( $now, $now->clone->add( hours => 3 ) )->hours;
484             is( $diff_hours, 3, 'hours: 3 hours, no holidays' );
485             my $diff_days = $calendar->days_between( $now, $now->clone->add( hours => 3 ) )->delta_days;
486             is( $diff_days, 0, 'days: 3 hours, no holidays' );
487
488             # Between 5th and 6th
489             $diff_hours = $calendar->hours_between( $now, $nov_6->clone->subtract( hours => 3 ) )->hours;
490             is( $diff_hours, 1 * 24 - 3, 'hours: 21 hours, no holidays' );
491             $diff_days = $calendar->days_between( $now, $nov_6->clone->subtract( hours => 3 ) )->delta_days;
492             is( $diff_days, 1, 'days: 21 hours, no holidays' );
493
494             # Between 5th and 7th
495             $diff_hours = $calendar->hours_between( $now, $nov_7->clone->subtract( hours => 3 ) )->hours;
496             is( $diff_hours, 2 * 24 - 3, 'hours: 45 hours, no holidays' );
497             $diff_days = $calendar->days_between( $now, $nov_7->clone->subtract( hours => 3 ) )->delta_days;
498             is( $diff_days, 2, 'days: 45 hours, no holidays' );
499
500             # Between 5th and 12th
501             $diff_hours = $calendar->hours_between( $now, $nov_12->clone->subtract( hours => 3 ) )->hours;
502             is( $diff_hours, 7 * 24 - 3, 'hours: 165 hours, no holidays' );
503             $diff_days = $calendar->days_between( $now, $nov_12->clone->subtract( hours => 3 ) )->delta_days;
504             is( $diff_days, 7, 'days: 165 hours, no holidays' );
505
506             # Between 5th and 15th
507             $diff_hours = $calendar->hours_between( $now, $nov_15->clone->subtract( hours => 3 ) )->hours;
508             is( $diff_hours, 10 * 24 - 3, 'hours: 237 hours, no holidays' );
509             $diff_days = $calendar->days_between( $now, $nov_15->clone->subtract( hours => 3 ) )->delta_days;
510             is( $diff_days, 10, 'days: 237 hours, no holidays' );
511         };
512     };
513
514     subtest 'With holiday' => sub {
515         plan tests => 2;
516
517         my $library = $builder->build_object( { class => 'Koha::Libraries' } );
518
519         # Wednesdays are closed
520         my $dbh = C4::Context->dbh;
521         $dbh->do(
522             q|
523             INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
524             VALUES ( ?, ?, NULL, NULL, ?, '' )
525         |, undef, $library->branchcode, 3, 'Closed on Wednesday'
526         );
527
528         my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
529
530         subtest 'Same hours' => sub {
531             plan tests => 12;
532
533             my ( $diff_hours, $diff_days );
534
535             # Between 5th and 6th (This case should never happen in real code, one cannot return on a closed day)
536             $diff_hours = $calendar->hours_between( $now, $nov_6 )->hours;
537             is( $diff_hours, 0 * 24, 'hours: 1 day, end_dt = holiday' );    # FIXME Is this really should be 0?
538             $diff_days = $calendar->days_between( $now, $nov_6 )->delta_days;
539             is( $diff_days, 0, 'days: 1 day, end_dt = holiday' );           # FIXME Is this really should be 0?
540
541             # Between 6th and 7th (This case should never happen in real code, one cannot issue on a closed day)
542             $diff_hours = $calendar->hours_between( $nov_6, $nov_7 )->hours;
543             is( $diff_hours, 0 * 24, 'hours: 1 day, start_dt = holiday' );    # FIXME Is this really should be 0?
544             $diff_days = $calendar->days_between( $nov_6, $nov_7 )->delta_days;
545             is( $diff_days, 0, 'days: 1 day, start_dt = holiday' );           # FIXME Is this really should be 0?
546
547             # Between 5th and 7th
548             $diff_hours = $calendar->hours_between( $now, $nov_7 )->hours;
549             is( $diff_hours, 2 * 24 - 1 * 24, 'hours: 2 days, 1 holiday' );
550             $diff_days = $calendar->days_between( $now, $nov_7 )->delta_days;
551             is( $diff_days, 2 - 1, 'days: 2 days, 1 holiday' );
552
553             # Between 5th and 12th
554             $diff_hours = $calendar->hours_between( $now, $nov_12 )->hours;
555             is( $diff_hours, 7 * 24 - 1 * 24, 'hours: 7 days, 1 holiday' );
556             $diff_days = $calendar->days_between( $now, $nov_12 )->delta_days;
557             is( $diff_days, 7 - 1, 'day: 7 days, 1 holiday' );
558
559             # Between 5th and 13th
560             $diff_hours = $calendar->hours_between( $now, $nov_13 )->hours;
561             is( $diff_hours, 8 * 24 - 2 * 24, 'hours: 8 days, 2 holidays' );
562             $diff_days = $calendar->days_between( $now, $nov_13 )->delta_days;
563             is( $diff_days, 8 - 2, 'days: 8 days, 2 holidays' );
564
565             # Between 5th and 15th
566             $diff_hours = $calendar->hours_between( $now, $nov_15 )->hours;
567             is( $diff_hours, 10 * 24 - 2 * 24, 'hours: 10 days, 2 holidays' );
568             $diff_days = $calendar->days_between( $now, $nov_15 )->delta_days;
569             is( $diff_days, 10 - 2, 'days: 10 days, 2 holidays' );
570         };
571
572         subtest 'Different hours' => sub {
573             plan tests => 14;
574
575             my ( $diff_hours, $diff_days );
576
577             # Between 5th and 5th (Same day short hours loan)
578             # No test - Tested above as 5th is an open day
579
580             # Between 5th and 6th (This case should never happen in real code, one cannot return on a closed day)
581             my $duration = $calendar->hours_between( $now, $nov_6->clone->subtract( hours => 3 ) );
582             is( $duration->hours, abs( 0 * 24 - 3 ), 'hours: 21 hours, end_dt = holiday' )
583                 ;    # FIXME $duration->hours always return a abs
584             is( $duration->is_negative, 1, '? is negative ?' )
585                 ;    # FIXME Do really test for that case in our calls to hours_between?
586             $duration = $calendar->days_between( $now, $nov_6->clone->subtract( hours => 3 ) );
587             is( $duration->hours, abs(0), 'days: 21 hours, end_dt = holiday' );    # FIXME Is this correct?
588
589             # Between 6th and 7th (This case should never happen in real code, one cannot issue on a closed day)
590             $duration = $calendar->hours_between( $nov_6, $nov_7->clone->subtract( hours => 3 ) );
591             is( $duration->hours, abs( 0 * 24 - 3 ), 'hours: 21 hours, start_dt = holiday' )
592                 ;    # FIXME $duration->hours always return a abs
593             is( $duration->is_negative, 1, '? is negative ?' )
594                 ;    # FIXME Do really test for that case in our calls to hours_between?
595             $duration = $calendar->days_between( $nov_6, $nov_7->clone->subtract( hours => 3 ) );
596             is( $duration->hours, abs(0), 'days: 21 hours, start_dt = holiday' );    # FIXME Is this correct?
597
598             # Between 5th and 7th
599             $diff_hours = $calendar->hours_between( $now, $nov_7->clone->subtract( hours => 3 ) )->hours;
600             is( $diff_hours, 2 * 24 - 1 * 24 - 3, 'hours: 45 hours, 1 holiday' );
601             $diff_days = $calendar->days_between( $now, $nov_7->clone->subtract( hours => 3 ) )->delta_days;
602             is( $diff_days, 2 - 1, 'days: 45 hours, 1 holiday' );
603
604             # Between 5th and 12th
605             $diff_hours = $calendar->hours_between( $now, $nov_12->clone->subtract( hours => 3 ) )->hours;
606             is( $diff_hours, 7 * 24 - 1 * 24 - 3, 'hours: 165 hours, 1 holiday' );
607             $diff_days = $calendar->days_between( $now, $nov_12->clone->subtract( hours => 3 ) )->delta_days;
608             is( $diff_days, 7 - 1, 'days: 165 hours, 1 holiday' );
609
610             # Between 5th and 13th
611             $diff_hours = $calendar->hours_between( $now, $nov_13->clone->subtract( hours => 3 ) )->hours;
612             is( $diff_hours, 8 * 24 - 2 * 24 - 3, 'hours: 289 hours, 2 holidays ' );
613             $diff_days = $calendar->days_between( $now, $nov_13->clone->subtract( hours => 3 ) )->delta_days;
614             is( $diff_days, 8 - 1, 'days: 289 hours, 2 holidays' );
615
616             # Between 5th and 15th
617             $diff_hours = $calendar->hours_between( $now, $nov_15->clone->subtract( hours => 3 ) )->hours;
618             is( $diff_hours, 10 * 24 - 2 * 24 - 3, 'hours: 237 hours, 2 holidays' );
619             $diff_days = $calendar->days_between( $now, $nov_15->clone->subtract( hours => 3 ) )->delta_days;
620             is( $diff_days, 10 - 2, 'days: 237 hours, 2 holidays' );
621         };
622
623     };
624
625     Time::Fake->reset;
626 };
627
628 subtest 'is_holiday' => sub {
629     plan tests => 1;
630
631     subtest 'weekday holidays' => sub {
632         plan tests => 7;
633
634         my $library = $builder->build_object( { class => 'Koha::Libraries' } );
635
636         my $day = dt_from_string();
637         my $dow = scalar $day->day_of_week;
638         $dow = 0 if $dow == 7;
639
640         # Closed this day of the week
641         my $dbh = C4::Context->dbh;
642         $dbh->do(
643             q|
644             INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
645             VALUES ( ?, ?, NULL, NULL, ?, '' )
646         |, undef, $library->branchcode, $dow, "TEST"
647         );
648
649         # Iterate 7 days
650         my $sth = $dbh->prepare("UPDATE repeatable_holidays SET weekday = ? WHERE branchcode = ? AND title = 'TEST'");
651         for my $i ( 0 .. 6 ) {
652             my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
653
654             is( $calendar->is_holiday($day), 1, $day->day_name() . " works as a repeatable holiday" );
655
656             # Increment the date and holiday day
657             $day->add( days => 1 );
658             $dow++;
659             $dow = 0 if $dow == 7;
660             $sth->execute( $dow, $library->branchcode );
661         }
662     };
663 };
664
665 subtest 'get_push_amt' => sub {
666     plan tests => 1;
667
668     t::lib::Mocks::mock_preference( 'useDaysMode', 'Dayweek' );
669
670     subtest 'weekday holidays' => sub {
671         plan tests => 7;
672
673         my $library = $builder->build_object( { class => 'Koha::Libraries' } );
674
675         my $day = dt_from_string();
676         my $dow = scalar $day->day_of_week;
677         $dow = 0 if $dow == 7;
678
679         # Closed this day of the week
680         my $dbh = C4::Context->dbh;
681         $dbh->do(
682             q|
683             INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
684             VALUES ( ?, ?, NULL, NULL, ?, '' )
685         |, undef, $library->branchcode, $dow, "TEST"
686         );
687
688         # Iterate 7 days
689         my $sth = $dbh->prepare("UPDATE repeatable_holidays SET weekday = ? WHERE branchcode = ? AND title = 'TEST'");
690         for my $i ( 0 .. 6 ) {
691             my $calendar = Koha::Calendar->new( branchcode => $library->branchcode, days_mode => 'Dayweek' );
692
693             my $npa;
694             eval {
695                 local $SIG{ALRM} = sub { die "alarm\n" };    # NB: \n required
696                 alarm 2;
697                 $npa = $calendar->next_open_days( $day, 0 );
698                 alarm 0;
699             };
700             if ($@) {
701                 die unless $@ eq "alarm\n";                  # propagate unexpected errors
702                                                              # timed out
703                 ok( 0, "next_push_amt succeeded for " . $day->day_name() . " weekday holiday" );
704             } else {
705                 ok( $npa, "next_push_amt succeeded for " . $day->day_name() . " weekday holiday" );
706             }
707
708             # Increment the date and holiday day
709             $day->add( days => 1 );
710             $dow++;
711             $dow = 0 if $dow == 7;
712             $sth->execute( $dow, $library->branchcode );
713         }
714     };
715 };
716
717 $schema->storage->txn_rollback();