Bug 23666: (follow-up) Adjust to new exceptions
[koha.git] / t / db_dependent / api / v1 / patrons_extended_attributes.t
1 #!/usr/bin/env perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use Test::More tests => 5;
21 use Test::Mojo;
22
23 use t::lib::TestBuilder;
24 use t::lib::Mocks;
25
26 use Koha::Database;
27
28 my $schema  = Koha::Database->new->schema;
29 my $builder = t::lib::TestBuilder->new();
30
31 my $t = Test::Mojo->new('Koha::REST::V1');
32
33 t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
34
35 subtest 'list_patron_attributes() tests' => sub {
36
37     plan tests => 9;
38
39     $schema->storage->txn_begin;
40
41     my $patron = $builder->build_object({
42         class => 'Koha::Patrons',
43         value => { flags => 2 ** 4 } # 'borrowers' flag == 4
44     });
45     my $password = 'thePassword123';
46     $patron->set_password({ password => $password, skip_validation => 1 });
47     my $userid = $patron->userid;
48
49     $t->get_ok("//$userid:$password@/api/v1/patrons/" . $patron->id . '/extended_attributes')
50       ->status_is( 200, 'SWAGGER3.2.2' )
51       ->json_is( [] );
52
53     # Let's add 3 attributes
54     foreach my $i ( 1..5 ) {
55         $builder->build_object({ class => 'Koha::Patron::Attributes', value => { borrowernumber => $patron->id } });
56     }
57
58     $t->get_ok("//$userid:$password@/api/v1/patrons/" . $patron->id . '/extended_attributes')
59       ->status_is( 200, 'SWAGGER3.2.2' )
60       ->json_is( '' => $patron->extended_attributes->to_api, 'Extended attributes retrieved correctly' );
61
62     my $non_existent_patron = $builder->build_object({ class => 'Koha::Patrons' });
63     my $non_existent_patron_id = $non_existent_patron->id;
64     # get rid of the patron
65     $non_existent_patron->delete;
66
67     $t->get_ok("//$userid:$password@/api/v1/patrons/" . $non_existent_patron_id . '/extended_attributes')
68       ->status_is( 404 )
69       ->json_is( '/error' => 'Patron not found' );
70
71     $schema->storage->txn_rollback;
72 };
73
74 subtest 'add() tests' => sub {
75
76     plan tests => 9;
77
78     $schema->storage->txn_begin;
79
80     my $patron = $builder->build_object({
81         class => 'Koha::Patrons',
82         value => { flags => 2 ** 4 } # 'borrowers' flag == 4
83     });
84     my $password = 'thePassword123';
85     $patron->set_password({ password => $password, skip_validation => 1 });
86     my $userid = $patron->userid;
87
88     my $mandatory_attr_type = $builder->build_object(
89         {
90             class => 'Koha::Patron::Attribute::Types',
91             value => {
92                 mandatory     => 1,
93                 repeatable    => 0,
94                 unique_id     => 0,
95                 category_code => undef
96             }
97         }
98     );
99     my $repeatable_attr_type = $builder->build_object(
100         {
101             class => 'Koha::Patron::Attribute::Types',
102             value => {
103                 mandatory     => 0,
104                 repeatable    => 1,
105                 unique_id     => 0,
106                 category_code => undef
107             }
108         }
109     );
110     my $unique_attr_type = $builder->build_object(
111         {
112             class => 'Koha::Patron::Attribute::Types',
113             value => {
114                 mandatory     => 0,
115                 repeatable    => 0,
116                 unique_id     => 1,
117                 category_code => undef
118             }
119         }
120     );
121
122     my $non_existent_patron = $builder->build_object({ class => 'Koha::Patrons' });
123     my $non_existent_patron_id = $non_existent_patron->id;
124     # get rid of the patron
125     $non_existent_patron->delete;
126
127     $t->post_ok( "//$userid:$password@/api/v1/patrons/"
128           . $non_existent_patron_id
129           . '/extended_attributes' => json =>
130           { type => $repeatable_attr_type->code, value => 'something' } )
131       ->status_is(404)->json_is( '/error' => 'Patron not found' );
132
133     my $response = $t->post_ok( "//$userid:$password@/api/v1/patrons/"
134           . $patron->id
135           . '/extended_attributes' => json =>
136           { type => $repeatable_attr_type->code, value => 'something' } )
137       ->status_is(201)->tx->res->json;
138
139     is_deeply(
140         Koha::Patron::Attributes->find( $response->{extended_attribute_id} )->to_api,
141         $response,
142         "The returned object is on the DB"
143     );
144
145     subtest 'Repeatability tests' => sub {
146         $t->post_ok( "//$userid:$password@/api/v1/patrons/"
147             . $patron->id
148             . '/extended_attributes' => json =>
149             { type => $repeatable_attr_type->code, value => 'something' } )
150         ->status_is(201, 'Repeatable attributes go through');
151
152         # Let's add a non-repeatable one
153         $patron->add_extended_attribute(
154             { code => $unique_attr_type->code, attribute => 'non_repeatable_1' } );
155         $t->post_ok( "//$userid:$password@/api/v1/patrons/"
156             . $patron->id
157             . '/extended_attributes' => json =>
158             { type => $unique_attr_type->code, value => 'non_repeatable_2' } )
159         ->status_is(409)
160         ->json_is( '/error' =>
161                 'Tried to add more than one non-repeatable attributes. type='
162             . $unique_attr_type->code
163             . ' value=non_repeatable_2' );
164     };
165
166     subtest 'Attribute uniqueness tests' => sub {
167
168         plan tests => 3;
169
170         my $patron_2 = $builder->build_object( { class => 'Koha::Patrons' } );
171         $t->post_ok( "//$userid:$password@/api/v1/patrons/"
172             . $patron_2->id
173             . '/extended_attributes' => json =>
174             { type => $unique_attr_type->code, value => 'non_repeatable_1' } )
175         ->status_is(409)
176         ->json_is( '/error' =>
177                 'Your action breaks a unique constraint on the attribute. type='
178             . $unique_attr_type->code
179             . ' value=non_repeatable_1' );
180     };
181
182     subtest 'Invalid type tests' => sub {
183
184         plan tests => 3;
185
186         my $invalid_type_obj = $builder->build_object({ class => 'Koha::Patron::Attribute::Types' });
187         my $invalid_type = $invalid_type_obj->code;
188         $invalid_type_obj->delete;
189
190         $t->post_ok( "//$userid:$password@/api/v1/patrons/"
191             . $patron->id
192             . '/extended_attributes' => json =>
193             { type => $invalid_type, value => 'blah' } )
194         ->status_is(400)
195         ->json_is( '/error' => "Tried to use an invalid attribute type. type=$invalid_type" );
196     };
197
198     $schema->storage->txn_rollback;
199 };
200
201 subtest 'overwrite() tests' => sub {
202
203     plan tests => 29;
204
205     $schema->storage->txn_begin;
206
207     Koha::Patron::Attribute::Types->delete;
208
209     my $patron = $builder->build_object({
210         class => 'Koha::Patrons',
211         value => { flags => 2 ** 4 } # 'borrowers' flag == 4
212     });
213     my $password = 'thePassword123';
214     $patron->set_password({ password => $password, skip_validation => 1 });
215     my $userid = $patron->userid;
216
217     my $mandatory_attr_type = $builder->build_object(
218         {
219             class => 'Koha::Patron::Attribute::Types',
220             value => {
221                 mandatory     => 1,
222                 repeatable    => 0,
223                 unique_id     => 0,
224                 category_code => undef
225             }
226         }
227     );
228     my $repeatable_attr_type = $builder->build_object(
229         {
230             class => 'Koha::Patron::Attribute::Types',
231             value => {
232                 mandatory     => 0,
233                 repeatable    => 1,
234                 unique_id     => 0,
235                 category_code => undef
236             }
237         }
238     );
239     my $unique_attr_type = $builder->build_object(
240         {
241             class => 'Koha::Patron::Attribute::Types',
242             value => {
243                 mandatory     => 0,
244                 repeatable    => 0,
245                 unique_id     => 1,
246                 category_code => undef
247             }
248         }
249     );
250     my $invalid_type_obj = $builder->build_object(
251         {
252             class => 'Koha::Patron::Attribute::Types'
253         }
254     );
255     my $invalid_type = $invalid_type_obj->code;
256     $invalid_type_obj->delete;
257
258     my $non_existent_patron = $builder->build_object({ class => 'Koha::Patrons' });
259     my $non_existent_patron_id = $non_existent_patron->id;
260     # get rid of the patron
261     $non_existent_patron->delete;
262
263     $t->put_ok( "//$userid:$password@/api/v1/patrons/"
264           . $non_existent_patron_id
265           . '/extended_attributes' => json =>
266           [ { type => $repeatable_attr_type->code, value => 'something' } ] )
267       ->status_is(404)
268       ->json_is( '/error' => 'Patron not found' );
269
270     $t->put_ok( "//$userid:$password@/api/v1/patrons/"
271           . $patron->id
272           . '/extended_attributes' => json =>
273           [ { type => $invalid_type, value => 'something' } ] )
274       ->status_is(400)
275       ->json_is( '/error' => "Tried to use an invalid attribute type. type=$invalid_type" );
276
277     my $unique_value = 'The only one!';
278     my $dummy_patron = $builder->build_object({ class => 'Koha::Patrons' });
279     $dummy_patron->add_extended_attribute({ code => $unique_attr_type->code, attribute => $unique_value });
280
281     $t->put_ok( "//$userid:$password@/api/v1/patrons/"
282           . $patron->id
283           . '/extended_attributes' => json =>
284           [ { type => $unique_attr_type->code, value => $unique_value } ] )
285       ->status_is(409)
286       ->json_is( '/error' => "Your action breaks a unique constraint on the attribute. type=" . $unique_attr_type->code . " value=$unique_value" );
287
288     my $value_1 = 'value_1';
289     my $value_2 = 'value_2';
290
291     $t->put_ok( "//$userid:$password@/api/v1/patrons/"
292           . $patron->id
293           . '/extended_attributes' => json =>
294           [ { type => $unique_attr_type->code, value => $value_1 },
295             { type => $unique_attr_type->code, value => $value_2 } ] )
296       ->status_is(409)
297       ->json_is( '/error' => "Tried to add more than one non-repeatable attributes. type=" . $unique_attr_type->code . " value=$value_2" );
298
299     $t->put_ok( "//$userid:$password@/api/v1/patrons/"
300           . $patron->id
301           . '/extended_attributes' => json =>
302           [ { type => $unique_attr_type->code, value => $value_1 } ] )
303       ->status_is(400)
304       ->json_is( '/error' => "Missing mandatory extended attribute (type=" . $mandatory_attr_type->code . ')' );
305
306     $patron->add_extended_attribute({ code => $repeatable_attr_type->code, attribute => 'repeatable_1' });
307     $patron->add_extended_attribute({ code => $repeatable_attr_type->code, attribute => 'repeatable_2' });
308     $patron->add_extended_attribute({ code => $mandatory_attr_type->code,  attribute => 'mandatory' });
309     $patron->add_extended_attribute({ code => $unique_attr_type->code,  attribute => 'unique' });
310
311     $t->get_ok("//$userid:$password@/api/v1/patrons/" . $patron->id . '/extended_attributes')
312       ->status_is( 200, 'SWAGGER3.2.2' )
313       ->json_is( '' => $patron->extended_attributes->to_api, 'Extended attributes retrieved correctly' );
314
315     my $updated_attributes = [
316         {
317             type  => $repeatable_attr_type->code,
318             value => 'updated_repeatable_1'
319         },
320         {
321             type  => $repeatable_attr_type->code,
322             value => 'updated_repeatable_2'
323         },
324         {
325             type  => $repeatable_attr_type->code,
326             value => 'updated_repeatable_3'
327         },
328         {
329             type  => $mandatory_attr_type->code,
330             value => 'updated_mandatory'
331         }
332     ];
333
334     $t->put_ok( "//$userid:$password@/api/v1/patrons/"
335           . $patron->id
336           . '/extended_attributes' => json => $updated_attributes )
337       ->status_is(200)
338       ->json_is( '/0/type'  => $updated_attributes->[0]->{type} )
339       ->json_is( '/0/value' => $updated_attributes->[0]->{value} )
340       ->json_is( '/1/type'  => $updated_attributes->[1]->{type} )
341       ->json_is( '/1/value' => $updated_attributes->[1]->{value} )
342       ->json_is( '/2/type'  => $updated_attributes->[2]->{type} )
343       ->json_is( '/2/value' => $updated_attributes->[2]->{value} )
344       ->json_is( '/3/type'  => $updated_attributes->[3]->{type} )
345       ->json_is( '/3/value' => $updated_attributes->[3]->{value} )
346       ->json_hasnt( '/4');
347
348     $schema->storage->txn_rollback;
349 };
350
351 subtest 'delete() tests' => sub {
352
353     plan tests => 9;
354
355     $schema->storage->txn_begin;
356
357     my $patron = $builder->build_object({
358         class => 'Koha::Patrons',
359         value => { flags => 2 ** 4 } # 'borrowers' flag == 4
360     });
361     my $password = 'thePassword123';
362     $patron->set_password({ password => $password, skip_validation => 1 });
363     my $userid = $patron->userid;
364
365     my $attr_type = $builder->build_object(
366         {
367             class => 'Koha::Patron::Attribute::Types',
368             value => {
369                 mandatory     => 0,
370                 repeatable    => 1,
371                 unique_id     => 0,
372                 category_code => undef
373             }
374         }
375     );
376
377     my $dummy_patron = $builder->build_object({ class => 'Koha::Patrons' });
378
379     my $attr = $dummy_patron->add_extended_attribute({ code => $attr_type->code, attribute => 'blah' });
380
381     $t->delete_ok("//$userid:$password@/api/v1/patrons/" . $dummy_patron->id . '/extended_attributes/' . $attr->id )
382       ->status_is(204, 'SWAGGER3.2.4')
383       ->content_is('', 'SWAGGER3.3.4');
384
385     $t->delete_ok("//$userid:$password@/api/v1/patrons/" . $dummy_patron->id . '/extended_attributes/' . $attr->id )
386       ->status_is(404)
387       ->json_is( '/error' => 'Attribute not found' );
388
389     $dummy_patron->delete;
390
391     $t->delete_ok("//$userid:$password@/api/v1/patrons/" . $dummy_patron->id . '/extended_attributes/' . $attr->id )
392       ->status_is(404)
393       ->json_is( '/error' => 'Patron not found' );
394
395     $schema->storage->txn_rollback;
396 };
397
398 subtest 'update() tests' => sub {
399
400     plan tests => 12;
401
402     $schema->storage->txn_begin;
403
404     my $patron = $builder->build_object(
405         {
406             class => 'Koha::Patrons',
407             value => { flags => 2**4 }    # 'borrowers' flag == 4
408         }
409     );
410     my $password = 'thePassword123';
411     $patron->set_password( { password => $password, skip_validation => 1 } );
412     my $userid = $patron->userid;
413
414     my $repeatable_attr_type = $builder->build_object(
415         {
416             class => 'Koha::Patron::Attribute::Types',
417             value => {
418                 mandatory     => 0,
419                 repeatable    => 1,
420                 unique_id     => 0,
421                 category_code => undef
422             }
423         }
424     );
425     my $unique_attr_type = $builder->build_object(
426         {
427             class => 'Koha::Patron::Attribute::Types',
428             value => {
429                 mandatory     => 0,
430                 repeatable    => 0,
431                 unique_id     => 1,
432                 category_code => undef
433             }
434         }
435     );
436
437     # Add a unique attribute to our patron
438     my $unique_attribute = $patron->add_extended_attribute(
439         {
440             code      => $unique_attr_type->code,
441             attribute => 'WOW'
442         }
443     );
444
445     # Let's have an attribute ID we are sure doesn't exist on the DB
446     my $non_existent_attribute = $patron->add_extended_attribute(
447         {
448             code      => $repeatable_attr_type->code,
449             attribute => 'BOO'
450         }
451     );
452     my $non_existent_attribute_id = $non_existent_attribute->id;
453     $non_existent_attribute->delete;
454
455     my $non_existent_patron =
456       $builder->build_object( { class => 'Koha::Patrons' } );
457     my $non_existent_patron_id = $non_existent_patron->id;
458
459     # get rid of the patron
460     $non_existent_patron->delete;
461
462     $t->patch_ok( "//$userid:$password@/api/v1/patrons/"
463           . $non_existent_patron_id
464           . '/extended_attributes/'
465           . 123 => json => { value => 'something' } )->status_is(404)
466       ->json_is( '/error' => 'Patron not found' );
467
468     $t->patch_ok( "//$userid:$password@/api/v1/patrons/"
469           . $patron->id
470           . '/extended_attributes/'
471           . $non_existent_attribute_id => json => { value => 'something' } )
472       ->status_is(404)->json_is( '/error' => 'Attribute not found' );
473
474     my $response =
475       $t->patch_ok( "//$userid:$password@/api/v1/patrons/"
476           . $patron->id
477           . '/extended_attributes/'
478           . $unique_attribute->id => json => { value => 'HEY' } )
479       ->status_is(200)->tx->res->json;
480
481     is_deeply(
482         Koha::Patron::Attributes->find( $response->{extended_attribute_id} )
483           ->to_api,
484         $response,
485         "The returned object is on the DB"
486     );
487
488     my $unique_value = 'HEHE';
489
490     # Add a patron with the unique attribute to test changing to it
491     $builder->build_object( { class => 'Koha::Patrons' } )
492       ->add_extended_attribute(
493         {
494             code      => $unique_attr_type->code,
495             attribute => $unique_value
496         }
497       );
498
499     $t->patch_ok( "//$userid:$password@/api/v1/patrons/"
500           . $patron->id
501           . '/extended_attributes/'
502           . $unique_attribute->id => json => { value => $unique_value } )
503       ->status_is(409)
504       ->json_is( '/error' =>
505             "Your action breaks a unique constraint on the attribute. type="
506           . $unique_attr_type->code
507           . " value=$unique_value" );
508
509     $schema->storage->txn_rollback;
510 };