Bugzilla – Attachment 192136 Details for
Bug 41700
Checkouts note_date has incorrect format in swagger definitions
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 41700: Unit test
Bug-41700-Unit-test.patch (text/plain), 18.40 KB, created by
Lucas Gass (lukeg)
on 2026-01-28 21:07:10 UTC
(
hide
)
Description:
Bug 41700: Unit test
Filename:
MIME Type:
Creator:
Lucas Gass (lukeg)
Created:
2026-01-28 21:07:10 UTC
Size:
18.40 KB
patch
obsolete
>From a6db15b52a70d2ad7024a1ad8e56bb792574af7a Mon Sep 17 00:00:00 2001 >From: Nick Clemens <nick@bywatersolutions.com> >Date: Fri, 23 Jan 2026 19:55:11 +0000 >Subject: [PATCH] Bug 41700: Unit test >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >Signed-off-by: Nick Clemens <nick@bywatersolutions.com> >Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io> >--- > t/db_dependent/api/v1/checkouts.t | 192 ++++++++++++++++++++---------- > 1 file changed, 132 insertions(+), 60 deletions(-) > >diff --git a/t/db_dependent/api/v1/checkouts.t b/t/db_dependent/api/v1/checkouts.t >index 7a00bd50ed6..a0d77e1d747 100755 >--- a/t/db_dependent/api/v1/checkouts.t >+++ b/t/db_dependent/api/v1/checkouts.t >@@ -18,7 +18,7 @@ > use Modern::Perl; > > use Test::NoWarnings; >-use Test::More tests => 109; >+use Test::More tests => 111; > use Test::MockModule; > use Test::Mojo; > use t::lib::Mocks; >@@ -91,7 +91,8 @@ $bookings_librarian->set_password( { password => $password, skip_validation => 1 > my $bookings_userid = $bookings_librarian->userid; > > $t->get_ok("//$bookings_userid:$password@/api/v1/checkouts?patron_id=$patron_id") >- ->status_is( 200, 'manage_bookings allows checkouts access' )->json_is( [] ); >+ ->status_is( 200, 'manage_bookings allows checkouts access' ) >+ ->json_is( [] ); > > Koha::CirculationRules->set_rules( > { >@@ -121,26 +122,39 @@ my $date_due3 = Koha::DateUtils::dt_from_string( $issue3->date_due ); > my $issue4 = C4::Circulation::AddIssue( $patron, $item4->barcode ); > C4::Circulation::AddReturn( $item4->barcode, $branchcode ); > >-$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id")->status_is(200) >- ->json_is( '/0/patron_id' => $patron_id )->json_is( '/0/item_id' => $item1->itemnumber ) >+$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id") >+ ->status_is(200) >+ ->json_is( '/0/patron_id' => $patron_id ) >+ ->json_is( '/0/item_id' => $item1->itemnumber ) > ->json_is( '/0/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) ) >- ->json_is( '/1/patron_id' => $patron_id )->json_is( '/1/item_id' => $item2->itemnumber ) >- ->json_is( '/1/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) )->json_hasnt('/2'); >+ ->json_is( '/1/patron_id' => $patron_id ) >+ ->json_is( '/1/item_id' => $item2->itemnumber ) >+ ->json_is( '/1/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) ) >+ ->json_hasnt('/2'); > > # Test checked_in parameter, zero means, the response is same as without it >-$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&checked_in=0")->status_is(200) >- ->json_is( '/0/patron_id' => $patron_id )->json_is( '/0/item_id' => $item1->itemnumber ) >+$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&checked_in=0") >+ ->status_is(200) >+ ->json_is( '/0/patron_id' => $patron_id ) >+ ->json_is( '/0/item_id' => $item1->itemnumber ) > ->json_is( '/0/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) ) >- ->json_is( '/1/patron_id' => $patron_id )->json_is( '/1/item_id' => $item2->itemnumber ) >- ->json_is( '/1/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) )->json_hasnt('/2'); >+ ->json_is( '/1/patron_id' => $patron_id ) >+ ->json_is( '/1/item_id' => $item2->itemnumber ) >+ ->json_is( '/1/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) ) >+ ->json_hasnt('/2'); > > # Test checked_in parameter, one measn, the checked in checkout is in the response too >-$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&checked_in=1")->status_is(200) >- ->json_is( '/0/patron_id' => $patron_id )->json_is( '/0/item_id' => $item4->itemnumber )->json_hasnt('/1'); >+$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&checked_in=1") >+ ->status_is(200) >+ ->json_is( '/0/patron_id' => $patron_id ) >+ ->json_is( '/0/item_id' => $item4->itemnumber ) >+ ->json_hasnt('/1'); > > $item4->delete; >-$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&checked_in=1")->status_is(200) >- ->json_is( '/0/patron_id' => $patron_id )->json_is( '/0/item_id' => undef ); >+$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&checked_in=1") >+ ->status_is(200) >+ ->json_is( '/0/patron_id' => $patron_id ) >+ ->json_is( '/0/item_id' => undef ); > > $t->get_ok( "//$unauth_userid:$unauth_password@/api/v1/checkouts/" . $issue3->issue_id )->status_is(403)->json_is( > { >@@ -149,49 +163,72 @@ $t->get_ok( "//$unauth_userid:$unauth_password@/api/v1/checkouts/" . $issue3->is > } > ); > >-$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id")->status_is(200) >- ->json_is( '/0/patron_id' => $patron_id )->json_is( '/0/item_id' => $item1->itemnumber ) >+$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id") >+ ->status_is(200) >+ ->json_is( '/0/patron_id' => $patron_id ) >+ ->json_is( '/0/item_id' => $item1->itemnumber ) > ->json_is( '/0/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) ) >- ->json_is( '/1/patron_id' => $patron_id )->json_is( '/1/item_id' => $item2->itemnumber ) >- ->json_is( '/1/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) )->json_hasnt('/2'); >- >-$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&_per_page=1&_page=1")->status_is(200) >- ->header_is( 'X-Total-Count', '2' )->header_like( 'Link', qr|rel="next"| )->header_like( 'Link', qr|rel="first"| ) >- ->header_like( 'Link', qr|rel="last"| )->json_is( '/0/patron_id' => $patron_id ) >- ->json_is( '/0/item_id' => $item1->itemnumber ) >- ->json_is( '/0/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) )->json_hasnt('/1'); >- >-$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&_per_page=1&_page=2")->status_is(200) >- ->header_is( 'X-Total-Count', '2' )->header_like( 'Link', qr|rel="prev"| )->header_like( 'Link', qr|rel="first"| ) >- ->header_like( 'Link', qr|rel="last"| )->json_is( '/0/patron_id' => $patron_id ) >- ->json_is( '/0/item_id' => $item2->itemnumber ) >- ->json_is( '/0/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) )->json_hasnt('/1'); >- >-$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id )->status_is(200) >- ->json_is( '/patron_id' => $patron_id )->json_is( '/item_id' => $item1->itemnumber ) >- ->json_is( '/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) )->json_hasnt('/1'); >- >-$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id )->status_is(200) >+ ->json_is( '/1/patron_id' => $patron_id ) >+ ->json_is( '/1/item_id' => $item2->itemnumber ) >+ ->json_is( '/1/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) ) >+ ->json_hasnt('/2'); >+ >+$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&_per_page=1&_page=1") >+ ->status_is(200) >+ ->header_is( 'X-Total-Count', '2' ) >+ ->header_like( 'Link', qr|rel="next"| ) >+ ->header_like( 'Link', qr|rel="first"| ) >+ ->header_like( 'Link', qr|rel="last"| ) >+ ->json_is( '/0/patron_id' => $patron_id ) >+ ->json_is( '/0/item_id' => $item1->itemnumber ) >+ ->json_is( '/0/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) ) >+ ->json_hasnt('/1'); >+ >+$t->get_ok("//$userid:$password@/api/v1/checkouts?patron_id=$patron_id&_per_page=1&_page=2") >+ ->status_is(200) >+ ->header_is( 'X-Total-Count', '2' ) >+ ->header_like( 'Link', qr|rel="prev"| ) >+ ->header_like( 'Link', qr|rel="first"| ) >+ ->header_like( 'Link', qr|rel="last"| ) >+ ->json_is( '/0/patron_id' => $patron_id ) >+ ->json_is( '/0/item_id' => $item2->itemnumber ) >+ ->json_is( '/0/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) ) >+ ->json_hasnt('/1'); >+ >+$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id ) >+ ->status_is(200) >+ ->json_is( '/patron_id' => $patron_id ) >+ ->json_is( '/item_id' => $item1->itemnumber ) >+ ->json_is( '/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) ) >+ ->json_hasnt('/1'); >+ >+$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id ) >+ ->status_is(200) > ->json_is( '/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due1 } ) ); > >-$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id )->status_is(200) >+$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id ) >+ ->status_is(200) > ->json_is( '/due_date' => output_pref( { dateformat => "rfc3339", dt => $date_due2 } ) ); > > my $expected_datedue = > $date_due->set_time_zone('local')->add( days => 7 )->set( hour => 23, minute => 59, second => 0 ); > >-$t->post_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id . "/renewal" )->status_is(201) >+$t->post_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id . "/renewal" ) >+ ->status_is(201) > ->json_is( '/due_date' => output_pref( { dateformat => "rfc3339", dt => $expected_datedue } ) ) > ->header_is( Location => "/api/v1/checkouts/" . $issue1->issue_id . "/renewal" ); > > my $renewal = $issue1->renewals->last; > is( $renewal->renewal_type, 'Manual', 'Manual renewal recorded' ); > >-$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id . "/renewals" )->status_is(200) >- ->json_is( '/0/checkout_id' => $issue1->issue_id )->json_is( '/0/interface' => 'api' ) >+$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id . "/renewals" ) >+ ->status_is(200) >+ ->json_is( '/0/checkout_id' => $issue1->issue_id ) >+ ->json_is( '/0/interface' => 'api' ) > ->json_is( '/0/renewer_id' => $librarian->borrowernumber ); > >-$t->post_ok( "//$unauth_userid:$unauth_password@/api/v1/checkouts/" . $issue3->issue_id . "/renewal" )->status_is(403) >+$t->post_ok( "//$unauth_userid:$unauth_password@/api/v1/checkouts/" . $issue3->issue_id . "/renewal" ) >+ ->status_is(403) > ->json_is( > { > error => "Authorization failure. Missing required permission(s).", >@@ -199,7 +236,8 @@ $t->post_ok( "//$unauth_userid:$unauth_password@/api/v1/checkouts/" . $issue3->i > } > ); > >-$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/allows_renewal" )->status_is(200) >+$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/allows_renewal" ) >+ ->status_is(200) > ->json_is( > { > allows_renewal => Mojo::JSON->true, >@@ -210,14 +248,17 @@ $t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/all > } > ); > >-$t->post_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/renewal" )->status_is(201) >+$t->post_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/renewal" ) >+ ->status_is(201) > ->json_is( '/due_date' => output_pref( { dateformat => "rfc3339", dt => $expected_datedue } ) ) > ->header_is( Location => "/api/v1/checkouts/" . $issue2->issue_id . "/renewal" ); > >-$t->post_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id . "/renewal" )->status_is(403) >+$t->post_ok( "//$userid:$password@/api/v1/checkouts/" . $issue1->issue_id . "/renewal" ) >+ ->status_is(403) > ->json_is( { error => 'Renewal not authorized (too_many)' } ); > >-$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/allows_renewal" )->status_is(200) >+$t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/allows_renewal" ) >+ ->status_is(200) > ->json_is( > { > allows_renewal => Mojo::JSON->false, >@@ -228,6 +269,10 @@ $t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/all > } > ); > >+#Confirm we can get a checkout with a note >+$issue1->note("Test")->notedate( dt_from_string() )->store; >+$t->get_ok("//$userid:$password@/api/v1/checkouts")->status_is(200); >+ > $schema->storage->txn_rollback; > > subtest 'get_availability' => sub { >@@ -275,7 +320,8 @@ subtest 'get_availability' => sub { > > $t->get_ok( > "//$unauth_userid:$unauth_password@/api/v1/checkouts/availability?item_id=$item1_id&patron_id=$patron_id") >- ->status_is(403)->json_is( >+ ->status_is(403) >+ ->json_is( > { > error => "Authorization failure. Missing required permission(s).", > required_permissions => { circulate => "circulate_remaining_permissions" } >@@ -284,21 +330,29 @@ subtest 'get_availability' => sub { > > # Available > $t->get_ok("//$userid:$password@/api/v1/checkouts/availability?item_id=$item1_id&patron_id=$patron_id") >- ->status_is(200)->json_is( '/blockers' => {} )->json_is( '/confirms' => {} )->json_is( '/warnings' => {} ) >+ ->status_is(200) >+ ->json_is( '/blockers' => {} ) >+ ->json_is( '/confirms' => {} ) >+ ->json_is( '/warnings' => {} ) > ->json_has('/confirmation_token'); > > # Blocked > %issuingimpossible = ( GNA => 1 ); > $t->get_ok("//$userid:$password@/api/v1/checkouts/availability?item_id=$item1_id&patron_id=$patron_id") >- ->status_is(200)->json_is( '/blockers' => { GNA => 1 } )->json_is( '/confirms' => {} ) >- ->json_is( '/warnings' => {} )->json_has('/confirmation_token'); >+ ->status_is(200) >+ ->json_is( '/blockers' => { GNA => 1 } ) >+ ->json_is( '/confirms' => {} ) >+ ->json_is( '/warnings' => {} ) >+ ->json_has('/confirmation_token'); > %issuingimpossible = (); > > # Warnings/Info > %alerts = ( alert1 => "this is an alert" ); > %messages = ( message1 => "this is a message" ); > $t->get_ok("//$userid:$password@/api/v1/checkouts/availability?item_id=$item1_id&patron_id=$patron_id") >- ->status_is(200)->json_is( '/blockers' => {} )->json_is( '/confirms' => {} ) >+ ->status_is(200) >+ ->json_is( '/blockers' => {} ) >+ ->json_is( '/confirms' => {} ) > ->json_is( '/warnings' => { alert1 => "this is an alert", message1 => "this is a message" } ) > ->json_has('/confirmation_token'); > %alerts = (); >@@ -308,8 +362,10 @@ subtest 'get_availability' => sub { > %needsconfirmation = ( confirm1 => 1, confirm2 => 'please' ); > my $token = Koha::Token->new->generate_jwt( { id => $librarian->id . ":" . $item1_id . ":confirm1:confirm2" } ); > $t->get_ok("//$userid:$password@/api/v1/checkouts/availability?item_id=$item1_id&patron_id=$patron_id") >- ->status_is(200)->json_is( '/blockers' => {} ) >- ->json_is( '/confirms' => { confirm1 => 1, confirm2 => 'please' } )->json_is( '/warnings' => {} ) >+ ->status_is(200) >+ ->json_is( '/blockers' => {} ) >+ ->json_is( '/confirms' => { confirm1 => 1, confirm2 => 'please' } ) >+ ->json_is( '/warnings' => {} ) > ->json_has('/confirmation_token'); > my $confirmation_token = $t->tx->res->json('/confirmation_token'); > ok( >@@ -336,15 +392,23 @@ subtest 'get_availability' => sub { > # All ok > $t->get_ok( > "//$unauth_userid:$unauth_password@/api/v1/public/checkouts/availability?item_id=$item1_id&patron_id=$patron_id" >- )->status_is(200)->json_is( '/blockers' => {} )->json_is( '/confirms' => {} )->json_is( '/warnings' => {} ) >+ ) >+ ->status_is(200) >+ ->json_is( '/blockers' => {} ) >+ ->json_is( '/confirms' => {} ) >+ ->json_is( '/warnings' => {} ) > ->json_has('/confirmation_token'); > > # Needs confirmation upgrade to blocker > %needsconfirmation = ( TOO_MANY => 1, ISSUED_TO_ANOTHER => 1 ); > $t->get_ok( > "//$unauth_userid:$unauth_password@/api/v1/public/checkouts/availability?item_id=$item1_id&patron_id=$patron_id" >- )->status_is(200)->json_is( '/blockers' => { TOO_MANY => 1, ISSUED_TO_ANOTHER => 1 } ) >- ->json_is( '/confirms' => {} )->json_is( '/warnings' => {} )->json_has('/confirmation_token'); >+ ) >+ ->status_is(200) >+ ->json_is( '/blockers' => { TOO_MANY => 1, ISSUED_TO_ANOTHER => 1 } ) >+ ->json_is( '/confirms' => {} ) >+ ->json_is( '/warnings' => {} ) >+ ->json_has('/confirmation_token'); > %needsconfirmation = (); > > # Remove personal information from public endpoint >@@ -393,7 +457,11 @@ subtest 'get_availability' => sub { > ); > $t->get_ok( > "//$unauth_userid:$unauth_password@/api/v1/public/checkouts/availability?item_id=$item1_id&patron_id=$patron_id" >- )->status_is(200)->json_is( '/blockers' => {} )->json_is( '/confirms' => {} )->json_is( '/warnings' => {} ) >+ ) >+ ->status_is(200) >+ ->json_is( '/blockers' => {} ) >+ ->json_is( '/confirms' => {} ) >+ ->json_is( '/warnings' => {} ) > ->json_has('/confirmation_token'); > %issuingimpossible = (); > %alerts = (); >@@ -504,10 +572,12 @@ subtest 'add checkout' => sub { > > $t->post_ok( > "/api/v1/public/patrons/$patron_id/checkouts" => json => { item_id => $item1_id, patron_id => $patron_id } ) >- ->status_is(401)->json_is( { error => "Authentication failure." } ); >+ ->status_is(401) >+ ->json_is( { error => "Authentication failure." } ); > > $t->post_ok( "//$useridp:$password@/api/v1/public/patrons/$patron_id/checkouts" => json => >- { item_id => $item1_id, patron_id => $patron_id } )->status_is(405) >+ { item_id => $item1_id, patron_id => $patron_id } ) >+ ->status_is(405) > ->json_is( { error => "Feature disabled", error_code => "FEATURE_DISABLED" } ); > > # Feature enabled >@@ -515,10 +585,12 @@ subtest 'add checkout' => sub { > > $t->post_ok( > "/api/v1/public/patrons/$patron_id/checkouts" => json => { item_id => $item1_id, patron_id => $patron_id } ) >- ->status_is(401)->json_is( { error => "Authentication failure." } ); >+ ->status_is(401) >+ ->json_is( { error => "Authentication failure." } ); > > $t->post_ok( "//$userid:$password@/api/v1/public/patrons/$patron_id/checkouts" => json => >- { item_id => $item1_id, patron_id => $patron_id } )->status_is(403) >+ { item_id => $item1_id, patron_id => $patron_id } ) >+ ->status_is(403) > ->json_is( { error => "Unprivileged user cannot access another user's resources" } ); > > $t->post_ok( "//$useridp:$password@/api/v1/public/patrons/$patron_id/checkouts" => json => >-- >2.39.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 41700
:
191985
|
191986
|
191987
|
191988
|
192029
|
192030
|
192135
| 192136