View | Details | Raw Unified | Return to bug 38924
Collapse All | Expand All

(-)a/t/db_dependent/Koha/Patron/Quotas.t (+416 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
5
use Test::More tests => 10;
6
use Test::Exception;
7
use Test::NoWarnings;
8
9
use t::lib::TestBuilder;
10
11
use Koha::Database;
12
use Koha::Patron::Quotas;
13
use Koha::DateUtils qw( dt_from_string );
14
15
my $schema  = Koha::Database->new->schema;
16
my $builder = t::lib::TestBuilder->new;
17
18
subtest 'basic CRUD operations' => sub {
19
    plan tests => 5;
20
21
    $schema->storage->txn_begin;
22
23
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
24
25
    my $quota = Koha::Patron::Quota->new(
26
        {
27
            patron_id   => $patron->id,
28
            description => 'Test quota',
29
            start_date  => '2025-01-01',
30
            end_date    => '2025-12-31',
31
            allocation  => 10,
32
            used        => 0
33
        }
34
    )->store;
35
36
    ok( $quota->id, 'Quota created with auto-increment ID' );
37
    is( $quota->allocation, 10, 'Allocation set correctly' );
38
    is( $quota->used,       0,  'Used initialized to 0' );
39
40
    my $retrieved = Koha::Patron::Quotas->find( $quota->id );
41
    is( $retrieved->id, $quota->id, 'Quota retrieved successfully' );
42
43
    $quota->delete;
44
    is( Koha::Patron::Quotas->search( { id => $quota->id } )->count, 0, 'Quota deleted successfully' );
45
46
    $schema->storage->txn_rollback;
47
};
48
49
subtest 'filter_by_active() method' => sub {
50
    plan tests => 3;
51
52
    $schema->storage->txn_begin;
53
54
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
55
56
    my $past_quota = $builder->build_object(
57
        {
58
            class => 'Koha::Patron::Quotas',
59
            value => {
60
                patron_id  => $patron->id,
61
                start_date => '2020-01-01',
62
                end_date   => '2020-12-31'
63
            }
64
        }
65
    );
66
67
    my $current_quota = $builder->build_object(
68
        {
69
            class => 'Koha::Patron::Quotas',
70
            value => {
71
                patron_id  => $patron->id,
72
                start_date => dt_from_string()->ymd,
73
                end_date   => dt_from_string()->add( days => 30 )->ymd
74
            }
75
        }
76
    );
77
78
    my $future_quota = $builder->build_object(
79
        {
80
            class => 'Koha::Patron::Quotas',
81
            value => {
82
                patron_id  => $patron->id,
83
                start_date => dt_from_string()->add( days => 365 )->ymd,
84
                end_date   => dt_from_string()->add( days => 730 )->ymd
85
            }
86
        }
87
    );
88
89
    my $all_quotas = Koha::Patron::Quotas->search( { patron_id => $patron->id } );
90
    is( $all_quotas->count, 3, 'All quotas present' );
91
92
    my $active_quotas = $all_quotas->filter_by_active;
93
    is( $active_quotas->count,    1,                  'Only one active quota' );
94
    is( $active_quotas->next->id, $current_quota->id, 'Correct quota is active' );
95
96
    $schema->storage->txn_rollback;
97
};
98
99
subtest 'has_available_quota() method' => sub {
100
    plan tests => 3;
101
102
    $schema->storage->txn_begin;
103
104
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
105
106
    my $quota_with_available = $builder->build_object(
107
        {
108
            class => 'Koha::Patron::Quotas',
109
            value => {
110
                patron_id  => $patron->id,
111
                allocation => 10,
112
                used       => 5
113
            }
114
        }
115
    );
116
117
    ok( $quota_with_available->has_available_quota, 'Quota has available allocation' );
118
119
    my $quota_fully_used = $builder->build_object(
120
        {
121
            class => 'Koha::Patron::Quotas',
122
            value => {
123
                patron_id  => $patron->id,
124
                allocation => 10,
125
                used       => 10
126
            }
127
        }
128
    );
129
130
    ok( !$quota_fully_used->has_available_quota, 'Fully used quota has no available allocation' );
131
132
    my $quota_exceeded = $builder->build_object(
133
        {
134
            class => 'Koha::Patron::Quotas',
135
            value => {
136
                patron_id  => $patron->id,
137
                allocation => 10,
138
                used       => 15
139
            }
140
        }
141
    );
142
143
    ok( !$quota_exceeded->has_available_quota, 'Exceeded quota has no available allocation' );
144
145
    $schema->storage->txn_rollback;
146
};
147
148
subtest 'available_quota() method' => sub {
149
    plan tests => 3;
150
151
    $schema->storage->txn_begin;
152
153
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
154
155
    my $quota = $builder->build_object(
156
        {
157
            class => 'Koha::Patron::Quotas',
158
            value => {
159
                patron_id  => $patron->id,
160
                allocation => 10,
161
                used       => 3
162
            }
163
        }
164
    );
165
166
    is( $quota->available_quota, 7, 'Available quota calculated correctly' );
167
168
    $quota->used(10)->store;
169
    is( $quota->available_quota, 0, 'No available quota when fully used' );
170
171
    $quota->used(12)->store;
172
    is( $quota->available_quota, -2, 'Negative available quota when exceeded' );
173
174
    $schema->storage->txn_rollback;
175
};
176
177
subtest 'is_active() method' => sub {
178
    plan tests => 3;
179
180
    $schema->storage->txn_begin;
181
182
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
183
184
    my $past_quota = $builder->build_object(
185
        {
186
            class => 'Koha::Patron::Quotas',
187
            value => {
188
                patron_id  => $patron->id,
189
                start_date => '2020-01-01',
190
                end_date   => '2020-12-31'
191
            }
192
        }
193
    );
194
195
    ok( !$past_quota->is_active, 'Past quota is not active' );
196
197
    my $current_quota = $builder->build_object(
198
        {
199
            class => 'Koha::Patron::Quotas',
200
            value => {
201
                patron_id  => $patron->id,
202
                start_date => dt_from_string()->ymd,
203
                end_date   => dt_from_string()->add( days => 30 )->ymd
204
            }
205
        }
206
    );
207
208
    ok( $current_quota->is_active, 'Current quota is active' );
209
210
    my $future_quota = $builder->build_object(
211
        {
212
            class => 'Koha::Patron::Quotas',
213
            value => {
214
                patron_id  => $patron->id,
215
                start_date => dt_from_string()->add( days => 365 )->ymd,
216
                end_date   => dt_from_string()->add( days => 730 )->ymd
217
            }
218
        }
219
    );
220
221
    ok( !$future_quota->is_active, 'Future quota is not active' );
222
223
    $schema->storage->txn_rollback;
224
};
225
226
subtest 'add_to_quota() method' => sub {
227
    plan tests => 3;
228
229
    $schema->storage->txn_begin;
230
231
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
232
233
    my $quota = $builder->build_object(
234
        {
235
            class => 'Koha::Patron::Quotas',
236
            value => {
237
                patron_id  => $patron->id,
238
                allocation => 10,
239
                used       => 3
240
            }
241
        }
242
    );
243
244
    is( $quota->used, 3, 'Initial used value is 3' );
245
246
    $quota->add_to_quota(2);
247
    is( $quota->used, 5, 'Used incremented by 2' );
248
249
    $quota->add_to_quota(7);
250
    is( $quota->used, 12, 'Used can exceed allocation' );
251
252
    $schema->storage->txn_rollback;
253
};
254
255
subtest 'add_usage() method' => sub {
256
    plan tests => 4;
257
258
    $schema->storage->txn_begin;
259
260
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
261
    my $item   = $builder->build_sample_item;
262
263
    my $quota = $builder->build_object(
264
        {
265
            class => 'Koha::Patron::Quotas',
266
            value => {
267
                patron_id  => $patron->id,
268
                allocation => 10,
269
                used       => 0
270
            }
271
        }
272
    );
273
274
    my $checkout = $builder->build_object(
275
        {
276
            class => 'Koha::Checkouts',
277
            value => {
278
                borrowernumber => $patron->id,
279
                itemnumber     => $item->id
280
            }
281
        }
282
    );
283
284
    my $usage = $quota->add_usage( { issue_id => $checkout->issue_id, type => 'ISSUE', amount => 1 } );
285
286
    ok( defined $usage, 'Usage created' );
287
    is( $usage->patron_quota_id, $quota->id,          'Usage linked to quota' );
288
    is( $usage->issue_id,        $checkout->issue_id, 'Usage linked to checkout' );
289
290
    $quota->discard_changes;
291
    is( $quota->used, 1, 'Quota used incremented' );
292
293
    $schema->storage->txn_rollback;
294
};
295
296
subtest 'period clash validation' => sub {
297
    plan tests => 4;
298
299
    $schema->storage->txn_begin;
300
301
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
302
303
    my $existing_quota = $builder->build_object(
304
        {
305
            class => 'Koha::Patron::Quotas',
306
            value => {
307
                patron_id  => $patron->id,
308
                start_date => '2025-01-01',
309
                end_date   => '2025-12-31'
310
            }
311
        }
312
    );
313
314
    throws_ok {
315
        Koha::Patron::Quota->new(
316
            {
317
                patron_id   => $patron->id,
318
                description => 'Overlapping quota',
319
                start_date  => '2025-06-01',
320
                end_date    => '2025-12-31',
321
                allocation  => 5,
322
                used        => 0
323
            }
324
        )->store;
325
    }
326
    'Koha::Exceptions::Quota::Clash', 'Overlapping period throws clash exception';
327
328
    throws_ok {
329
        Koha::Patron::Quota->new(
330
            {
331
                patron_id   => $patron->id,
332
                description => 'Same start date quota',
333
                start_date  => '2025-01-01',
334
                end_date    => '2025-06-30',
335
                allocation  => 5,
336
                used        => 0
337
            }
338
        )->store;
339
    }
340
    'Koha::Exceptions::Quota::Clash', 'Period starting same day throws clash exception';
341
342
    my $non_overlapping_past = Koha::Patron::Quota->new(
343
        {
344
            patron_id   => $patron->id,
345
            description => 'Past quota',
346
            start_date  => '2024-01-01',
347
            end_date    => '2024-12-31',
348
            allocation  => 5,
349
            used        => 0
350
        }
351
    )->store;
352
353
    ok( $non_overlapping_past->id, 'Non-overlapping past quota created successfully' );
354
355
    my $non_overlapping_future = Koha::Patron::Quota->new(
356
        {
357
            patron_id   => $patron->id,
358
            description => 'Future quota',
359
            start_date  => '2026-01-01',
360
            end_date    => '2026-12-31',
361
            allocation  => 5,
362
            used        => 0
363
        }
364
    )->store;
365
366
    ok( $non_overlapping_future->id, 'Non-overlapping future quota created successfully' );
367
368
    $schema->storage->txn_rollback;
369
};
370
371
subtest 'relations' => sub {
372
    plan tests => 4;
373
374
    $schema->storage->txn_begin;
375
376
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
377
    my $item   = $builder->build_sample_item;
378
379
    my $quota = $builder->build_object(
380
        {
381
            class => 'Koha::Patron::Quotas',
382
            value => { patron_id => $patron->id }
383
        }
384
    );
385
386
    isa_ok( $quota->patron, 'Koha::Patron', 'Patron relation works' );
387
    is( $quota->patron->id, $patron->id, 'Correct patron returned' );
388
389
    my $checkout = $builder->build_object(
390
        {
391
            class => 'Koha::Checkouts',
392
            value => {
393
                borrowernumber => $patron->id,
394
                itemnumber     => $item->id
395
            }
396
        }
397
    );
398
399
    my $usage = $builder->build_object(
400
        {
401
            class => 'Koha::Patron::Quota::Usages',
402
            value => {
403
                patron_quota_id => $quota->id,
404
                patron_id       => $patron->id,
405
                issue_id        => $checkout->issue_id,
406
                type            => 'ISSUE'
407
            }
408
        }
409
    );
410
411
    my $usages = $quota->usages;
412
    isa_ok( $usages, 'Koha::Patron::Quota::Usages', 'Usages relation works' );
413
    is( $usages->count, 1, 'Correct number of usages returned' );
414
415
    $schema->storage->txn_rollback;
416
};
(-)a/t/db_dependent/api/v1/patrons_quotas.t (-1 / +520 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
use Modern::Perl;
4
5
use Test::More tests => 8;
6
use Test::Mojo;
7
use Test::NoWarnings;
8
9
use t::lib::TestBuilder;
10
use t::lib::Mocks;
11
12
use Koha::Patron::Quotas;
13
use Koha::Database;
14
use Koha::DateUtils qw( dt_from_string );
15
16
my $schema  = Koha::Database->new->schema;
17
my $builder = t::lib::TestBuilder->new;
18
19
my $t = Test::Mojo->new('Koha::REST::V1');
20
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
21
22
subtest 'list() tests' => sub {
23
24
    plan tests => 14;
25
26
    $schema->storage->txn_begin;
27
28
    Koha::Patron::Quotas->search->delete;
29
30
    my $librarian = $builder->build_object(
31
        {
32
            class => 'Koha::Patrons',
33
            value => { flags => 2**4 }
34
        }
35
    );
36
    my $password = 'thePassword123';
37
    $librarian->set_password( { password => $password, skip_validation => 1 } );
38
    my $userid = $librarian->userid;
39
40
    my $patron = $builder->build_object(
41
        {
42
            class => 'Koha::Patrons',
43
            value => { flags => 0 }
44
        }
45
    );
46
    my $patron_password = 'thePassword000';
47
    $patron->set_password( { password => $patron_password, skip_validation => 1 } );
48
    my $unauth_userid = $patron->userid;
49
50
    my $patron_id = $patron->id;
51
52
    $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas")->status_is(200)->json_is( [] );
53
54
    my $current_year_start = dt_from_string()->truncate( to => 'year' )->ymd;
55
    my $current_year_end   = dt_from_string()->truncate( to => 'year' )->add( years => 1 )->subtract( days => 1 )->ymd;
56
57
    my $quota = $builder->build_object(
58
        {
59
            class => 'Koha::Patron::Quotas',
60
            value => {
61
                patron_id  => $patron_id,
62
                start_date => $current_year_start,
63
                end_date   => $current_year_end,
64
                allocation => 10,
65
                used       => 0
66
            }
67
        }
68
    );
69
70
    $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas")->status_is(200)->json_is( [ $quota->to_api ] );
71
72
    my $another_quota = $builder->build_object(
73
        {
74
            class => 'Koha::Patron::Quotas',
75
            value => {
76
                patron_id  => $patron_id,
77
                start_date => '2020-01-01',
78
                end_date   => '2020-12-31',
79
                allocation => 5,
80
                used       => 3
81
            }
82
        }
83
    );
84
85
    $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas")
86
        ->status_is(200)
87
        ->json_is( [ $quota->to_api, $another_quota->to_api ] );
88
89
    $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas?only_active=true")
90
        ->status_is(200)
91
        ->json_is( [ $quota->to_api ] );
92
93
    $t->get_ok("//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas")->status_is(403);
94
95
    $schema->storage->txn_rollback;
96
};
97
98
subtest 'get() tests' => sub {
99
100
    plan tests => 7;
101
102
    $schema->storage->txn_begin;
103
104
    my $librarian = $builder->build_object(
105
        {
106
            class => 'Koha::Patrons',
107
            value => { flags => 2**4 }
108
        }
109
    );
110
    my $password = 'thePassword123';
111
    $librarian->set_password( { password => $password, skip_validation => 1 } );
112
    my $userid = $librarian->userid;
113
114
    my $patron = $builder->build_object(
115
        {
116
            class => 'Koha::Patrons',
117
            value => { flags => 0 }
118
        }
119
    );
120
    my $patron_password = 'thePassword000';
121
    $patron->set_password( { password => $patron_password, skip_validation => 1 } );
122
    my $unauth_userid = $patron->userid;
123
124
    my $quota = $builder->build_object(
125
        {
126
            class => 'Koha::Patron::Quotas',
127
            value => { patron_id => $patron->id }
128
        }
129
    );
130
131
    $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $quota->patron_id . "/quotas/" . $quota->id )
132
        ->status_is(200)
133
        ->json_is( $quota->to_api );
134
135
    $t->get_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/" . $quota->patron_id . "/quotas/" . $quota->id )
136
        ->status_is(403);
137
138
    my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } );
