@@ -, +, @@ --- t/db_dependent/api/v1/checkouts.t | 147 ++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 20 deletions(-) --- a/t/db_dependent/api/v1/checkouts.t +++ a/t/db_dependent/api/v1/checkouts.t @@ -225,28 +225,135 @@ $t->get_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id . "/all error => 'too_many' }); -my $new_date_due = Koha::DateUtils::dt_from_string( $issue2->date_due ); -$new_date_due->add(days => 2); -$new_date_due = output_pref({ dateformat => "rfc3339", dt => $new_date_due }); - -$t->put_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id => json => { due_date => $new_date_due }) - ->status_is(200, 'Due date updated successfully') - ->json_is('/due_date' => $new_date_due); - -# Test for date handling for client differs from server locale -$new_date_due = $date_due->clone->set_time_zone( 'Australia/Sydney' ); -$new_date_due->add(days => 2); -my $offset_rfc3339 = $new_date_due . get_offset_string($new_date_due->offset); -my $offset_servertime = output_pref({ dateformat => "rfc3339", dt => $new_date_due }); -$t->put_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id => json => { due_date => $offset_rfc3339}) - ->status_is(200, 'Due date updated successfully') - ->json_is('/due_date' => $offset_servertime); - -$t->put_ok( "//$userid:$password@/api/v1/checkouts/" . $issue2->issue_id => json => { item_id => 3 }) - ->status_is(400, 'readOnly properties not updateable'); - $schema->storage->txn_rollback; +subtest 'update() tests' => sub { + + plan tests => 2; + + $schema->storage->txn_begin; + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2 } + } + ); + my $password = 'thePassword123'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + my $issue = $builder->build_object({ class => 'Koha::Checkouts' } ); + my $issue_id = $issue->id; + + # Unauthorized attempt to update + $t->put_ok( + "//$unauth_userid:$password@/api/v1/checkouts/$issue_id" => json => + { borrowernumber => $issue->borrowernumber } )->status_is(403); + + # FIXME: The tests for partial update are inspired by the same for /cities.. + # but they do not account for the readOnly nature of fields defined on this route. + + # Attempt partial update on a PUT + my $checkout_with_missing_field = { + borrowernumber => $issue->borrowernumber, + itemnumber => $issue->itemnumber, + library_id => $issue->branchcode, + checkin_date => undef, + last_renewed_date => undef, + checkout_date => undef, + note_date => undef, + note_seen => undef + }; + + $t->put_ok( "//$userid:$password@/api/v1/checkouts/$issue_id" => json => $checkout_with_missing_field ) + ->status_is(400) + ->json_is( "/errors" => + [ { message => "Missing property.", path => "/body/due_date" } ] + ); + + # Full object update on PUT + my $checkout_with_updated_field = { + borrowernumber => $issue->borrowernumber, + itemnumber => $issue->itemnumber, + library_id => $issue->branchcode, + due_date => $issue->date_due, + checkin_date => $issue->returndate, + last_renewed_date => $issue->lastreneweddate, + checkout_date => $issue->issuedate, + note_date => $issue->notedate, + note_seen => $issue->noteseen + }; + + $t->put_ok( "//$userid:$password@/api/v1/checkouts/$issue_id" => json => $checkout_with_updated_field ) + ->status_is(200) + ->json_is( '/name' => 'London' ); + + # Authorized attempt to write invalid data + my $checkout_with_invalid_field = { + blah => "Checkout Blah", + state => "Checkout State", + postal_code => "Checkout Zipcode", + country => "Checkout Country" + }; + + $t->put_ok( "//$userid:$password@/api/v1/checkouts/$issue_id" => json => $checkout_with_invalid_field ) + ->status_is(400) + ->json_is( + "/errors" => [ + { + message => "Properties not allowed: blah.", + path => "/body" + } + ] + ); + + my $checkout_to_delete = $builder->build_object({ class => 'Koha::Checkouts' }); + my $non_existent_id = $checkout_to_delete->id; + $checkout_to_delete->delete; + + $t->put_ok( "//$userid:$password@/api/v1/checkouts/$non_existent_id" => json => $checkout_with_updated_field ) + ->status_is(404); + + my $new_date_due = Koha::DateUtils::dt_from_string( $issue->date_due ); + $new_date_due->add( days => 2 ); + $new_date_due = + output_pref( { dateformat => "rfc3339", dt => $new_date_due } ); + + $t->put_ok( "//$userid:$password@/api/v1/checkouts/" + . $issue->issue_id => json => { due_date => $new_date_due } ) + ->status_is( 200, 'Due date updated successfully' ) + ->json_is( '/due_date' => $new_date_due ); + + # Test for date handling for client differs from server locale + $new_date_due = $date_due->clone->set_time_zone('Australia/Sydney'); + $new_date_due->add( days => 2 ); + my $offset_rfc3339 = + $new_date_due . get_offset_string( $new_date_due->offset ); + my $offset_servertime = + output_pref( { dateformat => "rfc3339", dt => $new_date_due } ); + $t->put_ok( "//$userid:$password@/api/v1/checkouts/" + . $issue->issue_id => json => { due_date => $offset_rfc3339 } ) + ->status_is( 200, 'Due date updated successfully' ) + ->json_is( '/due_date' => $offset_servertime ); + + $t->put_ok( "//$userid:$password@/api/v1/checkouts/" + . $issue->issue_id => json => { item_id => 3 } ) + ->status_is( 400, 'readOnly properties not updateable' ); + + $schema->storage->txn_rollback; +}; + # Borrowed from 2020 release of DateTime sub get_offset_string { my $offset = shift; --