Bugzilla – Attachment 193424 Details for
Bug 38924
Introduce an organization level loan 'Quota' system for Koha
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 38924: Add tests for quotas object classes and api endpoints
Bug-38924-Add-tests-for-quotas-object-classes-and-.patch (text/plain), 29.87 KB, created by
Jacob O'Mara
on 2026-02-19 12:11:29 UTC
(
hide
)
Description:
Bug 38924: Add tests for quotas object classes and api endpoints
Filename:
MIME Type:
Creator:
Jacob O'Mara
Created:
2026-02-19 12:11:29 UTC
Size:
29.87 KB
patch
obsolete
>From 2fc98802f58ad941cab31b2faad6af6c10570645 Mon Sep 17 00:00:00 2001 >From: Jacob O'Mara <Jacob.omara@openfifth.co.uk> >Date: Fri, 7 Nov 2025 12:25:28 +0000 >Subject: [PATCH] Bug 38924: Add tests for quotas object classes and api > endpoints > >--- > t/db_dependent/Koha/Patron/Quotas.t | 416 ++++++++++++++++++++ > t/db_dependent/api/v1/patrons_quotas.t | 520 +++++++++++++++++++++++++ > 2 files changed, 936 insertions(+) > create mode 100755 t/db_dependent/Koha/Patron/Quotas.t > create mode 100755 t/db_dependent/api/v1/patrons_quotas.t > >diff --git a/t/db_dependent/Koha/Patron/Quotas.t b/t/db_dependent/Koha/Patron/Quotas.t >new file mode 100755 >index 00000000000..8ebf1f65e2a >--- /dev/null >+++ b/t/db_dependent/Koha/Patron/Quotas.t >@@ -0,0 +1,416 @@ >+#!/usr/bin/perl >+ >+use Modern::Perl; >+ >+use Test::More tests => 10; >+use Test::Exception; >+use Test::NoWarnings; >+ >+use t::lib::TestBuilder; >+ >+use Koha::Database; >+use Koha::Patron::Quotas; >+use Koha::DateUtils qw( dt_from_string ); >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+subtest 'basic CRUD operations' => sub { >+ plan tests => 5; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ my $quota = Koha::Patron::Quota->new( >+ { >+ patron_id => $patron->id, >+ description => 'Test quota', >+ start_date => '2025-01-01', >+ end_date => '2025-12-31', >+ allocation => 10, >+ used => 0 >+ } >+ )->store; >+ >+ ok( $quota->id, 'Quota created with auto-increment ID' ); >+ is( $quota->allocation, 10, 'Allocation set correctly' ); >+ is( $quota->used, 0, 'Used initialized to 0' ); >+ >+ my $retrieved = Koha::Patron::Quotas->find( $quota->id ); >+ is( $retrieved->id, $quota->id, 'Quota retrieved successfully' ); >+ >+ $quota->delete; >+ is( Koha::Patron::Quotas->search( { id => $quota->id } )->count, 0, 'Quota deleted successfully' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'filter_by_active() method' => sub { >+ plan tests => 3; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ my $past_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ start_date => '2020-01-01', >+ end_date => '2020-12-31' >+ } >+ } >+ ); >+ >+ my $current_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ start_date => dt_from_string()->ymd, >+ end_date => dt_from_string()->add( days => 30 )->ymd >+ } >+ } >+ ); >+ >+ my $future_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ start_date => dt_from_string()->add( days => 365 )->ymd, >+ end_date => dt_from_string()->add( days => 730 )->ymd >+ } >+ } >+ ); >+ >+ my $all_quotas = Koha::Patron::Quotas->search( { patron_id => $patron->id } ); >+ is( $all_quotas->count, 3, 'All quotas present' ); >+ >+ my $active_quotas = $all_quotas->filter_by_active; >+ is( $active_quotas->count, 1, 'Only one active quota' ); >+ is( $active_quotas->next->id, $current_quota->id, 'Correct quota is active' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'has_available_quota() method' => sub { >+ plan tests => 3; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ my $quota_with_available = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ allocation => 10, >+ used => 5 >+ } >+ } >+ ); >+ >+ ok( $quota_with_available->has_available_quota, 'Quota has available allocation' ); >+ >+ my $quota_fully_used = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ allocation => 10, >+ used => 10 >+ } >+ } >+ ); >+ >+ ok( !$quota_fully_used->has_available_quota, 'Fully used quota has no available allocation' ); >+ >+ my $quota_exceeded = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ allocation => 10, >+ used => 15 >+ } >+ } >+ ); >+ >+ ok( !$quota_exceeded->has_available_quota, 'Exceeded quota has no available allocation' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'available_quota() method' => sub { >+ plan tests => 3; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ allocation => 10, >+ used => 3 >+ } >+ } >+ ); >+ >+ is( $quota->available_quota, 7, 'Available quota calculated correctly' ); >+ >+ $quota->used(10)->store; >+ is( $quota->available_quota, 0, 'No available quota when fully used' ); >+ >+ $quota->used(12)->store; >+ is( $quota->available_quota, -2, 'Negative available quota when exceeded' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'is_active() method' => sub { >+ plan tests => 3; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ my $past_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ start_date => '2020-01-01', >+ end_date => '2020-12-31' >+ } >+ } >+ ); >+ >+ ok( !$past_quota->is_active, 'Past quota is not active' ); >+ >+ my $current_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ start_date => dt_from_string()->ymd, >+ end_date => dt_from_string()->add( days => 30 )->ymd >+ } >+ } >+ ); >+ >+ ok( $current_quota->is_active, 'Current quota is active' ); >+ >+ my $future_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ start_date => dt_from_string()->add( days => 365 )->ymd, >+ end_date => dt_from_string()->add( days => 730 )->ymd >+ } >+ } >+ ); >+ >+ ok( !$future_quota->is_active, 'Future quota is not active' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add_to_quota() method' => sub { >+ plan tests => 3; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ allocation => 10, >+ used => 3 >+ } >+ } >+ ); >+ >+ is( $quota->used, 3, 'Initial used value is 3' ); >+ >+ $quota->add_to_quota(2); >+ is( $quota->used, 5, 'Used incremented by 2' ); >+ >+ $quota->add_to_quota(7); >+ is( $quota->used, 12, 'Used can exceed allocation' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add_usage() method' => sub { >+ plan tests => 4; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $item = $builder->build_sample_item; >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ allocation => 10, >+ used => 0 >+ } >+ } >+ ); >+ >+ my $checkout = $builder->build_object( >+ { >+ class => 'Koha::Checkouts', >+ value => { >+ borrowernumber => $patron->id, >+ itemnumber => $item->id >+ } >+ } >+ ); >+ >+ my $usage = $quota->add_usage( { issue_id => $checkout->issue_id, type => 'ISSUE', amount => 1 } ); >+ >+ ok( defined $usage, 'Usage created' ); >+ is( $usage->patron_quota_id, $quota->id, 'Usage linked to quota' ); >+ is( $usage->issue_id, $checkout->issue_id, 'Usage linked to checkout' ); >+ >+ $quota->discard_changes; >+ is( $quota->used, 1, 'Quota used incremented' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'period clash validation' => sub { >+ plan tests => 4; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ my $existing_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron->id, >+ start_date => '2025-01-01', >+ end_date => '2025-12-31' >+ } >+ } >+ ); >+ >+ throws_ok { >+ Koha::Patron::Quota->new( >+ { >+ patron_id => $patron->id, >+ description => 'Overlapping quota', >+ start_date => '2025-06-01', >+ end_date => '2025-12-31', >+ allocation => 5, >+ used => 0 >+ } >+ )->store; >+ } >+ 'Koha::Exceptions::Quota::Clash', 'Overlapping period throws clash exception'; >+ >+ throws_ok { >+ Koha::Patron::Quota->new( >+ { >+ patron_id => $patron->id, >+ description => 'Same start date quota', >+ start_date => '2025-01-01', >+ end_date => '2025-06-30', >+ allocation => 5, >+ used => 0 >+ } >+ )->store; >+ } >+ 'Koha::Exceptions::Quota::Clash', 'Period starting same day throws clash exception'; >+ >+ my $non_overlapping_past = Koha::Patron::Quota->new( >+ { >+ patron_id => $patron->id, >+ description => 'Past quota', >+ start_date => '2024-01-01', >+ end_date => '2024-12-31', >+ allocation => 5, >+ used => 0 >+ } >+ )->store; >+ >+ ok( $non_overlapping_past->id, 'Non-overlapping past quota created successfully' ); >+ >+ my $non_overlapping_future = Koha::Patron::Quota->new( >+ { >+ patron_id => $patron->id, >+ description => 'Future quota', >+ start_date => '2026-01-01', >+ end_date => '2026-12-31', >+ allocation => 5, >+ used => 0 >+ } >+ )->store; >+ >+ ok( $non_overlapping_future->id, 'Non-overlapping future quota created successfully' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'relations' => sub { >+ plan tests => 4; >+ >+ $schema->storage->txn_begin; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $item = $builder->build_sample_item; >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { patron_id => $patron->id } >+ } >+ ); >+ >+ isa_ok( $quota->patron, 'Koha::Patron', 'Patron relation works' ); >+ is( $quota->patron->id, $patron->id, 'Correct patron returned' ); >+ >+ my $checkout = $builder->build_object( >+ { >+ class => 'Koha::Checkouts', >+ value => { >+ borrowernumber => $patron->id, >+ itemnumber => $item->id >+ } >+ } >+ ); >+ >+ my $usage = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quota::Usages', >+ value => { >+ patron_quota_id => $quota->id, >+ patron_id => $patron->id, >+ issue_id => $checkout->issue_id, >+ type => 'ISSUE' >+ } >+ } >+ ); >+ >+ my $usages = $quota->usages; >+ isa_ok( $usages, 'Koha::Patron::Quota::Usages', 'Usages relation works' ); >+ is( $usages->count, 1, 'Correct number of usages returned' ); >+ >+ $schema->storage->txn_rollback; >+}; >diff --git a/t/db_dependent/api/v1/patrons_quotas.t b/t/db_dependent/api/v1/patrons_quotas.t >new file mode 100755 >index 00000000000..7ea2de1c9d6 >--- /dev/null >+++ b/t/db_dependent/api/v1/patrons_quotas.t >@@ -0,0 +1,520 @@ >+#!/usr/bin/env perl >+ >+use Modern::Perl; >+ >+use Test::More tests => 8; >+use Test::Mojo; >+use Test::NoWarnings; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use Koha::Patron::Quotas; >+use Koha::Database; >+use Koha::DateUtils qw( dt_from_string ); >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+my $t = Test::Mojo->new('Koha::REST::V1'); >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+ >+subtest 'list() tests' => sub { >+ >+ plan tests => 14; >+ >+ $schema->storage->txn_begin; >+ >+ Koha::Patron::Quotas->search->delete; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } >+ } >+ ); >+ 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 } >+ } >+ ); >+ my $patron_password = 'thePassword000'; >+ $patron->set_password( { password => $patron_password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $patron_id = $patron->id; >+ >+ $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas")->status_is(200)->json_is( [] ); >+ >+ my $current_year_start = dt_from_string()->truncate( to => 'year' )->ymd; >+ my $current_year_end = dt_from_string()->truncate( to => 'year' )->add( years => 1 )->subtract( days => 1 )->ymd; >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron_id, >+ start_date => $current_year_start, >+ end_date => $current_year_end, >+ allocation => 10, >+ used => 0 >+ } >+ } >+ ); >+ >+ $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas")->status_is(200)->json_is( [ $quota->to_api ] ); >+ >+ my $another_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron_id, >+ start_date => '2020-01-01', >+ end_date => '2020-12-31', >+ allocation => 5, >+ used => 3 >+ } >+ } >+ ); >+ >+ $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas") >+ ->status_is(200) >+ ->json_is( [ $quota->to_api, $another_quota->to_api ] ); >+ >+ $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas?only_active=true") >+ ->status_is(200) >+ ->json_is( [ $quota->to_api ] ); >+ >+ $t->get_ok("//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas")->status_is(403); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } >+ } >+ ); >+ 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 } >+ } >+ ); >+ my $patron_password = 'thePassword000'; >+ $patron->set_password( { password => $patron_password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { patron_id => $patron->id } >+ } >+ ); >+ >+ $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $quota->patron_id . "/quotas/" . $quota->id ) >+ ->status_is(200) >+ ->json_is( $quota->to_api ); >+ >+ $t->get_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/" . $quota->patron_id . "/quotas/" . $quota->id ) >+ ->status_is(403); >+ >+ my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } ); >+ my $non_existent_id = $quota_to_delete->id; >+ $quota_to_delete->delete; >+ >+ $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $quota->patron_id . "/quotas/$non_existent_id" ) >+ ->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add() tests' => sub { >+ >+ plan tests => 18; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } >+ } >+ ); >+ 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 } >+ } >+ ); >+ my $patron_password = 'thePassword000'; >+ $patron->set_password( { password => $patron_password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $patron_id = $test_patron->id; >+ >+ my $current_year_start = dt_from_string()->truncate( to => 'year' )->ymd; >+ my $current_year_end = dt_from_string()->truncate( to => 'year' )->add( years => 1 )->subtract( days => 1 )->ymd; >+ my $next_year_start = dt_from_string()->truncate( to => 'year' )->add( years => 1 )->ymd; >+ my $next_year_end = dt_from_string()->truncate( to => 'year' )->add( years => 2 )->subtract( days => 1 )->ymd; >+ >+ my $quota_data = { >+ description => 'Test quota', >+ start_date => $current_year_start, >+ end_date => $current_year_end, >+ allocation => 10, >+ used => 0 >+ }; >+ >+ $t->post_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas" => json => $quota_data ) >+ ->status_is(403); >+ >+ $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $quota_data ) >+ ->status_is( 201, 'SWAGGER3.2.1' ) >+ ->json_is( '/start_date' => $current_year_start ) >+ ->json_is( '/end_date' => $current_year_end ) >+ ->json_is( '/allocation' => 10 ) >+ ->json_is( '/used' => 0 ) >+ ->header_like( Location => qr|^/api/v1/patrons/$patron_id/quotas/\d+|, 'SWAGGER3.4.1' ); >+ >+ my $quota_with_id = { >+ id => 2, >+ description => 'Test quota with ID', >+ start_date => $next_year_start, >+ end_date => $next_year_end, >+ allocation => 5, >+ used => 0 >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $quota_with_id ) >+ ->status_is(400) >+ ->json_has('/errors'); >+ >+ my $overlapping_start = dt_from_string()->truncate( to => 'year' )->add( months => 6 )->ymd; >+ >+ my $overlapping_quota = { >+ description => 'Overlapping quota', >+ start_date => $overlapping_start, >+ end_date => $current_year_end, >+ allocation => 5, >+ used => 0 >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $overlapping_quota ) >+ ->status_is(409) >+ ->json_is( '/error' => 'Quota period overlaps with existing quota' ); >+ >+ my $future_year_start = dt_from_string()->truncate( to => 'year' )->add( years => 2 )->ymd; >+ my $future_year_end = dt_from_string()->truncate( to => 'year' )->add( years => 3 )->subtract( days => 1 )->ymd; >+ >+ my $invalid_quota = { >+ description => 'Invalid quota', >+ start_date => $future_year_start, >+ end_date => $future_year_end, >+ allocation => 10, >+ used => 0, >+ invalid_prop => 'Invalid' >+ }; >+ >+ $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $invalid_quota ) >+ ->status_is(400) >+ ->json_has('/errors'); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'update() tests' => sub { >+ >+ plan tests => 16; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } >+ } >+ ); >+ 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 } >+ } >+ ); >+ my $patron_password = 'thePassword000'; >+ $patron->set_password( { password => $patron_password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $patron_id = $test_patron->id; >+ >+ my $current_year_start = dt_from_string()->truncate( to => 'year' )->ymd; >+ my $current_year_end = dt_from_string()->truncate( to => 'year' )->add( years => 1 )->subtract( days => 1 )->ymd; >+ my $overlapping_start = dt_from_string()->truncate( to => 'year' )->add( months => 6 )->ymd; >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron_id, >+ start_date => $current_year_start, >+ end_date => $current_year_end, >+ allocation => 10, >+ used => 0 >+ } >+ } >+ ); >+ >+ $t->put_ok( >+ "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => { >+ description => 'Updated quota', >+ start_date => $current_year_start, >+ end_date => $current_year_end, >+ allocation => 15, >+ used => 0 >+ } >+ )->status_is(403); >+ >+ my $updated_quota = $quota->to_api; >+ delete $updated_quota->{id}; # readOnly field >+ delete $updated_quota->{patron_id}; # readOnly field >+ $updated_quota->{allocation} = 15; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $updated_quota ) >+ ->status_is(200) >+ ->json_is( '/allocation' => 15 ); >+ >+ my $partial_update = { allocation => 20 }; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $partial_update ) >+ ->status_is(400) >+ ->json_has('/errors'); >+ >+ my $another_quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { >+ patron_id => $patron_id, >+ start_date => '2020-01-01', >+ end_date => '2020-12-31', >+ allocation => 5, >+ used => 0 >+ } >+ } >+ ); >+ >+ my $overlapping_update = $quota->to_api; >+ delete $overlapping_update->{id}; # readOnly field >+ delete $overlapping_update->{patron_id}; # readOnly field >+ $overlapping_update->{start_date} = '2020-06-01'; # Overlaps with another_quota >+ $overlapping_update->{end_date} = '2020-12-31'; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $overlapping_update ) >+ ->status_is(409) >+ ->json_is( '/error' => 'Quota period overlaps with existing quota' ); >+ >+ my $invalid_update = $quota->to_api; >+ delete $invalid_update->{id}; # readOnly field >+ delete $invalid_update->{patron_id}; # readOnly field >+ $invalid_update->{invalid_prop} = 'Invalid'; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $invalid_update ) >+ ->status_is(400) >+ ->json_has('/errors'); >+ >+ my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } ); >+ my $non_existent_id = $quota_to_delete->id; >+ $quota_to_delete->delete; >+ >+ $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/$non_existent_id" => json => $updated_quota ) >+ ->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'delete() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } >+ } >+ ); >+ 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 } >+ } >+ ); >+ my $patron_password = 'thePassword000'; >+ $patron->set_password( { password => $patron_password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $patron_id = $test_patron->id; >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { patron_id => $patron_id } >+ } >+ ); >+ >+ $t->delete_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas/" . $quota->id ) >+ ->status_is(403); >+ >+ $t->delete_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id ) >+ ->status_is( 204, 'SWAGGER3.2.4' ) >+ ->content_is( '', 'SWAGGER3.3.4' ); >+ >+ my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } ); >+ my $non_existent_id = $quota_to_delete->id; >+ $quota_to_delete->delete; >+ >+ $t->delete_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas/$non_existent_id")->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get_usage() tests' => sub { >+ >+ plan tests => 13; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } >+ } >+ ); >+ 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 } >+ } >+ ); >+ my $patron_password = 'thePassword000'; >+ $patron->set_password( { password => $patron_password, skip_validation => 1 } ); >+ my $unauth_userid = $patron->userid; >+ >+ my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $patron_id = $test_patron->id; >+ >+ my $quota = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quotas', >+ value => { patron_id => $patron_id } >+ } >+ ); >+ >+ $t->get_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" ) >+ ->status_is(200) >+ ->json_is( [] ); >+ >+ my $usage = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quota::Usages', >+ value => { >+ patron_quota_id => $quota->id, >+ patron_id => $patron_id, >+ issue_id => undef, >+ type => 'ISSUE' >+ } >+ } >+ ); >+ >+ $t->get_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" ) >+ ->status_is(200) >+ ->json_is( [ $usage->to_api ] ); >+ >+ my $another_usage = $builder->build_object( >+ { >+ class => 'Koha::Patron::Quota::Usages', >+ value => { >+ patron_quota_id => $quota->id, >+ patron_id => $patron_id, >+ issue_id => undef, >+ type => 'ISSUE' >+ } >+ } >+ ); >+ >+ $t->get_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" ) >+ ->status_is(200) >+ ->json_is( [ $usage->to_api, $another_usage->to_api ] ); >+ >+ $t->get_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" ) >+ ->status_is(403); >+ >+ my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } ); >+ my $non_existent_id = $quota_to_delete->id; >+ $quota_to_delete->delete; >+ >+ $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas/$non_existent_id/usages")->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'query parameter validation' => sub { >+ >+ plan tests => 3; >+ >+ $schema->storage->txn_begin; >+ >+ my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 2**4 } >+ } >+ ); >+ my $password = 'thePassword123'; >+ $librarian->set_password( { password => $password, skip_validation => 1 } ); >+ my $userid = $librarian->userid; >+ >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $patron_id = $patron->id; >+ >+ $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas?quota_blah=blah") >+ ->status_is(400) >+ ->json_is( [ { path => '/query/quota_blah', message => 'Malformed query string' } ] ); >+ >+ $schema->storage->txn_rollback; >+}; >-- >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 38924
:
178582
|
178583
|
178584
|
178585
|
178586
|
178587
|
178588
|
178589
|
178590
|
178591
|
178592
|
178593
|
178594
|
178595
|
178596
|
178597
|
178598
|
178599
|
178600
|
178601
|
178602
|
178603
|
178604
|
178605
|
178606
|
178607
|
178608
|
185631
|
185632
|
185633
|
185634
|
185635
|
185636
|
185637
|
185638
|
185639
|
185640
|
185641
|
185642
|
185643
|
185644
|
185645
|
185646
|
185647
|
185648
|
185649
|
185650
|
185651
|
185652
|
185653
|
185654
|
185655
|
185656
|
185657
|
185675
|
185676
|
185677
|
185678
|
185679
|
185680
|
185681
|
185682
|
185683
|
185684
|
185685
|
185686
|
185687
|
185688
|
185689
|
185690
|
185691
|
185692
|
185693
|
185694
|
185695
|
185696
|
185697
|
185698
|
185699
|
185700
|
185701
|
185702
|
189266
|
189267
|
189268
|
189269
|
189270
|
189271
|
189272
|
189273
|
189274
|
189275
|
189276
|
189277
|
189278
|
189279
|
189280
|
189281
|
189282
|
189283
|
189284
|
189285
|
189286
|
189287
|
189288
|
189289
|
189290
|
189291
|
189292
|
193377
|
193378
|
193379
|
193380
|
193381
|
193382
|
193383
|
193384
|
193385
|
193386
|
193387
|
193388
|
193389
|
193390
|
193391
|
193392
|
193393
|
193394
|
193395
|
193396
|
193397
|
193398
|
193399
|
193400
|
193401
|
193402
|
193403
|
193404
|
193405
|
193406
|
193407
|
193408
|
193409
|
193410
|
193411
|
193412
|
193413
|
193414
|
193415
|
193416
|
193417
|
193418
|
193419
|
193420
|
193421
|
193422
|
193423
|
193424
|
193425
|
193426
|
193427
|
193428
|
193429
|
193430
|
193431
|
193432
|
193433
|
193434
|
193435
|
193436
|
193437
|
193438
|
193439
|
193440
|
193441
|
193442
|
193443
|
193444
|
193445
|
193446
|
193447
|
193448
|
193449
|
193450
|
193451
|
193452
|
193453
|
193454
|
193455
|
193456
|
193457
|
193458
|
193459
|
193460
|
193547
|
193548
|
193549
|
193550
|
193551
|
193552
|
193553
|
193554
|
193555
|
193556
|
193557
|
193558
|
193559
|
193560
|
193561
|
193562
|
193563
|
193564
|
193565
|
193566
|
193567
|
193568
|
193569
|
193570
|
193571
|
193572
|
193573
|
193574
|
193575
|
193576
|
193577