diff --git a/t/db_dependent/Calendar.t b/t/db_dependent/Calendar.t index d6752b9da4..f938b5c79a 100755 --- a/t/db_dependent/Calendar.t +++ b/t/db_dependent/Calendar.t @@ -18,7 +18,7 @@ use Modern::Perl; use Test::NoWarnings; -use Test::More tests => 7; +use Test::More tests => 8; use Time::Fake; use t::lib::Mocks; @@ -26,6 +26,7 @@ use t::lib::TestBuilder; use DateTime; use DateTime::Duration; +use C4::Calendar; use Koha::Caches; use Koha::Library::Calendar; use Koha::Database; @@ -793,4 +794,59 @@ subtest 'get_push_amt' => sub { }; }; +subtest 'insert_single_holiday/insert_exception_holiday update the in-memory cache' => sub { + + # Migrated from t/db_dependent/Holidays.t (C4::Calendar in-memory cache sync) + plan tests => 4; + + $schema->storage->txn_begin; + + my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode}; + my $calendar = C4::Calendar->new( branchcode => $branchcode ); + + my $single_day = dt_from_string()->add( days => 10 ); + $calendar->insert_single_holiday( + day => $single_day->day, + month => $single_day->month, + year => $single_day->year, + title => 'Single', + description => '', + ); + is( + $calendar->isHoliday( $single_day->day, $single_day->month, $single_day->year ), 1, + 'isHoliday sees a single holiday just inserted on the same C4::Calendar object' + ); + + # Close every weekday so exception_day is a holiday until the exception is added. + foreach my $weekday ( 0 .. 6 ) { + $calendar->insert_week_day_holiday( weekday => $weekday, title => '', description => '' ); + } + + # insert_exception_holiday called with 'date' rather than day/month/year, + # exercising the date-string derivation path. + my $exception_day = dt_from_string()->add( days => 20 ); + $calendar->insert_exception_holiday( + date => $exception_day->ymd, + title => 'Exception', + description => '', + ); + is( + $calendar->isHoliday( $exception_day->day, $exception_day->month, $exception_day->year ), 0, + 'isHoliday sees an exception holiday just inserted on the same C4::Calendar object, overriding the weekday closure' + ); + + # A second, freshly instantiated C4::Calendar object should agree. + my $reloaded = C4::Calendar->new( branchcode => $branchcode ); + is( + $reloaded->isHoliday( $single_day->day, $single_day->month, $single_day->year ), 1, + 'A freshly instantiated C4::Calendar also sees the single holiday' + ); + is( + $reloaded->isHoliday( $exception_day->day, $exception_day->month, $exception_day->year ), 0, + 'A freshly instantiated C4::Calendar also sees the exception holiday' + ); + + $schema->storage->txn_rollback; +}; + $schema->storage->txn_rollback(); diff --git a/t/db_dependent/Holidays.t b/t/db_dependent/Holidays.t deleted file mode 100755 index 4572a668dc..0000000000 --- a/t/db_dependent/Holidays.t +++ /dev/null @@ -1,443 +0,0 @@ -#!/usr/bin/perl - -# This file is part of Koha. -# -# Koha is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# Koha is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Koha; if not, see . - -use Modern::Perl; - -use Test::NoWarnings; -use Test::More tests => 16; -use Test::Exception; - -use DateTime; -use DateTime::TimeZone; - -use t::lib::TestBuilder; -use C4::Context; -use Koha::Database; -use Koha::DateUtils qw( dt_from_string ); - -BEGIN { - use_ok('Koha::Library::Calendar'); - use_ok('C4::Calendar'); -} - -my $schema = Koha::Database->new->schema; -my $dbh = C4::Context->dbh; -my $builder = t::lib::TestBuilder->new; - -subtest 'is_holiday timezone tests' => sub { - - plan tests => 1; - - $schema->storage->txn_begin; - - $dbh->do("DELETE FROM library_single_closures"); - $dbh->do("DELETE FROM library_closure_exceptions"); - - # Clear cache - Koha::Caches->get_instance->flush_all; - - # Artificially set timezone - my $timezone = 'America/Santiago'; - $ENV{TZ} = $timezone; - use POSIX qw(tzset); - tzset; - - my $branch = $builder->build( { source => 'Branch' } )->{branchcode}; - my $calendar = Koha::Library::Calendar->new( branchcode => $branch ); - - C4::Calendar->new( branchcode => $branch )->insert_exception_holiday( - day => 6, - month => 9, - year => 2015, - title => 'Invalid date', - description => 'Invalid date description', - ); - - my $exception_holiday = DateTime->new( day => 6, month => 9, year => 2015 ); - my $now_dt = DateTime->now; - - my $diff; - eval { $diff = $calendar->days_between( $now_dt, $exception_holiday ) }; - unlike( - $@, - qr/Invalid local time for date in time zone: America\/Santiago/, - 'Avoid invalid datetime due to DST' - ); - - $schema->storage->txn_rollback; -}; - -$schema->storage->txn_begin; - -# Create two fresh branches for the tests -my $branch_1 = $builder->build( { source => 'Branch' } )->{branchcode}; -my $branch_2 = $builder->build( { source => 'Branch' } )->{branchcode}; - -C4::Calendar->new( branchcode => $branch_1 )->insert_week_day_holiday( - weekday => 0, - title => '', - description => 'Sundays', -); - -my $holiday2add = dt_from_string("2015-01-01"); -C4::Calendar->new( branchcode => $branch_1 )->insert_day_month_holiday( - day => $holiday2add->day(), - month => $holiday2add->month(), - year => $holiday2add->year(), - title => '', - description => "New Year's Day", -); -$holiday2add = dt_from_string("2014-12-25"); -C4::Calendar->new( branchcode => $branch_1 )->insert_day_month_holiday( - day => $holiday2add->day(), - month => $holiday2add->month(), - year => $holiday2add->year(), - title => '', - description => 'Christmas', -); - -my $koha_calendar = Koha::Library::Calendar->new( branchcode => $branch_1 ); -my $c4_calendar = C4::Calendar->new( branchcode => $branch_1 ); - -isa_ok( $koha_calendar, 'Koha::Library::Calendar', 'Koha::Library::Calendar class returned' ); -isa_ok( $c4_calendar, 'C4::Calendar', 'C4::Calendar class returned' ); - -my $sunday = DateTime->new( - year => 2011, - month => 6, - day => 26, -); -my $monday = DateTime->new( - year => 2011, - month => 6, - day => 27, -); -my $christmas = DateTime->new( - year => 2032, - month => 12, - day => 25, -); -my $newyear = DateTime->new( - year => 2035, - month => 1, - day => 1, -); - -is( $koha_calendar->is_holiday($sunday), 1, 'Sunday is a closed day' ); -is( $koha_calendar->is_holiday($monday), 0, 'Monday is not a closed day' ); -is( $koha_calendar->is_holiday($christmas), 1, 'Christmas is a closed day' ); -is( $koha_calendar->is_holiday($newyear), 1, 'New Years day is a closed day' ); - -$dbh->do("DELETE FROM library_weekly_closures"); -$dbh->do("DELETE FROM library_repeating_closures"); -$dbh->do("DELETE FROM library_single_closures"); -$dbh->do("DELETE FROM library_closure_exceptions"); - -my $custom_holiday = DateTime->new( - year => 2013, - month => 11, - day => 12, -); - -my $today = dt_from_string(); -C4::Calendar->new( branchcode => $branch_2 )->insert_single_holiday( - day => $today->day(), - month => $today->month(), - year => $today->year(), - title => "$today", - description => "$today", -); - -is( - Koha::Library::Calendar->new( branchcode => $branch_2 )->is_holiday($today), 1, - "Today is a holiday for $branch_2" -); -is( - Koha::Library::Calendar->new( branchcode => $branch_1 )->is_holiday($today), 0, - "Today is not a holiday for $branch_1" -); - -$schema->storage->txn_rollback; - -subtest 'copy_to_branch' => sub { - - plan tests => 10; - - $schema->storage->txn_begin; - - my $branch1 = $builder->build( { source => 'Branch' } )->{branchcode}; - my $calendar1 = C4::Calendar->new( branchcode => $branch1 ); - my $sunday = dt_from_string("2020-03-15"); - $calendar1->insert_week_day_holiday( - weekday => 0, - title => '', - description => 'Sundays', - ); - - my $day_month = dt_from_string("2020-03-17"); - $calendar1->insert_day_month_holiday( - day => $day_month->day(), - month => $day_month->month(), - year => $day_month->year(), - title => '', - description => "", - ); - - my $future_date = dt_from_string("9999-12-31"); - $calendar1->insert_single_holiday( - day => $future_date->day(), - month => $future_date->month(), - year => $future_date->year(), - title => "", - description => "", - ); - - my $future_exception = dt_from_string("9999-12-30"); - $calendar1->insert_exception_holiday( - day => $future_exception->day(), - month => $future_exception->month(), - year => $future_exception->year(), - title => "", - description => "", - ); - - my $past_date = dt_from_string("2019-11-20"); - $calendar1->insert_single_holiday( - day => $past_date->day(), - month => $past_date->month(), - year => $past_date->year(), - title => "", - description => "", - ); - - my $past_exception = dt_from_string("2020-03-09"); - $calendar1->insert_exception_holiday( - day => $past_exception->day(), - month => $past_exception->month(), - year => $past_exception->year(), - title => "", - description => "", - ); - - my $branch2 = $builder->build( { source => 'Branch' } )->{branchcode}; - - C4::Calendar->new( branchcode => $branch1 )->copy_to_branch($branch2); - - my $calendar2 = C4::Calendar->new( branchcode => $branch2 ); - my $exceptions = $calendar2->get_exception_holidays; - - is( $calendar2->isHoliday( $sunday->day, $sunday->month, $sunday->year ), 1, "Weekday holiday copied to branch 2" ); - is( - $calendar2->isHoliday( $day_month->day, $day_month->month, $day_month->year ), 1, - "Day/month holiday copied to branch 2" - ); - is( - $calendar2->isHoliday( $future_date->day, $future_date->month, $future_date->year ), 1, - "Single holiday copied to branch 2" - ); - is( ( grep { $_->{date} eq "9999-12-30" } values %$exceptions ), 1, "Exception holiday copied to branch 2" ); - is( - $calendar2->isHoliday( $past_date->day, $past_date->month, $past_date->year ), 0, - "Don't copy past single holidays" - ); - is( ( grep { $_->{date} eq "2020-03-09" } values %$exceptions ), 0, "Don't copy past exception holidays " ); - - C4::Calendar->new( branchcode => $branch1 )->copy_to_branch($branch2); - - #Select all rows with same values from database - my $dbh = C4::Context->dbh; - my $get_weekly_closures = "SELECT a.* FROM library_weekly_closures a - JOIN (SELECT library_id, weekday, COUNT(*) - FROM library_weekly_closures - GROUP BY library_id, weekday HAVING count(*) > 1) b - ON a.library_id = b.library_id AND a.weekday = b.weekday - ORDER BY a.library_id;"; - my $sth = $dbh->prepare($get_weekly_closures); - $sth->execute; - - my @weekly_closures; - while ( my $row = $sth->fetchrow_hashref ) { - push @weekly_closures, $row; - } - - is( scalar(@weekly_closures), 0, "None of the weekly closures were doubled" ); - - my $get_repeating_closures = "SELECT a.* FROM library_repeating_closures a - JOIN (SELECT library_id, day, month, COUNT(*) - FROM library_repeating_closures - GROUP BY library_id, day, month HAVING count(*) > 1) b - ON a.library_id = b.library_id AND a.day = b.day AND a.month = b.month - ORDER BY a.library_id;"; - $sth = $dbh->prepare($get_repeating_closures); - $sth->execute; - - my @repeating_closures; - while ( my $row = $sth->fetchrow_hashref ) { - push @repeating_closures, $row; - } - - is( scalar(@repeating_closures), 0, "None of the repeating closures were doubled" ); - - my $get_single_closures = "SELECT a.* FROM library_single_closures a - JOIN (SELECT library_id, date, COUNT(*) - FROM library_single_closures - GROUP BY library_id, date HAVING count(*) > 1) b - ON a.library_id = b.library_id AND a.date = b.date - ORDER BY a.library_id;"; - $sth = $dbh->prepare($get_single_closures); - $sth->execute; - - my @single_closures; - while ( my $row = $sth->fetchrow_hashref ) { - push @single_closures, $row; - } - - is( scalar(@single_closures), 0, "None of the single closures were doubled" ); - - my $get_closure_exceptions = "SELECT a.* FROM library_closure_exceptions a - JOIN (SELECT library_id, date, COUNT(*) - FROM library_closure_exceptions - GROUP BY library_id, date HAVING count(*) > 1) b - ON a.library_id = b.library_id AND a.date = b.date - ORDER BY a.library_id;"; - $sth = $dbh->prepare($get_closure_exceptions); - $sth->execute; - - my @closure_exceptions; - while ( my $row = $sth->fetchrow_hashref ) { - push @closure_exceptions, $row; - } - - is( scalar(@closure_exceptions), 0, "None of the closure exceptions were doubled" ); - - $schema->storage->txn_rollback; - -}; - -subtest 'with a library that is never open' => sub { - plan tests => 2; - - $schema->storage->txn_begin; - - my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode}; - my $calendar = C4::Calendar->new( branchcode => $branchcode ); - foreach my $weekday ( 0 .. 6 ) { - $calendar->insert_week_day_holiday( weekday => $weekday, title => '', description => '' ); - } - - my $now = dt_from_string; - - subtest 'next_open_days should throw an exception' => sub { - my $kcalendar = Koha::Library::Calendar->new( branchcode => $branchcode, days_mode => 'Calendar' ); - throws_ok { $kcalendar->next_open_days( $now, 1 ) } 'Koha::Exceptions::Calendar::NoOpenDays'; - }; - - subtest 'prev_open_days should throw an exception' => sub { - my $kcalendar = Koha::Library::Calendar->new( branchcode => $branchcode, days_mode => 'Calendar' ); - throws_ok { $kcalendar->prev_open_days( $now, 1 ) } 'Koha::Exceptions::Calendar::NoOpenDays'; - }; - - $schema->storage->txn_rollback; -}; - -subtest 'with a library that is *almost* never open' => sub { - plan tests => 2; - - $schema->storage->txn_begin; - - my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode}; - my $calendar = C4::Calendar->new( branchcode => $branchcode ); - foreach my $weekday ( 0 .. 6 ) { - $calendar->insert_week_day_holiday( weekday => $weekday, title => '', description => '' ); - } - - my $now = dt_from_string; - my $open_day_in_the_future = $now->clone()->add( years => 1 ); - my $open_day_in_the_past = $now->clone()->subtract( years => 1 ); - $calendar->insert_exception_holiday( date => $open_day_in_the_future->ymd, title => '', description => '' ); - $calendar->insert_exception_holiday( date => $open_day_in_the_past->ymd, title => '', description => '' ); - - subtest 'next_open_days should find the open day' => sub { - my $kcalendar = Koha::Library::Calendar->new( branchcode => $branchcode, days_mode => 'Calendar' ); - my $next_open_day = $kcalendar->next_open_days( $now, 1 ); - is( $next_open_day->ymd, $open_day_in_the_future->ymd ); - }; - - subtest 'prev_open_days should find the open day' => sub { - my $kcalendar = Koha::Library::Calendar->new( branchcode => $branchcode, days_mode => 'Calendar' ); - my $prev_open_day = $kcalendar->prev_open_days( $now, 1 ); - is( $prev_open_day->ymd, $open_day_in_the_past->ymd ); - }; - - $schema->storage->txn_rollback; -}; - -subtest 'insert_single_holiday/insert_exception_holiday update the in-memory cache' => sub { - plan tests => 4; - - $schema->storage->txn_begin; - - my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode}; - my $calendar = C4::Calendar->new( branchcode => $branchcode ); - - my $single_day = dt_from_string()->add( days => 10 ); - $calendar->insert_single_holiday( - day => $single_day->day, - month => $single_day->month, - year => $single_day->year, - title => 'Single', - description => '', - ); - is( - $calendar->isHoliday( $single_day->day, $single_day->month, $single_day->year ), 1, - 'isHoliday sees a single holiday just inserted on the same C4::Calendar object' - ); - - # Close every weekday so exception_day is a holiday until the exception is added. - foreach my $weekday ( 0 .. 6 ) { - $calendar->insert_week_day_holiday( weekday => $weekday, title => '', description => '' ); - } - - # insert_exception_holiday called with 'date' rather than day/month/year, - # exercising the date-string derivation path. - my $exception_day = dt_from_string()->add( days => 20 ); - $calendar->insert_exception_holiday( - date => $exception_day->ymd, - title => 'Exception', - description => '', - ); - is( - $calendar->isHoliday( $exception_day->day, $exception_day->month, $exception_day->year ), 0, - 'isHoliday sees an exception holiday just inserted on the same C4::Calendar object, overriding the weekday closure' - ); - - # A second, freshly instantiated C4::Calendar object should agree. - my $reloaded = C4::Calendar->new( branchcode => $branchcode ); - is( - $reloaded->isHoliday( $single_day->day, $single_day->month, $single_day->year ), 1, - 'A freshly instantiated C4::Calendar also sees the single holiday' - ); - is( - $reloaded->isHoliday( $exception_day->day, $exception_day->month, $exception_day->year ), 0, - 'A freshly instantiated C4::Calendar also sees the exception holiday' - ); - - $schema->storage->txn_rollback; -}; - -# Clear cache -Koha::Caches->get_instance->flush_all; diff --git a/t/db_dependent/Koha/Library/Calendar.t b/t/db_dependent/Koha/Library/Calendar.t index c92b9bba76..1b5ec2a270 100755 --- a/t/db_dependent/Koha/Library/Calendar.t +++ b/t/db_dependent/Koha/Library/Calendar.t @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl # Copyright 2025 Koha Development team # @@ -19,13 +19,15 @@ use Modern::Perl; -use Test::More tests => 12; +use Test::More tests => 15; use Test::Exception; use Test::NoWarnings; use Koha::Caches; use Koha::Database; use Koha::DateUtils qw( dt_from_string ); + +use DateTime; use Koha::Library::Calendar::WeeklyClosures; use Koha::Library::Calendar::RepeatingClosures; use Koha::Library::Calendar::SingleClosures; @@ -473,3 +475,167 @@ subtest 'delete_weekly_closure/delete_repeating_closure refresh in-process state $schema->storage->txn_rollback; }; + +subtest 'is_holiday DST edge case' => sub { + + # Migrated from t/db_dependent/Holidays.t + plan tests => 1; + + $schema->storage->txn_begin; + + Koha::Caches->get_instance->flush_all; + + # Artificially set a timezone with a DST transition that historically + # triggered "Invalid local time for date in time zone" in date math. + local $ENV{TZ} = 'America/Santiago'; + require POSIX; + POSIX::tzset(); + + my $library = $builder->build_object( { class => 'Koha::Libraries' } ); + my $calendar = Koha::Library::Calendar->new( branchcode => $library->branchcode ); + + $calendar->add_exception( + { date => '2015-09-06', title => 'Invalid date', description => 'Invalid date description' } ); + + my $exception_holiday = DateTime->new( day => 6, month => 9, year => 2015 ); + my $now_dt = dt_from_string; + + my $diff; + eval { $diff = $calendar->days_between( $now_dt, $exception_holiday ) }; + unlike( + $@, + qr{Invalid local time for date in time zone: America/Santiago}, + 'Avoid invalid datetime due to DST' + ); + + # Restore the process timezone so later teardown / end-of-run checks + # (e.g. Test::NoWarnings) are not affected by the artificial TZ. + delete $ENV{TZ}; + POSIX::tzset(); + + $schema->storage->txn_rollback; +}; + +subtest 'next_open_days / prev_open_days' => sub { + + # Migrated from t/db_dependent/Holidays.t + plan tests => 2; + + subtest 'with a library that is never open' => sub { + plan tests => 2; + + $schema->storage->txn_begin; + + my $library = $builder->build_object( { class => 'Koha::Libraries' } ); + my $branchcode = $library->branchcode; + my $calendar = Koha::Library::Calendar->new( branchcode => $branchcode ); + $calendar->add_weekly_closure( { weekday => $_, title => '', description => '' } ) for ( 0 .. 6 ); + + my $now = dt_from_string; + my $kcalendar = Koha::Library::Calendar->new( branchcode => $branchcode, days_mode => 'Calendar' ); + + throws_ok { $kcalendar->next_open_days( $now, 1 ) } 'Koha::Exceptions::Calendar::NoOpenDays', + 'next_open_days throws when the library is never open'; + throws_ok { $kcalendar->prev_open_days( $now, 1 ) } 'Koha::Exceptions::Calendar::NoOpenDays', + 'prev_open_days throws when the library is never open'; + + $schema->storage->txn_rollback; + }; + + subtest 'with a library that is *almost* never open' => sub { + plan tests => 2; + + $schema->storage->txn_begin; + + my $library = $builder->build_object( { class => 'Koha::Libraries' } ); + my $branchcode = $library->branchcode; + my $calendar = Koha::Library::Calendar->new( branchcode => $branchcode ); + $calendar->add_weekly_closure( { weekday => $_, title => '', description => '' } ) for ( 0 .. 6 ); + + my $now = dt_from_string; + my $open_day_in_the_future = $now->clone->add( years => 1 ); + my $open_day_in_the_past = $now->clone->subtract( years => 1 ); + + # Exceptions override the weekly closures, making those single days open + $calendar->add_exception( { date => $open_day_in_the_future->ymd, title => '', description => '' } ); + $calendar->add_exception( { date => $open_day_in_the_past->ymd, title => '', description => '' } ); + + my $kcalendar = Koha::Library::Calendar->new( branchcode => $branchcode, days_mode => 'Calendar' ); + + is( + $kcalendar->next_open_days( $now, 1 )->ymd, $open_day_in_the_future->ymd, + 'next_open_days finds the future open day created via an exception' + ); + is( + $kcalendar->prev_open_days( $now, 1 )->ymd, $open_day_in_the_past->ymd, + 'prev_open_days finds the past open day created via an exception' + ); + + $schema->storage->txn_rollback; + }; +}; + +subtest 'copy_to completeness' => sub { + + # Migrated from t/db_dependent/Holidays.t copy_to_branch: all four closure + # types copy, past-dated single/exception are excluded, and a second copy + # does not duplicate any of the four types. + plan tests => 8; + + $schema->storage->txn_begin; + + my $library = $builder->build_object( { class => 'Koha::Libraries' } ); + my $calendar = Koha::Library::Calendar->new( branchcode => $library->branchcode ); + + my $today = dt_from_string; + $calendar->add_weekly_closure( { weekday => 6, title => 'Saturdays', description => '' } ); + $calendar->add_repeating_closure( { day => 4, month => 7, title => 'Independence', description => '' } ); + $calendar->add_single_closure( { date => '2027-01-01', title => 'New Year', description => '' } ); + $calendar->add_exception( { date => '2027-01-02', title => 'Special opening', description => '' } ); + + # Past-dated single closure and exception must not be copied + my $past_date = $today->clone->subtract( years => 1 )->ymd; + $calendar->add_single_closure( { date => $past_date, title => 'Past single', description => '' } ); + $calendar->add_exception( { date => $past_date, title => 'Past exception', description => '' } ); + + my $library2 = $builder->build_object( { class => 'Koha::Libraries' } ); + $calendar->copy_to( $library2->branchcode ); + + is( + Koha::Library::Calendar::WeeklyClosures->search( { library_id => $library2->branchcode } )->count, 1, + 'Weekly closure copied' + ); + is( + Koha::Library::Calendar::RepeatingClosures->search( { library_id => $library2->branchcode } )->count, 1, + 'Repeating closure copied' + ); + is( + Koha::Library::Calendar::SingleClosures->search( { library_id => $library2->branchcode } )->count, 1, + 'Only the future single closure copied (past single excluded)' + ); + is( + Koha::Library::Calendar::Exceptions->search( { library_id => $library2->branchcode } )->count, 1, + 'Only the future exception copied (past exception excluded)' + ); + + # copy_to should not duplicate on a second run + $calendar->copy_to( $library2->branchcode ); + is( + Koha::Library::Calendar::WeeklyClosures->search( { library_id => $library2->branchcode } )->count, 1, + 'No duplicate weekly after second copy' + ); + is( + Koha::Library::Calendar::RepeatingClosures->search( { library_id => $library2->branchcode } )->count, 1, + 'No duplicate repeating after second copy' + ); + is( + Koha::Library::Calendar::SingleClosures->search( { library_id => $library2->branchcode } )->count, 1, + 'No duplicate single after second copy' + ); + is( + Koha::Library::Calendar::Exceptions->search( { library_id => $library2->branchcode } )->count, 1, + 'No duplicate exception after second copy' + ); + + $schema->storage->txn_rollback; +};