139
    my $non_existent_id = $quota_to_delete->id;
140
    $quota_to_delete->delete;
141
142
    $t->get_ok( "//$userid:$password@/api/v1/patrons/" . $quota->patron_id . "/quotas/$non_existent_id" )
143
        ->status_is(404);
144
145
    $schema->storage->txn_rollback;
146
};
147
148
subtest 'add() tests' => sub {
149
150
    plan tests => 18;
151
152
    $schema->storage->txn_begin;
153
154
    my $librarian = $builder->build_object(
155
        {
156
            class => 'Koha::Patrons',
157
            value => { flags => 2**4 }
158
        }
159
    );
160
    my $password = 'thePassword123';
161
    $librarian->set_password( { password => $password, skip_validation => 1 } );
162
    my $userid = $librarian->userid;
163
164
    my $patron = $builder->build_object(
165
        {
166
            class => 'Koha::Patrons',
167
            value => { flags => 0 }
168
        }
169
    );
170
    my $patron_password = 'thePassword000';
171
    $patron->set_password( { password => $patron_password, skip_validation => 1 } );
172
    my $unauth_userid = $patron->userid;
173
174
    my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } );
175
    my $patron_id   = $test_patron->id;
176
177
    my $current_year_start = dt_from_string()->truncate( to => 'year' )->ymd;
