Bug 32336: Add unit test for UNIMARC encoding problems in REST API
[koha.git] / t / db_dependent / api / v1 / biblios.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 utf8;
21 use Encode;
22
23 use Test::More tests => 11;
24 use Test::MockModule;
25 use Test::Mojo;
26 use Test::Warn;
27
28 use t::lib::Mocks;
29 use t::lib::TestBuilder;
30
31 use C4::Auth;
32 use C4::Circulation qw( AddIssue AddReturn );
33
34 use Koha::Biblios;
35 use Koha::Database;
36 use Koha::Checkouts;
37 use Koha::Old::Checkouts;
38
39 my $schema  = Koha::Database->new->schema;
40 my $builder = t::lib::TestBuilder->new;
41
42 t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
43
44 my $t = Test::Mojo->new('Koha::REST::V1');
45
46 subtest 'get() tests' => sub {
47
48     plan tests => 22;
49
50     $schema->storage->txn_begin;
51
52     my $patron = $builder->build_object(
53         {
54             class => 'Koha::Patrons',
55             value => { flags => 0 }
56         }
57     );
58     my $password = 'thePassword123';
59     $patron->set_password( { password => $password, skip_validation => 1 } );
60     $patron->discard_changes;
61     my $userid = $patron->userid;
62
63     my $biblio = $builder->build_sample_biblio({
64         title  => 'The unbearable lightness of being',
65         author => 'Milan Kundera'
66     });
67     $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber)
68       ->status_is(403);
69
70     $patron->flags(4)->store;
71
72     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
73                 => { Accept => 'application/weird+format' } )
74       ->status_is(400);
75
76     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
77                  => { Accept => 'application/json' } )
78       ->status_is(200)
79       ->json_is( '/title', 'The unbearable lightness of being' )
80       ->json_is( '/author', 'Milan Kundera' );
81
82     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
83                  => { Accept => 'application/marcxml+xml' } )
84       ->status_is(200);
85
86     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
87                  => { Accept => 'application/marc-in-json' } )
88       ->status_is(200);
89
90     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
91                  => { Accept => 'application/marc' } )
92       ->status_is(200);
93
94     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
95                  => { Accept => 'text/plain' } )
96       ->status_is(200)
97       ->content_is($biblio->metadata->record->as_formatted);
98
99     $biblio->delete;
100     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
101                  => { Accept => 'application/marc' } )
102       ->status_is(404)
103       ->json_is( '/error', 'Object not found.' );
104
105     subtest 'marc-in-json encoding tests' => sub {
106
107         plan tests => 3;
108
109         my $title_with_diacritics = "L'insoutenable légèreté de l'être";
110
111         my $biblio = $builder->build_sample_biblio(
112             {
113                 title  => $title_with_diacritics,
114                 author => "Milan Kundera"
115             }
116         );
117
118         my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
119                     => { Accept => 'application/marc-in-json' } )
120           ->status_is(200)->tx->res->body;
121
122         my $encoded_title  = Encode::encode( "UTF-8", $title_with_diacritics );
123
124         like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
125     };
126
127     subtest 'marcxml encoding tests' => sub {
128         plan tests => 3;
129
130         my $marcflavour = C4::Context->preference('marcflavour');
131         t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
132
133
134         my $title_with_diacritics = "L'insoutenable légèreté de l'être";
135
136         my $biblio = $builder->build_sample_biblio(
137             {
138                 title  => $title_with_diacritics,
139                 author => "Milan Kundera"
140             }
141         );
142
143         my $record = $biblio->metadata->record;
144         $record->leader('     nam         3  4500');
145         $biblio->metadata->metadata($record->as_xml_record('UNIMARC'));
146         $biblio->metadata->store;
147
148         my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
149                     => { Accept => 'application/marcxml+xml' } )
150           ->status_is(200)->tx->res->body;
151
152         my $encoded_title  = Encode::encode( "UTF-8", $title_with_diacritics );
153
154         like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
155         t::lib::Mocks::mock_preference('marcflavour', $marcflavour);
156     };
157
158     $schema->storage->txn_rollback;
159 };
160
161 subtest 'get_items() tests' => sub {
162
163     plan tests => 8;
164
165     $schema->storage->txn_begin;
166
167     my $patron = $builder->build_object(
168         {
169             class => 'Koha::Patrons',
170             value => { flags => 0 }
171         }
172     );
173     my $password = 'thePassword123';
174     $patron->set_password( { password => $password, skip_validation => 1 } );
175     $patron->discard_changes;
176     my $userid = $patron->userid;
177
178     my $biblio = $builder->build_sample_biblio();
179     $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
180       ->status_is(403);
181
182     $patron->flags(4)->store;
183
184     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
185       ->status_is(200)
186       ->json_is( '' => [], 'No items on the biblio' );
187
188     my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
189     my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
190
191     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
192       ->status_is(200)
193       ->json_is( '' => [ $item_1->to_api, $item_2->to_api ], 'The items are returned' );
194
195     $schema->storage->txn_rollback;
196 };
197
198 subtest 'delete() tests' => sub {
199
200     plan tests => 9;
201
202     $schema->storage->txn_begin;
203
204     my $patron = $builder->build_object(
205         {
206             class => 'Koha::Patrons',
207             value => { flags => 0 } # no permissions
208         }
209     );
210     my $password = 'thePassword123';
211     $patron->set_password( { password => $password, skip_validation => 1 } );
212     my $userid = $patron->userid;
213
214     my $item      = $builder->build_sample_item();
215     my $biblio_id = $item->biblionumber;
216
217     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
218       ->status_is(403, 'Not enough permissions makes it return the right code');
219
220     # Add permissions
221     $builder->build(
222         {
223             source => 'UserPermission',
224             value  => {
225                 borrowernumber => $patron->borrowernumber,
226                 module_bit     => 9,
227                 code           => 'edit_catalogue'
228             }
229         }
230     );
231
232
233     # Bibs with items cannot be deleted
234     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
235       ->status_is(409);
236
237     $item->delete();
238
239     # Bibs with no items can be deleted
240     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
241       ->status_is(204, 'SWAGGER3.2.4')
242       ->content_is('', 'SWAGGER3.3.4');
243
244     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
245       ->status_is(404);
246
247     $schema->storage->txn_rollback;
248 };
249
250 subtest 'get_public() tests' => sub {
251
252     plan tests => 25;
253
254     $schema->storage->txn_begin;
255
256     my $category = $builder->build_object({ class => 'Koha::Patron::Categories' });
257     my $patron = $builder->build_object(
258         {
259             class => 'Koha::Patrons',
260             value => {
261                 flags        => undef, # opac user
262                 categorycode => $category->categorycode
263             }
264         }
265     );
266     my $password = 'thePassword123';
267     $patron->set_password( { password => $password, skip_validation => 1 } );
268     $patron->discard_changes;
269     my $userid = $patron->userid;
270
271     my $biblio = $builder->build_sample_biblio({
272         title  => 'The unbearable lightness of being',
273         author => 'Milan Kundera'
274     });
275
276     # Make sure author in shown in the OPAC
277     my $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
278     while ( my $subfield = $subfields->next ) {
279         $subfield->set({ hidden => -1 })->store;
280     }
281     Koha::Caches->get_instance()->flush_all;
282
283     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
284                 => { Accept => 'application/weird+format' } )
285       ->status_is(400);
286
287     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
288                  => { Accept => 'text/plain' } )
289       ->status_is(200)
290       ->content_like( qr{100\s+_aMilan Kundera} )
291       ->content_like( qr{245\s+_aThe unbearable lightness of being} );
292
293     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
294                  => { Accept => 'application/marcxml+xml' } )
295       ->status_is(200);
296
297     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
298                  => { Accept => 'application/marc-in-json' } )
299       ->status_is(200);
300
301     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
302                  => { Accept => 'application/marc' } )
303       ->status_is(200);
304
305     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
306                  => { Accept => 'text/plain' } )
307       ->status_is(200)
308       ->content_is($biblio->metadata->record->as_formatted);
309
310     subtest 'anonymous access' => sub {
311         plan tests => 9;
312
313         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
314                  => { Accept => 'application/marcxml+xml' } )
315           ->status_is(200);
316
317         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
318                     => { Accept => 'application/marc-in-json' } )
319         ->status_is(200);
320
321         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
322                     => { Accept => 'application/marc' } )
323         ->status_is(200);
324
325         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
326                     => { Accept => 'text/plain' } )
327         ->status_is(200)
328         ->content_is($biblio->metadata->record->as_formatted);
329     };
330
331     subtest 'marc-in-json encoding tests' => sub {
332
333         plan tests => 3;
334
335         my $title_with_diacritics = "L'insoutenable légèreté de l'être";
336
337         my $biblio = $builder->build_sample_biblio(
338             {
339                 title  => $title_with_diacritics,
340                 author => "Milan Kundera"
341             }
342         );
343
344         my $result = $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
345                     => { Accept => 'application/marc-in-json' } )
346           ->status_is(200)->tx->res->body;
347
348         my $encoded_title  = Encode::encode( "UTF-8", $title_with_diacritics );
349
350         like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
351     };
352
353     # Hide author in OPAC
354     $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
355     while ( my $subfield = $subfields->next ) {
356         $subfield->set({ hidden => 1 })->store;
357     }
358
359     Koha::Caches->get_instance()->flush_all;
360
361     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
362                  => { Accept => 'text/plain' } )
363       ->status_is(200)
364       ->content_unlike( qr{100\s+_aMilan Kundera} )
365       ->content_like( qr{245\s+_aThe unbearable lightness of being} );
366
367     subtest 'hidden_in_opac tests' => sub {
368
369         plan tests => 6;
370
371         my $biblio_hidden_in_opac = 1;
372
373         my $biblio_class = Test::MockModule->new('Koha::Biblio');
374         # force biblio hidden in OPAC
375         $biblio_class->mock( 'hidden_in_opac', sub { return $biblio_hidden_in_opac; } );
376
377         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
378                  => { Accept => 'text/plain' } )
379           ->status_is(404, 'hidden_in_opac + anonymous => hidden');
380
381         my $category_override_hidden_items = 0;
382         my $category_class = Test::MockModule->new('Koha::Patron::Category');
383         $category_class->mock( 'override_hidden_items', sub { return $category_override_hidden_items; } );
384         $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
385                  => { Accept => 'text/plain' } )
386           ->status_is(404, "hidden_in_opac + patron whose category doesn't override => hidden");
387
388         # Make the category override
389         $category_override_hidden_items = 1;
390         $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
391                  => { Accept => 'text/plain' } )
392           ->status_is(200, "hidden_in_opac + patron whose category that overrides => displayed");
393
394         t::lib::Mocks::mock_preference('OpacHiddenItems');
395     };
396
397     $biblio->delete;
398     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
399                  => { Accept => 'application/marc' } )
400       ->status_is(404)
401       ->json_is( '/error', 'Object not found.' );
402
403     $schema->storage->txn_rollback;
404 };
405
406 subtest 'pickup_locations() tests' => sub {
407
408     plan tests => 16;
409
410     $schema->storage->txn_begin;
411
412     t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
413
414     # Small trick to ease testing
415     Koha::Libraries->search->update({ pickup_location => 0 });
416
417     my $library_1 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'A', pickup_location => 1 } });
418     my $library_2 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'B', pickup_location => 1 } });
419     my $library_3 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'C', pickup_location => 1 } });
420
421     my $library_1_api = $library_1->to_api();
422     my $library_2_api = $library_2->to_api();
423     my $library_3_api = $library_3->to_api();
424
425     $library_1_api->{needs_override} = Mojo::JSON->false;
426     $library_2_api->{needs_override} = Mojo::JSON->false;
427     $library_3_api->{needs_override} = Mojo::JSON->true;
428
429     my $patron = $builder->build_object(
430         {
431             class => 'Koha::Patrons',
432             value => { userid => 'tomasito', flags => 0 }
433         }
434     );
435     my $password = 'thePassword123';
436     $patron->set_password( { password => $password, skip_validation => 1 } );
437     my $userid = $patron->userid;
438     $builder->build(
439         {
440             source => 'UserPermission',
441             value  => {
442                 borrowernumber => $patron->borrowernumber,
443                 module_bit     => 6,
444                 code           => 'place_holds',
445             },
446         }
447     );
448
449     my $biblio_class = Test::MockModule->new('Koha::Biblio');
450     $biblio_class->mock(
451         'pickup_locations',
452         sub {
453             my ( $self, $params ) = @_;
454             my $mock_patron = $params->{patron};
455             is( $mock_patron->borrowernumber,
456                 $patron->borrowernumber, 'Patron passed correctly' );
457             return Koha::Libraries->search(
458                 {
459                     branchcode => {
460                         '-in' => [
461                             $library_1->branchcode,
462                             $library_2->branchcode
463                         ]
464                     }
465                 },
466                 {   # we make sure no surprises in the order of the result
467                     order_by => { '-asc' => 'marcorgcode' }
468                 }
469             );
470         }
471     );
472
473     my $biblio = $builder->build_sample_biblio;
474
475     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
476           . $biblio->id
477           . "/pickup_locations?patron_id=" . $patron->id )
478       ->json_is( [ $library_1_api, $library_2_api ] );
479
480     # filtering works!
481     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
482           . $biblio->id
483           . '/pickup_locations?'
484           . 'patron_id=' . $patron->id . '&q={"marc_org_code": { "-like": "A%" }}' )
485       ->json_is( [ $library_1_api ] );
486
487     t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
488
489     my $library_4 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 0, marcorgcode => 'X' } });
490     my $library_5 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 1, marcorgcode => 'Y' } });
491
492     my $library_5_api = $library_5->to_api();
493     $library_5_api->{needs_override} = Mojo::JSON->true;
494
495     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
496           . $biblio->id
497           . "/pickup_locations?"
498           . "patron_id=" . $patron->id . "&_order_by=marc_org_code" )
499       ->json_is( [ $library_1_api, $library_2_api, $library_3_api, $library_5_api ] );
500
501     subtest 'Pagination and AllowHoldPolicyOverride tests' => sub {
502
503         plan tests => 27;
504
505         t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
506
507         $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
508           ->json_is( [$library_1_api] )
509           ->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
510           ->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
511           ->header_unlike( 'Link', qr|rel="prev"| )
512           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
513           ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
514           ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
515
516         $t->get_ok( "//$userid:$password@/api/v1/biblios/"
517               . $biblio->id
518               . "/pickup_locations?"
519               . "patron_id="
520               . $patron->id
521               . "&_order_by=marc_org_code"
522               . "&_per_page=1&_page=3" )    # force the needs_override=1 check
523           ->json_is( [$library_3_api] )
524           ->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
525           ->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
526           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="prev"# )
527           ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1.*)>\; rel="next"# )
528           ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
529           ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
530
531         t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
532
533         $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
534           ->json_is( [$library_1_api] )
535           ->header_is( 'X-Total-Count', '2' )
536           ->header_is( 'X-Base-Total-Count', '2' )
537           ->header_unlike( 'Link', qr|rel="prev"| )
538           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
539           ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
540           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1).*>\; rel="last"# );
541     };
542
543     my $deleted_patron = $builder->build_object({ class => 'Koha::Patrons' });
544     my $deleted_patron_id = $deleted_patron->id;
545     $deleted_patron->delete;
546
547     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
548           . $biblio->id
549           . "/pickup_locations?"
550           . "patron_id=" . $deleted_patron_id )
551       ->status_is( 400 )
552       ->json_is( '/error' => 'Patron not found' );
553
554     $biblio->delete;
555
556     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
557           . $biblio->id
558           . "/pickup_locations?"
559           . "patron_id=" . $patron->id )
560       ->status_is( 404 )
561       ->json_is( '/error' => 'Biblio not found' );
562
563     $schema->storage->txn_rollback;
564 };
565
566 subtest 'get_items_public() tests' => sub {
567
568     plan tests => 15;
569
570     $schema->storage->txn_begin;
571
572     my $override_hidden_items = 0;
573
574     my $mocked_category = Test::MockModule->new('Koha::Patron::Category');
575     $mocked_category->mock(
576         'override_hidden_items',
577         sub {
578             return $override_hidden_items;
579         }
580     );
581
582     my $rules = undef;
583
584     my $mocked_context = Test::MockModule->new('C4::Context');
585     $mocked_context->mock(
586         'yaml_preference',
587         sub {
588             return $rules;
589         }
590     );
591
592     my $patron   = $builder->build_object( { class => 'Koha::Patrons' } );
593     my $password = 'thePassword123';
594     $patron->set_password( { password => $password, skip_validation => 1 } );
595     $patron->discard_changes;
596     my $userid = $patron->userid;
597
598     my $biblio = $builder->build_sample_biblio();
599
600     $t->get_ok(
601         "//$userid:$password@/api/v1/public/biblios/" . $biblio->id . "/items" )
602       ->status_is(200)->json_is( '' => [], 'No items on the biblio' );
603
604     my $item_1 = $builder->build_sample_item( { biblionumber => $biblio->id } );
605     my $item_2 = $builder->build_sample_item(
606         { biblionumber => $biblio->id, withdrawn => 1 } );
607
608     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
609           . $biblio->biblionumber
610           . "/items" )->status_is(200)->json_is(
611         '' => [
612             $item_1->to_api( { public => 1 } ),
613             $item_2->to_api( { public => 1 } )
614         ],
615         'The items are returned'
616           );
617
618     $rules = { withdrawn => ['1'] };
619
620     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
621           . $biblio->biblionumber
622           . "/items" )->status_is(200)->json_is(
623         '' => [ $item_1->to_api( { public => 1 } ) ],
624         'The items are returned, hidden one is not returned'
625           );
626
627     $t->get_ok( "/api/v1/public/biblios/"
628           . $biblio->biblionumber
629           . "/items" )->status_is(200)->json_is(
630         '' => [ $item_1->to_api( { public => 1 } ) ],
631         'Anonymous user, items are returned, hidden one is not returned'
632           );
633
634
635     $override_hidden_items = 1;
636
637     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
638           . $biblio->biblionumber
639           . "/items" )->status_is(200)->json_is(
640         '' => [
641             $item_1->to_api( { public => 1 } ),
642             $item_2->to_api( { public => 1 } )
643         ],
644         'The items are returned, the patron category has an override'
645           );
646
647     $schema->storage->txn_rollback;
648 };
649
650 subtest 'get_checkouts() tests' => sub {
651
652     plan tests => 14;
653
654     $schema->storage->txn_begin;
655
656     my $patron = $builder->build_object(
657         {
658             class => 'Koha::Patrons',
659             value => { flags => 0 }
660         }
661     );
662     my $password = 'thePassword123';
663     $patron->set_password( { password => $password, skip_validation => 1 } );
664     $patron->discard_changes;
665     my $userid = $patron->userid;
666
667     my $biblio = $builder->build_sample_biblio();
668     $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
669       ->status_is(403);
670
671     $patron->flags(1)->store; # circulate permissions
672
673     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
674       ->status_is(200)
675       ->json_is( '' => [], 'No checkouts on the biblio' );
676
677     my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
678     my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
679
680     AddIssue( $patron->unblessed, $item_1->barcode );
681     AddIssue( $patron->unblessed, $item_2->barcode );
682
683     my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
684       ->status_is(200)
685       ->tx->res->json;
686
687     my $checkout_1 = Koha::Checkouts->find({ itemnumber => $item_1->id });
688     my $checkout_2 = Koha::Checkouts->find({ itemnumber => $item_2->id });
689
690     is_deeply( $ret, [ $checkout_1->to_api, $checkout_2->to_api ] );
691
692     AddReturn( $item_1->barcode );
693
694     $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
695       ->status_is(200)
696       ->tx->res->json;
697
698     is_deeply( $ret, [ $checkout_2->to_api ] );
699
700     $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts?checked_in=1")
701       ->status_is(200)
702       ->tx->res->json;
703
704     my $old_checkout_1 = Koha::Old::Checkouts->find( $checkout_1->id );
705
706     is_deeply( $ret, [ $old_checkout_1->to_api ] );
707
708     $schema->storage->txn_rollback;
709 };
710
711 subtest 'set_rating() tests' => sub {
712
713     plan tests => 12;
714
715     $schema->storage->txn_begin;
716
717     my $patron = $builder->build_object(
718         {
719             class => 'Koha::Patrons',
720             value => { flags => 0 }
721         }
722     );
723     my $password = 'thePassword123';
724     $patron->set_password( { password => $password, skip_validation => 1 } );
725     $patron->discard_changes;
726     my $userid = $patron->userid;
727
728     my $biblio = $builder->build_sample_biblio();
729     $t->post_ok("/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
730       ->status_is(403);
731
732     $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
733       ->status_is(200)
734       ->json_is( '/rating', '3' )
735       ->json_is( '/average', '3' )
736       ->json_is( '/count', '1' );
737
738     $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => undef })
739       ->status_is(200)
740       ->json_is( '/rating', undef )
741       ->json_is( '/average', '0' )
742       ->json_is( '/count', '0' );
743
744     $schema->storage->txn_rollback;
745
746 };
747
748
749 subtest 'post() tests' => sub {
750
751     plan tests => 13;
752
753     $schema->storage->txn_begin;
754
755     my $patron = $builder->build_object(
756         {
757             class => 'Koha::Patrons',
758             value => { flags => 0 } # no permissions
759         }
760     );
761     my $password = 'thePassword123';
762     $patron->set_password( { password => $password, skip_validation => 1 } );
763     my $userid = $patron->userid;
764
765     my $frameworkcode = 'BKS';
766     my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
767     <record
768         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
769         xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
770         xmlns="http://www.loc.gov/MARC21/slim">
771
772       <leader>01102pam a2200289 a 7500</leader>
773       <controlfield tag="001">2504398</controlfield>
774       <controlfield tag="005">20200421093816.0</controlfield>
775       <controlfield tag="008">920610s1993    caub         s001 0 eng  </controlfield>
776       <datafield tag="010" ind1=" " ind2=" ">
777         <subfield code="a">   92021731 </subfield>
778       </datafield>
779       <datafield tag="020" ind1=" " ind2=" ">
780         <subfield code="a">05200784381 (Test marcxml)</subfield>
781       </datafield>
782       <datafield tag="020" ind1=" " ind2=" ">
783         <subfield code="a">05200784461 (Test marcxml)</subfield>
784       </datafield>
785       <datafield tag="040" ind1=" " ind2=" ">
786         <subfield code="a">DLC</subfield>
787         <subfield code="c">DLC</subfield>
788         <subfield code="d">DLC</subfield>
789       </datafield>
790       <datafield tag="041" ind1="0" ind2=" ">
791         <subfield code="a">enggrc</subfield>
792       </datafield>
793       <datafield tag="050" ind1="0" ind2="0">
794         <subfield code="a">PA522</subfield>
795         <subfield code="b">.M38 1993</subfield>
796       </datafield>
797       <datafield tag="082" ind1="0" ind2="0">
798         <subfield code="a">480</subfield>
799         <subfield code="2">20</subfield>
800       </datafield>
801       <datafield tag="100" ind1="1" ind2=" ">
802         <subfield code="a">Mastronarde, Donald J.</subfield>
803         <subfield code="9">389</subfield>
804       </datafield>
805       <datafield tag="245" ind1="1" ind2="0">
806         <subfield code="a">Introduction to Attic Greek (Using marcxml) /</subfield>
807         <subfield code="c">Donald J. Mastronarde.</subfield>
808       </datafield>
809       <datafield tag="260" ind1=" " ind2=" ">
810         <subfield code="a">Berkeley :</subfield>
811         <subfield code="b">University of California Press,</subfield>
812         <subfield code="c">c1993.</subfield>
813       </datafield>
814       <datafield tag="300" ind1=" " ind2=" ">
815         <subfield code="a">ix, 425 p. :</subfield>
816         <subfield code="b">maps ;</subfield>
817         <subfield code="c">26 cm.</subfield>
818       </datafield>
819       <datafield tag="500" ind1=" " ind2=" ">
820         <subfield code="a">Includes index.</subfield>
821       </datafield>
822       <datafield tag="650" ind1=" " ind2="0">
823         <subfield code="a">Attic Greek dialect</subfield>
824         <subfield code="9">7</subfield>
825       </datafield>
826       <datafield tag="856" ind1="4" ind2="2">
827         <subfield code="3">Contributor biographical information</subfield>
828         <subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
829       </datafield>
830       <datafield tag="856" ind1="4" ind2="2">
831         <subfield code="3">Publisher description</subfield>
832         <subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
833       </datafield>
834       <datafield tag="906" ind1=" " ind2=" ">
835         <subfield code="a">7</subfield>
836         <subfield code="b">cbc</subfield>
837         <subfield code="c">orignew</subfield>
838         <subfield code="d">1</subfield>
839         <subfield code="e">ocip</subfield>
840         <subfield code="f">19</subfield>
841         <subfield code="g">y-gencatlg</subfield>
842       </datafield>
843       <datafield tag="942" ind1=" " ind2=" ">
844         <subfield code="2">ddc</subfield>
845         <subfield code="c">BK</subfield>
846       </datafield>
847       <datafield tag="955" ind1=" " ind2=" ">
848         <subfield code="a">pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93</subfield>
849       </datafield>
850       <datafield tag="999" ind1=" " ind2=" ">
851         <subfield code="c">3</subfield>
852         <subfield code="d">3</subfield>
853       </datafield>
854     </record>|;
855
856     my $mij = q|{
857       "fields": [
858         {
859           "001": "2504398"
860         },
861         {
862           "005": "20200421093816.0"
863         },
864         {
865           "008": "920610s1993    caub         s001 0 eng  "
866         },
867         {
868           "010": {
869             "ind1": " ",
870             "subfields": [
871               {
872                 "a": "   92021731 "
873               }
874             ],
875             "ind2": " "
876           }
877         },
878         {
879           "020": {
880             "subfields": [
881               {
882                 "a": "05200784382 (Test mij)"
883               }
884             ],
885             "ind2": " ",
886             "ind1": " "
887           }
888         },
889         {
890           "020": {
891             "subfields": [
892               {
893                 "a": "05200784462 (Test mij)"
894               }
895             ],
896             "ind1": " ",
897             "ind2": " "
898           }
899         },
900         {
901           "040": {
902             "subfields": [
903               {
904                 "a": "DLC"
905               },
906               {
907                 "c": "DLC"
908               },
909               {
910                 "d": "DLC"
911               }
912             ],
913             "ind2": " ",
914             "ind1": " "
915           }
916         },
917         {
918           "041": {
919             "ind2": " ",
920             "subfields": [
921               {
922                 "a": "enggrc"
923               }
924             ],
925             "ind1": "0"
926           }
927         },
928         {
929           "050": {
930             "subfields": [
931               {
932                 "a": "PA522"
933               },
934               {
935                 "b": ".M38 1993"
936               }
937             ],
938             "ind1": "0",
939             "ind2": "0"
940           }
941         },
942         {
943           "082": {
944             "subfields": [
945               {
946                 "a": "480"
947               },
948               {
949                 "2": "20"
950               }
951             ],
952             "ind2": "0",
953             "ind1": "0"
954           }
955         },
956         {
957           "100": {
958             "ind2": " ",
959             "subfields": [
960               {
961                 "a": "Mastronarde, Donald J."
962               },
963               {
964                 "9": "389"
965               }
966             ],
967             "ind1": "1"
968           }
969         },
970         {
971           "245": {
972             "ind1": "1",
973             "subfields": [
974               {
975                 "a": "Introduction to Attic Greek  (Using mij) /"
976               },
977               {
978                 "c": "Donald J. Mastronarde."
979               }
980             ],
981             "ind2": "0"
982           }
983         },
984         {
985           "260": {
986             "subfields": [
987               {
988                 "a": "Berkeley :"
989               },
990               {
991                 "b": "University of California Press,"
992               },
993               {
994                 "c": "c1993."
995               }
996             ],
997             "ind2": " ",
998             "ind1": " "
999           }
1000         },
1001         {
1002           "300": {
1003             "ind1": " ",
1004             "subfields": [
1005               {
1006                 "a": "ix, 425 p. :"
1007               },
1008               {
1009                 "b": "maps ;"
1010               },
1011               {
1012                 "c": "26 cm."
1013               }
1014             ],
1015             "ind2": " "
1016           }
1017         },
1018         {
1019           "500": {
1020             "subfields": [
1021               {
1022                 "a": "Includes index."
1023               }
1024             ],
1025             "ind1": " ",
1026             "ind2": " "
1027           }
1028         },
1029         {
1030           "650": {
1031             "subfields": [
1032               {
1033                 "a": "Attic Greek dialect"
1034               },
1035               {
1036                 "9": "7"
1037               }
1038             ],
1039             "ind2": "0",
1040             "ind1": " "
1041           }
1042         },
1043         {
1044           "856": {
1045             "subfields": [
1046               {
1047                 "3": "Contributor biographical information"
1048               },
1049               {
1050                 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1051               }
1052             ],
1053             "ind2": "2",
1054             "ind1": "4"
1055           }
1056         },
1057         {
1058           "856": {
1059             "ind1": "4",
1060             "subfields": [
1061               {
1062                 "3": "Publisher description"
1063               },
1064               {
1065                 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
1066               }
1067             ],
1068             "ind2": "2"
1069           }
1070         },
1071         {
1072           "906": {
1073             "subfields": [
1074               {
1075                 "a": "7"
1076               },
1077               {
1078                 "b": "cbc"
1079               },
1080               {
1081                 "c": "orignew"
1082               },
1083               {
1084                 "d": "1"
1085               },
1086               {
1087                 "e": "ocip"
1088               },
1089               {
1090                 "f": "19"
1091               },
1092               {
1093                 "g": "y-gencatlg"
1094               }
1095             ],
1096             "ind1": " ",
1097             "ind2": " "
1098           }
1099         },
1100         {
1101           "942": {
1102             "subfields": [
1103               {
1104                 "2": "ddc"
1105               },
1106               {
1107                 "c": "BK"
1108               }
1109             ],
1110             "ind2": " ",
1111             "ind1": " "
1112           }
1113         },
1114         {
1115           "955": {
1116             "subfields": [
1117               {
1118                 "a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93"
1119               }
1120             ],
1121             "ind2": " ",
1122             "ind1": " "
1123           }
1124         },
1125         {
1126           "999": {
1127             "subfields": [
1128               {
1129                 "c": "3"
1130               },
1131               {
1132                 "d": "3"
1133               }
1134             ],
1135             "ind1": " ",
1136             "ind2": " "
1137           }
1138         }
1139       ],
1140       "leader": "01102pam a2200289 a 8500"
1141     }|;
1142     my $marc = q|01102pam a2200289 a 9500001000800000005001700008008004100025010001700066020002800083020003500111040001800146041001100164050002100175082001200196100003200208245005800240260005600298300003300354500002000387650002700407856009500434856008700529906004500616942001200661955013000673999000900803\1e2504398\1e20200421093816.0\1e920610s1993    caub         s001 0 eng  \1e  \1fa   92021731 \1e  \1fa05200784383 (Test usmarc)\1e  \1fa05200784463 (Test usmarc)\1e  \1faDLC\1fcDLC\1fdDLC\1e\1faenggrc\1e00\1faPA522\1fb.M38 1993\1e00\1fa480\1f220\1e\1faMastronarde, Donald J.\1f9389\1e10\1faIntroduction to Attic Greek  (Using usmarc) /\1fcDonald J. Mastronarde.\1e  \1faBerkeley :\1fbUniversity of California Press,\1fcc1993.\1e  \1faix, 425 p. :\1fbmaps ;\1fc26 cm.\1e  \1faIncludes index.\1e 0\1faAttic Greek dialect\1f97\1e42\1f3Contributor biographical information\1fuhttp://www.loc.gov/catdir/bios/ucal051/92021731.html\1e42\1f3Publisher description\1fuhttp://www.loc.gov/catdir/description/ucal041/92021731.html\1e  \1fa7\1fbcbc\1fcorignew\1fd1\1feocip\1ff19\1fgy-gencatlg\1e  \1f2ddc\1fcBK\1e  \1fapc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93\1e  \1fc3\1fd3\1e\1d|;
1143
1144     $t->post_ok("//$userid:$password@/api/v1/biblios")
1145       ->status_is(403, 'Not enough permissions makes it return the right code');
1146
1147     # Add permissions
1148     $builder->build(
1149         {
1150             source => 'UserPermission',
1151             value  => {
1152                 borrowernumber => $patron->borrowernumber,
1153                 module_bit     => 9,
1154                 code           => 'edit_catalogue'
1155             }
1156         }
1157     );
1158
1159     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode, "x-marc-schema" => 'INVALID'})
1160       ->status_is(400, 'Invalid header x-marc-schema');
1161
1162     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
1163       ->status_is(200)
1164       ->json_has('/id');
1165
1166     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode, 'x-confirm-not-duplicate' => 1} => $mij)
1167       ->status_is(200)
1168       ->json_has('/id');
1169
1170     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1171       ->status_is(200)
1172       ->json_has('/id');
1173
1174     $schema->storage->txn_rollback;
1175 };
1176
1177 subtest 'put() tests' => sub {
1178
1179     plan tests => 14;
1180
1181     $schema->storage->txn_begin;
1182
1183     my $patron = $builder->build_object(
1184         {
1185             class => 'Koha::Patrons',
1186             value => { flags => 0 } # no permissions
1187         }
1188     );
1189     my $password = 'thePassword123';
1190     $patron->set_password( { password => $password, skip_validation => 1 } );
1191     my $userid = $patron->userid;
1192
1193     my $frameworkcode = 'BKS';
1194     my $biblio = $builder->build_sample_biblio({frameworkcode => $frameworkcode});
1195
1196     my $biblionumber = $biblio->biblionumber;
1197
1198     my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
1199     <record
1200         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1201         xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
1202         xmlns="http://www.loc.gov/MARC21/slim">
1203
1204       <leader>01102pam a2200289 a 6500</leader>
1205       <controlfield tag="001">2504398</controlfield>
1206       <controlfield tag="005">20200421093816.0</controlfield>
1207       <controlfield tag="008">920610s1993    caub         s001 0 eng  </controlfield>
1208       <datafield tag="010" ind1=" " ind2=" ">
1209         <subfield code="a">   92021731 </subfield>
1210       </datafield>
1211       <datafield tag="020" ind1=" " ind2=" ">
1212         <subfield code="a">05200784384 (Test json)</subfield>
1213       </datafield>
1214       <datafield tag="020" ind1=" " ind2=" ">
1215         <subfield code="a">05200784464 (Test json)</subfield>
1216       </datafield>
1217       <datafield tag="040" ind1=" " ind2=" ">
1218         <subfield code="a">DLC</subfield>
1219         <subfield code="c">DLC</subfield>
1220         <subfield code="d">DLC</subfield>
1221       </datafield>
1222       <datafield tag="041" ind1="0" ind2=" ">
1223         <subfield code="a">enggrc</subfield>
1224       </datafield>
1225       <datafield tag="050" ind1="0" ind2="0">
1226         <subfield code="a">PA522</subfield>
1227         <subfield code="b">.M38 1993</subfield>
1228       </datafield>
1229       <datafield tag="082" ind1="0" ind2="0">
1230         <subfield code="a">480</subfield>
1231         <subfield code="2">20</subfield>
1232       </datafield>
1233       <datafield tag="100" ind1="1" ind2=" ">
1234         <subfield code="a">Mastronarde, Donald J.</subfield>
1235         <subfield code="9">389</subfield>
1236       </datafield>
1237       <datafield tag="245" ind1="1" ind2="0">
1238         <subfield code="a">Introduction to Attic Greek  (Using marcxml) /</subfield>
1239         <subfield code="c">Donald J. Mastronarde.</subfield>
1240       </datafield>
1241       <datafield tag="260" ind1=" " ind2=" ">
1242         <subfield code="a">Berkeley :</subfield>
1243         <subfield code="b">University of California Press,</subfield>
1244         <subfield code="c">c1993.</subfield>
1245       </datafield>
1246       <datafield tag="300" ind1=" " ind2=" ">
1247         <subfield code="a">ix, 425 p. :</subfield>
1248         <subfield code="b">maps ;</subfield>
1249         <subfield code="c">26 cm.</subfield>
1250       </datafield>
1251       <datafield tag="500" ind1=" " ind2=" ">
1252         <subfield code="a">Includes index.</subfield>
1253       </datafield>
1254       <datafield tag="650" ind1=" " ind2="0">
1255         <subfield code="a">Attic Greek dialect</subfield>
1256         <subfield code="9">7</subfield>
1257       </datafield>
1258       <datafield tag="856" ind1="4" ind2="2">
1259         <subfield code="3">Contributor biographical information</subfield>
1260         <subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
1261       </datafield>
1262       <datafield tag="856" ind1="4" ind2="2">
1263         <subfield code="3">Publisher description</subfield>
1264         <subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
1265       </datafield>
1266       <datafield tag="906" ind1=" " ind2=" ">
1267         <subfield code="a">7</subfield>
1268         <subfield code="b">cbc</subfield>
1269         <subfield code="c">orignew</subfield>
1270         <subfield code="d">1</subfield>
1271         <subfield code="e">ocip</subfield>
1272         <subfield code="f">19</subfield>
1273         <subfield code="g">y-gencatlg</subfield>
1274       </datafield>
1275       <datafield tag="942" ind1=" " ind2=" ">
1276         <subfield code="2">ddc</subfield>
1277         <subfield code="c">BK</subfield>
1278       </datafield>
1279       <datafield tag="955" ind1=" " ind2=" ">
1280         <subfield code="a">pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93</subfield>
1281       </datafield>
1282       <datafield tag="999" ind1=" " ind2=" ">
1283         <subfield code="c">3</subfield>
1284         <subfield code="d">3</subfield>
1285       </datafield>
1286     </record>|;
1287
1288     my $mij = q|{
1289       "fields": [
1290         {
1291           "001": "2504398"
1292         },
1293         {
1294           "005": "20200421093816.0"
1295         },
1296         {
1297           "008": "920610s1993    caub         s001 0 eng  "
1298         },
1299         {
1300           "010": {
1301             "ind1": " ",
1302             "subfields": [
1303               {
1304                 "a": "   92021731 "
1305               }
1306             ],
1307             "ind2": " "
1308           }
1309         },
1310         {
1311           "020": {
1312             "subfields": [
1313               {
1314                 "a": "05200784382 (Test mij)"
1315               }
1316             ],
1317             "ind2": " ",
1318             "ind1": " "
1319           }
1320         },
1321         {
1322           "020": {
1323             "subfields": [
1324               {
1325                 "a": "05200784462 (Test mij)"
1326               }
1327             ],
1328             "ind1": " ",
1329             "ind2": " "
1330           }
1331         },
1332         {
1333           "040": {
1334             "subfields": [
1335               {
1336                 "a": "DLC"
1337               },
1338               {
1339                 "c": "DLC"
1340               },
1341               {
1342                 "d": "DLC"
1343               }
1344             ],
1345             "ind2": " ",
1346             "ind1": " "
1347           }
1348         },
1349         {
1350           "041": {
1351             "ind2": " ",
1352             "subfields": [
1353               {
1354                 "a": "enggrc"
1355               }
1356             ],
1357             "ind1": "0"
1358           }
1359         },
1360         {
1361           "050": {
1362             "subfields": [
1363               {
1364                 "a": "PA522"
1365               },
1366               {
1367                 "b": ".M38 1993"
1368               }
1369             ],
1370             "ind1": "0",
1371             "ind2": "0"
1372           }
1373         },
1374         {
1375           "082": {
1376             "subfields": [
1377               {
1378                 "a": "480"
1379               },
1380               {
1381                 "2": "20"
1382               }
1383             ],
1384             "ind2": "0",
1385             "ind1": "0"
1386           }
1387         },
1388         {
1389           "100": {
1390             "ind2": " ",
1391             "subfields": [
1392               {
1393                 "a": "Mastronarde, Donald J."
1394               },
1395               {
1396                 "9": "389"
1397               }
1398             ],
1399             "ind1": "1"
1400           }
1401         },
1402         {
1403           "245": {
1404             "ind1": "1",
1405             "subfields": [
1406               {
1407                 "a": "Introduction to Attic Greek  (Using mij) /"
1408               },
1409               {
1410                 "c": "Donald J. Mastronarde."
1411               }
1412             ],
1413             "ind2": "0"
1414           }
1415         },
1416         {
1417           "260": {
1418             "subfields": [
1419               {
1420                 "a": "Berkeley :"
1421               },
1422               {
1423                 "b": "University of California Press,"
1424               },
1425               {
1426                 "c": "c1993."
1427               }
1428             ],
1429             "ind2": " ",
1430             "ind1": " "
1431           }
1432         },
1433         {
1434           "300": {
1435             "ind1": " ",
1436             "subfields": [
1437               {
1438                 "a": "ix, 425 p. :"
1439               },
1440               {
1441                 "b": "maps ;"
1442               },
1443               {
1444                 "c": "26 cm."
1445               }
1446             ],
1447             "ind2": " "
1448           }
1449         },
1450         {
1451           "500": {
1452             "subfields": [
1453               {
1454                 "a": "Includes index."
1455               }
1456             ],
1457             "ind1": " ",
1458             "ind2": " "
1459           }
1460         },
1461         {
1462           "650": {
1463             "subfields": [
1464               {
1465                 "a": "Attic Greek dialect"
1466               },
1467               {
1468                 "9": "7"
1469               }
1470             ],
1471             "ind2": "0",
1472             "ind1": " "
1473           }
1474         },
1475         {
1476           "856": {
1477             "subfields": [
1478               {
1479                 "3": "Contributor biographical information"
1480               },
1481               {
1482                 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1483               }
1484             ],
1485             "ind2": "2",
1486             "ind1": "4"
1487           }
1488         },
1489         {
1490           "856": {
1491             "ind1": "4",
1492             "subfields": [
1493               {
1494                 "3": "Publisher description"
1495               },
1496               {
1497                 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
1498               }
1499             ],
1500             "ind2": "2"
1501           }
1502         },
1503         {
1504           "906": {
1505             "subfields": [
1506               {
1507                 "a": "7"
1508               },
1509               {
1510                 "b": "cbc"
1511               },
1512               {
1513                 "c": "orignew"
1514               },
1515               {
1516                 "d": "1"
1517               },
1518               {
1519                 "e": "ocip"
1520               },
1521               {
1522                 "f": "19"
1523               },
1524               {
1525                 "g": "y-gencatlg"
1526               }
1527             ],
1528             "ind1": " ",
1529             "ind2": " "
1530           }
1531         },
1532         {
1533           "942": {
1534             "subfields": [
1535               {
1536                 "2": "ddc"
1537               },
1538               {
1539                 "c": "BK"
1540               }
1541             ],
1542             "ind2": " ",
1543             "ind1": " "
1544           }
1545         },
1546         {
1547           "955": {
1548             "subfields": [
1549               {
1550                 "a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93"
1551               }
1552             ],
1553             "ind2": " ",
1554             "ind1": " "
1555           }
1556         },
1557         {
1558           "999": {
1559             "subfields": [
1560               {
1561                 "c": "3"
1562               },
1563               {
1564                 "d": "3"
1565               }
1566             ],
1567             "ind1": " ",
1568             "ind2": " "
1569           }
1570         }
1571       ],
1572       "leader": "01102pam a2200289 a 8500"
1573     }|;
1574     my $marc = q|01116pam a2200289 a 4500001000800000005001700008008004100025010001700066020002800083020002800111040001800139041001100157050002100168082001200189100003200201245007500233260005600308300003300364500002000397650002700417856009500444856008700539906004500626942001200671955013000683999001300813\1e2504398\1e20221223213433.0\1e920610s1993    caub         s001 0 eng  \1e  \1fa   92021731 \1e  \1fa05200784384 (Test json)\1e  \1fa05200784464 (Test json)\1e  \1faDLC\1fcDLC\1fdDLC\1e\1faenggrc\1e00\1faPA522\1fb.M38 1993\1e00\1fa480\1f220\1e\1faMastronarde, Donald J.\1f9389\1e10\1faIntroduction to Attic Greek  (Using usmarc) /\1fcDonald J. Mastronarde.\1e  \1faBerkeley :\1fbUniversity of California Press,\1fcc1993.\1e  \1faix, 425 p. :\1fbmaps ;\1fc26 cm.\1e  \1faIncludes index.\1e 0\1faAttic Greek dialect\1f97\1e42\1f3Contributor biographical information\1fuhttp://www.loc.gov/catdir/bios/ucal051/92021731.html\1e42\1f3Publisher description\1fuhttp://www.loc.gov/catdir/description/ucal041/92021731.html\1e  \1fa7\1fbcbc\1fcorignew\1fd1\1feocip\1ff19\1fgy-gencatlg\1e  \1f2ddc\1fcBK\1e  \1fapc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93\1e  \1fc715\1fd715\1e\1d|;
1575
1576     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber")
1577       ->status_is(403, 'Not enough permissions makes it return the right code');
1578
1579     # Add permissions
1580     $builder->build(
1581         {
1582             source => 'UserPermission',
1583             value  => {
1584                 borrowernumber => $patron->borrowernumber,
1585                 module_bit     => 9,
1586                 code           => 'edit_catalogue'
1587             }
1588         }
1589     );
1590
1591     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
1592       ->status_is(200)
1593       ->json_has('/id');
1594
1595     $biblio = Koha::Biblios->find($biblionumber);
1596
1597     is($biblio->title, 'Introduction to Attic Greek  (Using marcxml) /');
1598
1599     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode} => $mij)
1600       ->status_is(200)
1601       ->json_has('/id');
1602
1603     $biblio = Koha::Biblios->find($biblionumber);
1604
1605     is($biblio->title, 'Introduction to Attic Greek  (Using mij) /');
1606
1607     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1608       ->status_is(200)
1609       ->json_has('/id');
1610
1611     $biblio = Koha::Biblios->find($biblionumber);
1612
1613     is($biblio->title, 'Introduction to Attic Greek  (Using usmarc) /');
1614
1615     $schema->storage->txn_rollback;
1616 };
1617
1618 subtest 'list() tests' => sub {
1619     plan tests => 14;
1620
1621     $schema->storage->txn_begin;
1622
1623     my $patron = $builder->build_object(
1624         {
1625             class => 'Koha::Patrons',
1626             value => { flags => 0 }
1627         }
1628     );
1629     my $password = 'thePassword123';
1630     $patron->set_password( { password => $password, skip_validation => 1 } );
1631     $patron->discard_changes;
1632     my $userid = $patron->userid;
1633
1634     my $biblionumber1 = $builder->build_sample_biblio->biblionumber;
1635     my $biblionumber2 = $builder->build_sample_biblio->biblionumber;
1636
1637     my $search =
1638 "[{\"biblionumber\": \"$biblionumber1\"}, {\"biblionumber\": \"$biblionumber2\"}]";
1639     $t->get_ok(
1640         "//$userid:$password@/api/v1/biblios/" => { 'x-koha-query' => $search }
1641     )->status_is(403);
1642
1643     $patron->flags(4)->store;
1644
1645     $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1646           { Accept => 'application/weird+format', 'x-koha-query' => $search } )
1647       ->status_is(400, 'Status is 400 for bad format');
1648
1649     $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1650           { Accept => 'application/json', 'x-koha-query' => $search } )
1651       ->status_is(200, 'Status is 200 for application/json');
1652
1653     $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1654           { Accept => 'application/marcxml+xml', 'x-koha-query' => $search } )
1655       ->status_is(200, 'Status is 200 for application/marcxml+xml');
1656
1657     $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1658           { Accept => 'application/marc-in-json', 'x-koha-query' => $search } )
1659       ->status_is(200, 'Status is 200 for application/marc-in-json');
1660
1661     $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1662           { Accept => 'application/marc', 'x-koha-query' => $search } )
1663       ->status_is(200, 'Status is 200 for application/marc');
1664
1665     $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1666           { Accept => 'text/plain', 'x-koha-query' => $search } )
1667       ->status_is(200, 'Status is 200 for text/plain');
1668
1669     $schema->storage->txn_rollback;
1670 };