Bug 42868: (follow-up) Add regression test for non-UTC library timezone

The bug this fixed only manifests when the library's configured
timezone (C4::Context->tz) is not UTC: Koha::Object::_recursive_fixup
converts an incoming RFC3339 instant to that timezone before storing
it in the timezone-naive 'bookings.start_date'/'end_date' columns, so
a UTC-anchored boundary shifts by the timezone offset on write.

None of the existing Cypress specs catch this, because they never run
with the browser/test-runner timezone diverging from the configured
library timezone - both always come from the same container clock.

Add a Perl-level API test that mocks C4::Context->tz to a non-UTC zone
(America/New_York) and asserts the boundaries sent by place_booking.js
round-trip to the correct library-local date, pinning this regression
independently of any particular environment's timezone.

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Martin Renvoize 2026-07-01 13:02:56 +01:00 committed by Pedro Amorim
parent 5a7c328475
commit 111637445a
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
1 changed files with 76 additions and 1 deletions

View File

@ -18,8 +18,9 @@
use Modern::Perl;
use Test::NoWarnings;
use Test::More tests => 8;
use Test::More tests => 9;
use Test::Mojo;
use Test::MockModule;
use Test::Warn;
use t::lib::TestBuilder;
@ -654,3 +655,77 @@ subtest 'add() with itemtype_id tests' => sub {
$schema->storage->txn_rollback;
};
subtest 'add() applies the library timezone to start_date/end_date (Bug 42868)' => sub {
plan tests => 4;
$schema->storage->txn_begin;
# Regression test for Bug 42868: the API converts the incoming RFC3339
# instant to C4::Context->tz before storing it in the timezone-naive
# 'datetime' column (see Koha::Object::_recursive_fixup), so a client
# must anchor day boundaries to the library's configured timezone, not
# UTC, or the stored date shifts whenever that timezone isn't UTC.
my $context = Test::MockModule->new('C4::Context');
$context->mock( 'tz', sub { 'America/New_York' } );
my $librarian = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 } # no additional permissions
}
);
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $librarian->borrowernumber,
module_bit => 1,
code => 'manage_bookings',
},
}
);
my $password = 'thePassword123';
$librarian->set_password( { password => $password, skip_validation => 1 } );
my $userid = $librarian->userid;
my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
my $biblio = $builder->build_sample_biblio;
my $item = $builder->build_sample_item( { bookable => 1, biblionumber => $biblio->id } );
my $pickup_library = $builder->build_object( { class => 'Koha::Libraries' } );
# 2026-07-01T00:00:00 and 2026-07-01T23:59:59 in America/New_York (EDT, UTC-4) -
# exactly what place_booking.js sends via dayjs.tz(date, $timezone()).
my $booking = {
biblio_id => $biblio->id,
item_id => $item->itemnumber,
pickup_library_id => $pickup_library->branchcode,
patron_id => $patron->id,
start_date => '2026-07-01T04:00:00Z',
end_date => '2026-07-02T03:59:59Z',
};
my $booking_id =
$t->post_ok( "//$userid:$password@/api/v1/bookings" => json => $booking )
->status_is(201)
->tx->res->json->{booking_id};
my ( $start_date, $end_date ) = $schema->storage->dbh->selectrow_array(
q{SELECT start_date, end_date FROM bookings WHERE booking_id = ?},
undef, $booking_id
);
is(
$start_date, '2026-07-01 00:00:00',
'start_date is stored as library-local midnight, not shifted by the UTC offset'
);
is(
$end_date, '2026-07-01 23:59:59',
'end_date is stored as library-local end of day, not shifted by the UTC offset'
);
$context->unmock('tz');
$schema->storage->txn_rollback;
};