178
    my $current_year_end   = dt_from_string()->truncate( to => 'year' )->add( years => 1 )->subtract( days => 1 )->ymd;
179
    my $next_year_start    = dt_from_string()->truncate( to => 'year' )->add( years => 1 )->ymd;
180
    my $next_year_end      = dt_from_string()->truncate( to => 'year' )->add( years => 2 )->subtract( days => 1 )->ymd;
181
182
    my $quota_data = {
183
        description => 'Test quota',
184
        start_date  => $current_year_start,
185
        end_date    => $current_year_end,
186
        allocation  => 10,
187
        used        => 0
188
    };
189
190
    $t->post_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas" => json => $quota_data )
191
        ->status_is(403);
192
193
    $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $quota_data )
194
        ->status_is( 201, 'SWAGGER3.2.1' )
195
        ->json_is( '/start_date' => $current_year_start )
196
        ->json_is( '/end_date'   => $current_year_end )
197
        ->json_is( '/allocation' => 10 )
198
        ->json_is( '/used'       => 0 )
199
        ->header_like( Location => qr|^/api/v1/patrons/$patron_id/quotas/\d+|, 'SWAGGER3.4.1' );
200
201
    my $quota_with_id = {
202
        id          => 2,
203
        description => 'Test quota with ID',
204
        start_date  => $next_year_start,
205
        end_date    => $next_year_end,
206
        allocation  => 5,
207
        used        => 0
208
    };
