Bug 26589: Fix t/db_dependent/OAI/Sets.t failing when OAI-PMH:AutoUpdateSets is enabled
[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 Koha::Caches;
28 use Koha::DateUtils;
29
30 use_ok('Koha::Calendar');
31
32 my $schema  = Koha::Database->new->schema;
33 $schema->storage->txn_begin;
34
35 my $today = dt_from_string();
36 my $holiday_dt = $today->clone;
37 $holiday_dt->add(days => 3);
38
39 Koha::Caches->get_instance()->flush_all();
40
41 my $builder = t::lib::TestBuilder->new();
42 my $library = $builder->build_object({ class => 'Koha::Libraries' });
43 my $holiday = $builder->build(
44     {
45         source => 'SpecialHoliday',
46         value  => {
47             branchcode  => $library->branchcode,
48             day         => $holiday_dt->day,
49             month       => $holiday_dt->month,
50             year        => $holiday_dt->year,
51             title       => 'My holiday',
52             isexception => 0
53         },
54     }
55 );
56
57 t::lib::Mocks::mock_preference('useDaysMode', 'Calendar');
58 my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
59
60 subtest 'days_forward' => sub {
61
62     plan tests => 4;
63     my $forwarded_dt = $calendar->days_forward( $today, 2 );
64     my $expected = $today->clone->add( days => 2 );
65     is( $forwarded_dt->ymd, $expected->ymd, 'With no holiday on the perioddays_forward should add 2 days' );
66
67     $forwarded_dt = $calendar->days_forward( $today, 5 );
68     $expected = $today->clone->add( days => 6 );
69     is( $forwarded_dt->ymd, $expected->ymd, 'With holiday on the perioddays_forward should add 5 days + 1 day for holiday'
70     );
71
72     $forwarded_dt = $calendar->days_forward( $today, 0 );
73     is( $forwarded_dt->ymd, $today->ymd, '0 day should return start dt' );
74
75     $forwarded_dt = $calendar->days_forward( $today, -2 );
76     is( $forwarded_dt->ymd, $today->ymd, 'negative day should return start dt' );
77 };
78
79 subtest 'crossing_DST' => sub {
80
81     plan tests => 3;
82
83     my $tz = DateTime::TimeZone->new( name => 'America/New_York' );
84     my $start_date = dt_from_string( "2016-03-09 02:29:00", undef, $tz );
85     my $end_date   = dt_from_string( "2017-01-01 00:00:00", undef, $tz );
86     my $days_between = $calendar->days_between( $start_date, $end_date );
87     is( $days_between->delta_days, 298, "Days calculated correctly" );
88     $days_between = $calendar->days_between( $end_date, $start_date );
89     is( $days_between->delta_days, 298, "Swapping returns the same" );
90     my $hours_between = $calendar->hours_between( $start_date, $end_date );
91     is(
92         $hours_between->delta_minutes,
93         298 * 24 * 60 - 149,
94         "Hours (in minutes) calculated correctly"
95     );
96 };
97
98 subtest 'hours_between | days_between' => sub {
99
100     plan tests => 2;
101
102     #    November 2019
103     # Su Mo Tu We Th Fr Sa
104     #                 1  2
105     #  3  4 *5* 6  7  8  9
106     # 10 11 12 13 14 15 16
107     # 17 18 19 20 21 22 23
108     # 24 25 26 27 28 29 30
109
110     my $now    = dt_from_string('2019-11-05 12:34:56'); # Today is 2019 Nov 5th
111     my $nov_6  = dt_from_string('2019-11-06 12:34:56');
112     my $nov_7  = dt_from_string('2019-11-07 12:34:56');
113     my $nov_12 = dt_from_string('2019-11-12 12:34:56');
114     my $nov_13 = dt_from_string('2019-11-13 12:34:56');
115     my $nov_15 = dt_from_string('2019-11-15 12:34:56');
116     Time::Fake->offset($now->epoch);
117
118     subtest 'No holiday' => sub {
119
120         plan tests => 2;
121
122         my $library = $builder->build_object({ class => 'Koha::Libraries' });
123         my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
124
125         subtest 'Same hours' => sub {
126
127             plan tests => 8;
128
129             # Between 5th and 6th
130             my $diff_hours = $calendar->hours_between( $now, $nov_6 )->hours;
131             is( $diff_hours, 1 * 24, 'hours: 1 day, no holiday' );
132             my $diff_days = $calendar->days_between( $now, $nov_6 )->delta_days;
133             is( $diff_days, 1, 'days: 1 day, no holiday' );
134
135             # Between 5th and 7th
136             $diff_hours = $calendar->hours_between( $now, $nov_7 )->hours;
137             is( $diff_hours, 2 * 24, 'hours: 2 days, no holiday' );
138             $diff_days = $calendar->days_between( $now, $nov_7 )->delta_days;
139             is( $diff_days, 2, 'days: 2 days, no holiday' );
140
141             # Between 5th and 12th
142             $diff_hours = $calendar->hours_between( $now, $nov_12 )->hours;
143             is( $diff_hours, 7 * 24, 'hours: 7 days, no holiday' );
144             $diff_days = $calendar->days_between( $now, $nov_12 )->delta_days;
145             is( $diff_days, 7, 'days: 7 days, no holiday' );
146
147             # Between 5th and 15th
148             $diff_hours = $calendar->hours_between( $now, $nov_15 )->hours;
149             is( $diff_hours, 10 * 24, 'hours: 10 days, no holiday' );
150             $diff_days = $calendar->days_between( $now, $nov_15 )->delta_days;
151             is( $diff_days, 10, 'days: 10 days, no holiday' );
152         };
153
154         subtest 'Different hours' => sub {
155
156             plan tests => 10;
157
158             # Between 5th and 5th (Same day short hours loan)
159             my $diff_hours = $calendar->hours_between( $now, $now->clone->add(hours => 3) )->hours;
160             is( $diff_hours, 3, 'hours: 3 hours, no holidays' );
161             my $diff_days = $calendar->days_between( $now, $now->clone->add(hours => 3) )->delta_days;
162             is( $diff_days, 0, 'days: 3 hours, no holidays' );
163
164             # Between 5th and 6th
165             $diff_hours = $calendar->hours_between( $now, $nov_6->clone->subtract(hours => 3) )->hours;
166             is( $diff_hours, 1 * 24 - 3, 'hours: 21 hours, no holidays' );
167             $diff_days = $calendar->days_between( $now, $nov_6->clone->subtract(hours => 3) )->delta_days;
168             is( $diff_days, 1, 'days: 21 hours, no holidays' );
169
170             # Between 5th and 7th
171             $diff_hours = $calendar->hours_between( $now, $nov_7->clone->subtract(hours => 3) )->hours;
172             is( $diff_hours, 2 * 24 - 3, 'hours: 45 hours, no holidays' );
173             $diff_days = $calendar->days_between( $now, $nov_7->clone->subtract(hours => 3) )->delta_days;
174             is( $diff_days, 2, 'days: 45 hours, no holidays' );
175
176             # Between 5th and 12th
177             $diff_hours = $calendar->hours_between( $now, $nov_12->clone->subtract(hours => 3) )->hours;
178             is( $diff_hours, 7 * 24 - 3, 'hours: 165 hours, no holidays' );
179             $diff_days = $calendar->days_between( $now, $nov_12->clone->subtract(hours => 3) )->delta_days;
180             is( $diff_days, 7, 'days: 165 hours, no holidays' );
181
182             # Between 5th and 15th
183             $diff_hours = $calendar->hours_between( $now, $nov_15->clone->subtract(hours => 3) )->hours;
184             is( $diff_hours, 10 * 24 - 3, 'hours: 237 hours, no holidays' );
185             $diff_days = $calendar->days_between( $now, $nov_15->clone->subtract(hours => 3) )->delta_days;
186             is( $diff_days, 10, 'days: 237 hours, no holidays' );
187         };
188     };
189
190     subtest 'With holiday' => sub {
191         plan tests => 2;
192
193         my $library = $builder->build_object({ class => 'Koha::Libraries' });
194
195         # Wednesdays are closed
196         my $dbh = C4::Context->dbh;
197         $dbh->do(q|
198             INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
199             VALUES ( ?, ?, NULL, NULL, ?, '' )
200         |, undef, $library->branchcode, 3, 'Closed on Wednesday');
201
202         my $calendar = Koha::Calendar->new( branchcode => $library->branchcode );
203
204         subtest 'Same hours' => sub {
205             plan tests => 12;
206
207             my ( $diff_hours, $diff_days );
208
209             # Between 5th and 6th (This case should never happen in real code, one cannot return on a closed day)
210             $diff_hours = $calendar->hours_between( $now, $nov_6 )->hours;
211             is( $diff_hours, 0 * 24, 'hours: 1 day, end_dt = holiday' ); # FIXME Is this really should be 0?
212             $diff_days = $calendar->days_between( $now, $nov_6)->delta_days;
213             is( $diff_days, 0, 'days: 1 day, end_dt = holiday' ); # FIXME Is this really should be 0?
214
215             # Between 6th and 7th (This case should never happen in real code, one cannot issue on a closed day)
216             $diff_hours = $calendar->hours_between( $nov_6, $nov_7 )->hours;
217             is( $diff_hours, 0 * 24, 'hours: 1 day, start_dt = holiday' ); # FIXME Is this really should be 0?
218             $diff_days = $calendar->days_between( $nov_6, $nov_7 )->delta_days;
219             is( $diff_days, 0, 'days: 1 day, start_dt = holiday' ); # FIXME Is this really should be 0?
220
221             # Between 5th and 7th
222             $diff_hours = $calendar->hours_between( $now, $nov_7 )->hours;
223             is( $diff_hours, 2 * 24 - 1 * 24, 'hours: 2 days, 1 holiday' );
224             $diff_days = $calendar->days_between( $now, $nov_7 )->delta_days;
225             is( $diff_days, 2 - 1, 'days: 2 days, 1 holiday' );
226
227             # Between 5th and 12th
228             $diff_hours = $calendar->hours_between( $now, $nov_12 )->hours;
229             is( $diff_hours, 7 * 24 - 1 * 24, 'hours: 7 days, 1 holiday' );
230             $diff_days = $calendar->days_between( $now, $nov_12)->delta_days;
231             is( $diff_days, 7 - 1, 'day: 7 days, 1 holiday' );
232
233             # Between 5th and 13th
234             $diff_hours = $calendar->hours_between( $now, $nov_13 )->hours;
235             is( $diff_hours, 8 * 24 - 2 * 24, 'hours: 8 days, 2 holidays' );
236             $diff_days = $calendar->days_between( $now, $nov_13)->delta_days;
237             is( $diff_days, 8 - 2, 'days: 8 days, 2 holidays' );
238
239             # Between 5th and 15th
240             $diff_hours = $calendar->hours_between( $now, $nov_15 )->hours;
241             is( $diff_hours, 10 * 24 - 2 * 24, 'hours: 10 days, 2 holidays' );
242             $diff_days = $calendar->days_between( $now, $nov_15)->delta_days;
243             is( $diff_days, 10 - 2, 'days: 10 days, 2 holidays' );
244         };
245
246         subtest 'Different hours' => sub {
247             plan tests => 14;
248
249             my ( $diff_hours, $diff_days );
250
251             # Between 5th and 5th (Same day short hours loan)
252             # No test - Tested above as 5th is an open day
253
254             # Between 5th and 6th (This case should never happen in real code, one cannot return on a closed day)
255             my $duration = $calendar->hours_between( $now, $nov_6->clone->subtract(hours => 3) );
256             is( $duration->hours, abs(0 * 24 - 3), 'hours: 21 hours, end_dt = holiday' ); # FIXME $duration->hours always return a abs
257             is( $duration->is_negative, 1, '? is negative ?' ); # FIXME Do really test for that case in our calls to hours_between?
258             $duration = $calendar->days_between( $now, $nov_6->clone->subtract(hours => 3) );
259             is( $duration->hours, abs(0), 'days: 21 hours, end_dt = holiday' ); # FIXME Is this correct?
260
261             # Between 6th and 7th (This case should never happen in real code, one cannot issue on a closed day)
262             $duration = $calendar->hours_between( $nov_6, $nov_7->clone->subtract(hours => 3) );
263             is( $duration->hours, abs(0 * 24 - 3), 'hours: 21 hours, start_dt = holiday' ); # FIXME $duration->hours always return a abs
264             is( $duration->is_negative, 1, '? is negative ?' ); # FIXME Do really test for that case in our calls to hours_between?
265             $duration = $calendar->days_between( $nov_6, $nov_7->clone->subtract(hours => 3) );
266             is( $duration->hours, abs(0), 'days: 21 hours, start_dt = holiday' ); # FIXME Is this correct?
267
268             # Between 5th and 7th
269             $diff_hours = $calendar->hours_between( $now, $nov_7->clone->subtract(hours => 3) )->hours;
270             is( $diff_hours, 2 * 24 - 1 * 24 - 3, 'hours: 45 hours, 1 holiday' );
271             $diff_days = $calendar->days_between( $now, $nov_7->clone->subtract(hours => 3) )->delta_days;
272             is( $diff_days, 2 - 1, 'days: 45 hours, 1 holiday' );
273
274             # Between 5th and 12th
275             $diff_hours = $calendar->hours_between( $now, $nov_12->clone->subtract(hours => 3) )->hours;
276             is( $diff_hours, 7 * 24 - 1 * 24 - 3, 'hours: 165 hours, 1 holiday' );
277             $diff_days = $calendar->days_between( $now, $nov_12->clone->subtract(hours => 3) )->delta_days;
278             is( $diff_days, 7 - 1, 'days: 165 hours, 1 holiday' );
279
280             # Between 5th and 13th
281             $diff_hours = $calendar->hours_between( $now, $nov_13->clone->subtract(hours => 3) )->hours;
282             is( $diff_hours, 8 * 24 - 2 * 24 - 3, 'hours: 289 hours, 2 holidays ' );
283             $diff_days = $calendar->days_between( $now, $nov_13->clone->subtract(hours => 3) )->delta_days;
284             is( $diff_days, 8 - 1, 'days: 289 hours, 2 holidays' );
285
286             # Between 5th and 15th
287             $diff_hours = $calendar->hours_between( $now, $nov_15->clone->subtract(hours => 3) )->hours;
288             is( $diff_hours, 10 * 24 - 2 * 24 - 3, 'hours: 237 hours, 2 holidays' );
289             $diff_days = $calendar->days_between( $now, $nov_15->clone->subtract(hours => 3) )->delta_days;
290             is( $diff_days, 10 - 2, 'days: 237 hours, 2 holidays' );
291         };
292
293     };
294
295     Time::Fake->reset;
296 };
297
298 subtest 'is_holiday' => sub {
299     plan tests => 1;
300
301     subtest 'weekday holidays' => sub {
302         plan tests => 7;
303
304         my $library = $builder->build_object( { class => 'Koha::Libraries' } );
305
306         my $day = DateTime->now();
307         my $dow = scalar $day->day_of_week;
308         $dow = 0 if $dow == 7;
309
310         # Closed this day of the week
311         my $dbh = C4::Context->dbh;
312         $dbh->do(
313             q|
314             INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
315             VALUES ( ?, ?, NULL, NULL, ?, '' )
316         |, undef, $library->branchcode, $dow, "TEST"
317         );
318
319         # Iterate 7 days
320         my $sth = $dbh->prepare(
321 "UPDATE repeatable_holidays SET weekday = ? WHERE branchcode = ? AND title = 'TEST'"
322         );
323         for my $i ( 0 .. 6 ) {
324             my $calendar =
325               Koha::Calendar->new( branchcode => $library->branchcode );
326
327             is( $calendar->is_holiday($day), 1, $day->day_name() ." works as a repeatable holiday");
328
329             # Increment the date and holiday day
330             $day->add( days => 1 );
331             $dow++;
332             $dow = 0 if $dow == 7;
333             $sth->execute($dow, $library->branchcode);
334         }
335     };
336 };
337
338 subtest 'get_push_amt' => sub {
339     plan tests => 1;
340
341     t::lib::Mocks::mock_preference('useDaysMode', 'Dayweek');
342
343     subtest 'weekday holidays' => sub {
344         plan tests => 7;
345
346         my $library = $builder->build_object( { class => 'Koha::Libraries' } );
347
348         my $day = dt_from_string();
349         my $dow = scalar $day->day_of_week;
350         $dow = 0 if $dow == 7;
351
352         # Closed this day of the week
353         my $dbh = C4::Context->dbh;
354         $dbh->do(
355             q|
356             INSERT INTO repeatable_holidays (branchcode,weekday,day,month,title,description)
357             VALUES ( ?, ?, NULL, NULL, ?, '' )
358         |, undef, $library->branchcode, $dow, "TEST"
359         );
360
361         # Iterate 7 days
362         my $sth = $dbh->prepare(
363 "UPDATE repeatable_holidays SET weekday = ? WHERE branchcode = ? AND title = 'TEST'"
364         );
365         for my $i ( 0 .. 6 ) {
366             my $calendar =
367               Koha::Calendar->new( branchcode => $library->branchcode, days_mode => 'Dayweek' );
368
369             my $npa;
370             eval {
371                 local $SIG{ALRM} = sub { die "alarm\n" };    # NB: \n required
372                 alarm 2;
373                 $npa = $calendar->next_open_days( $day, 0 );
374                 alarm 0;
375             };
376             if ($@) {
377                 die unless $@ eq "alarm\n";    # propagate unexpected errors
378                 # timed out
379                 ok(0, "next_push_amt succeeded for ".$day->day_name()." weekday holiday");
380             }
381             else {
382                 ok($npa, "next_push_amt succeeded for ".$day->day_name()." weekday holiday");
383             }
384
385             # Increment the date and holiday day
386             $day->add( days => 1 );
387             $dow++;
388             $dow = 0 if $dow == 7;
389             $sth->execute( $dow, $library->branchcode );
390         }
391     };
392 };
393
394 $schema->storage->txn_rollback();