209
210
    $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $quota_with_id )
211
        ->status_is(400)
212
        ->json_has('/errors');
213
214
    my $overlapping_start = dt_from_string()->truncate( to => 'year' )->add( months => 6 )->ymd;
215
216
    my $overlapping_quota = {
217
        description => 'Overlapping quota',
218
        start_date  => $overlapping_start,
219
        end_date    => $current_year_end,
220
        allocation  => 5,
221
        used        => 0
222
    };
223
224
    $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $overlapping_quota )
225
        ->status_is(409)
226
        ->json_is( '/error' => 'Quota period overlaps with existing quota' );
227
228
    my $future_year_start = dt_from_string()->truncate( to => 'year' )->add( years => 2 )->ymd;
229
    my $future_year_end   = dt_from_string()->truncate( to => 'year' )->add( years => 3 )->subtract( days => 1 )->ymd;
230
231
    my $invalid_quota = {
232
        description  => 'Invalid quota',
233
        start_date   => $future_year_start,
234
        end_date     => $future_year_end,
235
        allocation   => 10,
236
        used         => 0,
237
        invalid_prop => 'Invalid'
238
    };
239
240
    $t->post_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas" => json => $invalid_quota )
241
        ->status_is(400)
242
        ->json_has('/errors');
243
244
    $schema->storage->txn_rollback;
245
};
246
247
subtest 'update() tests' => sub {
248
249
    plan tests => 16;
250
251
    $schema->storage->txn_begin;
252
253
    my $librarian = $builder->build_object(
254
        {
255
            class => 'Koha::Patrons',
256
            value => { flags => 2**4 }
257
        }
258
    );
259
    my $password = 'thePassword123';
260
    $librarian->set_password( { password => $password, skip_validation => 1 } );
261
    my $userid = $librarian->userid;
262
263
    my $patron = $builder->build_object(
264
        {
265
            class => 'Koha::Patrons',
266
            value => { flags => 0 }
267
        }
268
    );
269
    my $patron_password = 'thePassword000';
270
    $patron->set_password( { password => $patron_password, skip_validation => 1 } );
271
    my $unauth_userid = $patron->userid;
272
273
    my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } );
274
    my $patron_id   = $test_patron->id;
275
276
    my $current_year_start = dt_from_string()->truncate( to => 'year' )->ymd;
277
    my $current_year_end   = dt_from_string()->truncate( to => 'year' )->add( years  => 1 )->subtract( days => 1 )->ymd;
278
    my $overlapping_start  = dt_from_string()->truncate( to => 'year' )->add( months => 6 )->ymd;
279
280
    my $quota = $builder->build_object(
281
        {
282
            class => 'Koha::Patron::Quotas',
283
            value => {
284
                patron_id  => $patron_id,
285
                start_date => $current_year_start,
286
                end_date   => $current_year_end,
287
                allocation => 10,
288
                used       => 0
289
            }
290
        }
291
    );
292
293
    $t->put_ok(
294
        "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => {
295
            description => 'Updated quota',
296
            start_date  => $current_year_start,
297
            end_date    => $current_year_end,
298
            allocation  => 15,
299
            used        => 0
300
        }
301
    )->status_is(403);
302
303
    my $updated_quota = $quota->to_api;
304
    delete $updated_quota->{id};           # readOnly field
305
    delete $updated_quota->{patron_id};    # readOnly field
306
    $updated_quota->{allocation} = 15;
307
308
    $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $updated_quota )
309
        ->status_is(200)
310
        ->json_is( '/allocation' => 15 );
311
312
    my $partial_update = { allocation => 20 };
313
314
    $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $partial_update )
315
        ->status_is(400)
316
        ->json_has('/errors');
317
318
    my $another_quota = $builder->build_object(
319
        {
320
            class => 'Koha::Patron::Quotas',
321
            value => {
322
                patron_id  => $patron_id,
323
                start_date => '2020-01-01',
324
                end_date   => '2020-12-31',
325
                allocation => 5,
326
                used       => 0
327
            }
328
        }
329
    );
330
331
    my $overlapping_update = $quota->to_api;
332
    delete $overlapping_update->{id};                    # readOnly field
333
    delete $overlapping_update->{patron_id};             # readOnly field
334
    $overlapping_update->{start_date} = '2020-06-01';    # Overlaps with another_quota
335
    $overlapping_update->{end_date}   = '2020-12-31';
336
337
    $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $overlapping_update )
338
        ->status_is(409)
339
        ->json_is( '/error' => 'Quota period overlaps with existing quota' );
340
341
    my $invalid_update = $quota->to_api;
342
    delete $invalid_update->{id};                        # readOnly field
343
    delete $invalid_update->{patron_id};                 # readOnly field
344
    $invalid_update->{invalid_prop} = 'Invalid';
345
346
    $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id => json => $invalid_update )
347
        ->status_is(400)
348
        ->json_has('/errors');
349
350
    my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } );
351
    my $non_existent_id = $quota_to_delete->id;
352
    $quota_to_delete->delete;
353
354
    $t->put_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/$non_existent_id" => json => $updated_quota )
355
        ->status_is(404);
356
357
    $schema->storage->txn_rollback;
358
};
359
360
subtest 'delete() tests' => sub {
361
362
    plan tests => 7;
363
364
    $schema->storage->txn_begin;
365
366
    my $librarian = $builder->build_object(
367
        {
368
            class => 'Koha::Patrons',
369
            value => { flags => 2**4 }
370
        }
371
    );
372
    my $password = 'thePassword123';
373
    $librarian->set_password( { password => $password, skip_validation => 1 } );
374
    my $userid = $librarian->userid;
375
376
    my $patron = $builder->build_object(
377
        {
378
            class => 'Koha::Patrons',
379
            value => { flags => 0 }
380
        }
381
    );
382
    my $patron_password = 'thePassword000';
383
    $patron->set_password( { password => $patron_password, skip_validation => 1 } );
384
    my $unauth_userid = $patron->userid;
385
386
    my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } );
387
    my $patron_id   = $test_patron->id;
388
389
    my $quota = $builder->build_object(
390
        {
391
            class => 'Koha::Patron::Quotas',
392
            value => { patron_id => $patron_id }
393
        }
394
    );
395
396
    $t->delete_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas/" . $quota->id )
397
        ->status_is(403);
398
399
    $t->delete_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id )
400
        ->status_is( 204, 'SWAGGER3.2.4' )
401
        ->content_is( '', 'SWAGGER3.3.4' );
402
403
    my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } );
404
    my $non_existent_id = $quota_to_delete->id;
405
    $quota_to_delete->delete;
406
407
    $t->delete_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas/$non_existent_id")->status_is(404);
408
409
    $schema->storage->txn_rollback;
410
};
411
412
subtest 'get_usage() tests' => sub {
413
414
    plan tests => 13;
415
416
    $schema->storage->txn_begin;
417
418
    my $librarian = $builder->build_object(
419
        {
420
            class => 'Koha::Patrons',
421
            value => { flags => 2**4 }
422
        }
423
    );
424
    my $password = 'thePassword123';
425
    $librarian->set_password( { password => $password, skip_validation => 1 } );
426
    my $userid = $librarian->userid;
427
428
    my $patron = $builder->build_object(
429
        {
430
            class => 'Koha::Patrons',
431
            value => { flags => 0 }
432
        }
433
    );
434
    my $patron_password = 'thePassword000';
435
    $patron->set_password( { password => $patron_password, skip_validation => 1 } );
436
    my $unauth_userid = $patron->userid;
437
438
    my $test_patron = $builder->build_object( { class => 'Koha::Patrons' } );
439
    my $patron_id   = $test_patron->id;
440
441
    my $quota = $builder->build_object(
442
        {
443
            class => 'Koha::Patron::Quotas',
444
            value => { patron_id => $patron_id }
445
        }
446
    );
447
448
    $t->get_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" )
449
        ->status_is(200)
450
        ->json_is( [] );
451
452
    my $usage = $builder->build_object(
453
        {
454
            class => 'Koha::Patron::Quota::Usages',
455
            value => {
456
                patron_quota_id => $quota->id,
457
                patron_id       => $patron_id,
458
                issue_id        => undef,
459
                type            => 'ISSUE'
460
            }
461
        }
462
    );
463
464
    $t->get_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" )
465
        ->status_is(200)
466
        ->json_is( [ $usage->to_api ] );
467
468
    my $another_usage = $builder->build_object(
469
        {
470
            class => 'Koha::Patron::Quota::Usages',
471
            value => {
472
                patron_quota_id => $quota->id,
473
                patron_id       => $patron_id,
474
                issue_id        => undef,
475
                type            => 'ISSUE'
476
            }
477
        }
478
    );
479
480
    $t->get_ok( "//$userid:$password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" )
481
        ->status_is(200)
482
        ->json_is( [ $usage->to_api, $another_usage->to_api ] );
483
484
    $t->get_ok( "//$unauth_userid:$patron_password@/api/v1/patrons/$patron_id/quotas/" . $quota->id . "/usages" )
485
        ->status_is(403);
486
487
    my $quota_to_delete = $builder->build_object( { class => 'Koha::Patron::Quotas' } );
488
    my $non_existent_id = $quota_to_delete->id;
489
    $quota_to_delete->delete;
490
491
    $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas/$non_existent_id/usages")->status_is(404);
492
493
    $schema->storage->txn_rollback;
494
};
495
496
subtest 'query parameter validation' => sub {
497
498
    plan tests => 3;
499
500
    $schema->storage->txn_begin;
501
502
    my $librarian = $builder->build_object(
503
        {
504
            class => 'Koha::Patrons',
505
            value => { flags => 2**4 }
506
        }
507
    );
508
    my $password = 'thePassword123';
509
    $librarian->set_password( { password => $password, skip_validation => 1 } );
510
    my $userid = $librarian->userid;
511
512
    my $patron    = $builder->build_object( { class => 'Koha::Patrons' } );
513
    my $patron_id = $patron->id;
514
515
    $t->get_ok("//$userid:$password@/api/v1/patrons/$patron_id/quotas?quota_blah=blah")
516
        ->status_is(400)
517
        ->json_is( [ { path => '/query/quota_blah', message => 'Malformed query string' } ] );
518
519
    $schema->storage->txn_rollback;
520
};

Return to bug 38924