Bug 33036: REST API: Merge biblio records implements merging of records
[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 => 15;
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 Mojo::JSON qw(encode_json);
32
33 use C4::Auth;
34 use C4::Circulation qw( AddIssue AddReturn );
35
36 use Koha::Biblios;
37 use Koha::Database;
38 use Koha::DateUtils qw (dt_from_string);
39 use Koha::Checkouts;
40 use Koha::Old::Checkouts;
41
42 use Mojo::JSON qw(encode_json);
43
44 my $schema  = Koha::Database->new->schema;
45 my $builder = t::lib::TestBuilder->new;
46
47 t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
48
49 my $t = Test::Mojo->new('Koha::REST::V1');
50
51 subtest 'get() tests' => sub {
52
53     plan tests => 22;
54
55     $schema->storage->txn_begin;
56
57     my $patron = $builder->build_object(
58         {
59             class => 'Koha::Patrons',
60             value => { flags => 0 }
61         }
62     );
63     my $password = 'thePassword123';
64     $patron->set_password( { password => $password, skip_validation => 1 } );
65     $patron->discard_changes;
66     my $userid = $patron->userid;
67
68     my $biblio = $builder->build_sample_biblio({
69         title  => 'The unbearable lightness of being',
70         author => 'Milan Kundera'
71     });
72     $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber)
73       ->status_is(403);
74
75     $patron->flags(4)->store;
76
77     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
78                 => { Accept => 'application/weird+format' } )
79       ->status_is(400);
80
81     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
82                  => { Accept => 'application/json' } )
83       ->status_is(200)
84       ->json_is( '/title', 'The unbearable lightness of being' )
85       ->json_is( '/author', 'Milan Kundera' );
86
87     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
88                  => { Accept => 'application/marcxml+xml' } )
89       ->status_is(200);
90
91     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
92                  => { Accept => 'application/marc-in-json' } )
93       ->status_is(200);
94
95     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
96                  => { Accept => 'application/marc' } )
97       ->status_is(200);
98
99     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
100                  => { Accept => 'text/plain' } )
101       ->status_is(200)
102       ->content_is($biblio->metadata->record->as_formatted);
103
104     $biblio->delete;
105     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
106                  => { Accept => 'application/marc' } )
107       ->status_is(404)
108       ->json_is( '/error', 'Object not found.' );
109
110     subtest 'marc-in-json encoding tests' => sub {
111
112         plan tests => 3;
113
114         my $title_with_diacritics = "L'insoutenable légèreté de l'être";
115
116         my $biblio = $builder->build_sample_biblio(
117             {
118                 title  => $title_with_diacritics,
119                 author => "Milan Kundera"
120             }
121         );
122
123         my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
124                     => { Accept => 'application/marc-in-json' } )
125           ->status_is(200)->tx->res->body;
126
127         my $encoded_title  = Encode::encode( "UTF-8", $title_with_diacritics );
128
129         like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
130     };
131
132     subtest 'marcxml encoding tests' => sub {
133         plan tests => 3;
134
135         my $marcflavour = C4::Context->preference('marcflavour');
136         t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
137
138
139         my $title_with_diacritics = "L'insoutenable légèreté de l'être";
140
141         my $biblio = $builder->build_sample_biblio(
142             {
143                 title  => $title_with_diacritics,
144                 author => "Milan Kundera"
145             }
146         );
147
148         my $record = $biblio->metadata->record;
149         $record->leader('     nam         3  4500');
150         $biblio->metadata->metadata($record->as_xml_record('UNIMARC'));
151         $biblio->metadata->store;
152
153         my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
154                     => { Accept => 'application/marcxml+xml' } )
155           ->status_is(200)->tx->res->body;
156
157         my $encoded_title  = Encode::encode( "UTF-8", $title_with_diacritics );
158
159         like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
160         t::lib::Mocks::mock_preference('marcflavour', $marcflavour);
161     };
162
163     $schema->storage->txn_rollback;
164 };
165
166 subtest 'get_items() tests' => sub {
167
168     plan tests => 11;
169
170     $schema->storage->txn_begin;
171
172     my $patron = $builder->build_object(
173         {
174             class => 'Koha::Patrons',
175             value => { flags => 0 }
176         }
177     );
178     my $password = 'thePassword123';
179     $patron->set_password( { password => $password, skip_validation => 1 } );
180     $patron->discard_changes;
181     my $userid = $patron->userid;
182
183     my $biblio = $builder->build_sample_biblio();
184     $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
185       ->status_is(403);
186
187     $patron->flags(4)->store;
188
189     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
190       ->status_is(200)
191       ->json_is( '' => [], 'No items on the biblio' );
192
193     my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
194     my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
195
196     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
197       ->status_is(200)
198       ->json_is( '' => [ $item_1->to_api, $item_2->to_api ], 'The items are returned' );
199
200     $t->get_ok(
201         "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items" => { "x-koha-embed" => "+strings" } )
202         ->status_is(200)
203         ->json_has( '/0/_strings/home_library_id/str' => $item_1->holding_branch->branchname, '_strings are embedded' );
204
205     $schema->storage->txn_rollback;
206 };
207
208 subtest 'delete() tests' => sub {
209
210     plan tests => 9;
211
212     $schema->storage->txn_begin;
213
214     my $patron = $builder->build_object(
215         {
216             class => 'Koha::Patrons',
217             value => { flags => 0 } # no permissions
218         }
219     );
220     my $password = 'thePassword123';
221     $patron->set_password( { password => $password, skip_validation => 1 } );
222     my $userid = $patron->userid;
223
224     my $item      = $builder->build_sample_item();
225     my $biblio_id = $item->biblionumber;
226
227     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
228       ->status_is(403, 'Not enough permissions makes it return the right code');
229
230     # Add permissions
231     $builder->build(
232         {
233             source => 'UserPermission',
234             value  => {
235                 borrowernumber => $patron->borrowernumber,
236                 module_bit     => 9,
237                 code           => 'edit_catalogue'
238             }
239         }
240     );
241
242
243     # Bibs with items cannot be deleted
244     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
245       ->status_is(409);
246
247     $item->delete();
248
249     # Bibs with no items can be deleted
250     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
251       ->status_is(204, 'SWAGGER3.2.4')
252       ->content_is('', 'SWAGGER3.3.4');
253
254     $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
255       ->status_is(404);
256
257     $schema->storage->txn_rollback;
258 };
259
260 subtest 'get_public() tests' => sub {
261
262     plan tests => 25;
263
264     $schema->storage->txn_begin;
265
266     my $category = $builder->build_object({ class => 'Koha::Patron::Categories' });
267     my $patron = $builder->build_object(
268         {
269             class => 'Koha::Patrons',
270             value => {
271                 flags        => undef, # opac user
272                 categorycode => $category->categorycode
273             }
274         }
275     );
276     my $password = 'thePassword123';
277     $patron->set_password( { password => $password, skip_validation => 1 } );
278     $patron->discard_changes;
279     my $userid = $patron->userid;
280
281     my $biblio = $builder->build_sample_biblio({
282         title  => 'The unbearable lightness of being',
283         author => 'Milan Kundera'
284     });
285
286     # Make sure author in shown in the OPAC
287     my $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
288     while ( my $subfield = $subfields->next ) {
289         $subfield->set({ hidden => -1 })->store;
290     }
291     Koha::Caches->get_instance()->flush_all;
292
293     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
294                 => { Accept => 'application/weird+format' } )
295       ->status_is(400);
296
297     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
298                  => { Accept => 'text/plain' } )
299       ->status_is(200)
300       ->content_like( qr{100\s+_aMilan Kundera} )
301       ->content_like( qr{245\s+_aThe unbearable lightness of being} );
302
303     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
304                  => { Accept => 'application/marcxml+xml' } )
305       ->status_is(200);
306
307     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
308                  => { Accept => 'application/marc-in-json' } )
309       ->status_is(200);
310
311     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
312                  => { Accept => 'application/marc' } )
313       ->status_is(200);
314
315     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
316                  => { Accept => 'text/plain' } )
317       ->status_is(200)
318       ->content_is($biblio->metadata->record->as_formatted);
319
320     subtest 'anonymous access' => sub {
321         plan tests => 9;
322
323         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
324                  => { Accept => 'application/marcxml+xml' } )
325           ->status_is(200);
326
327         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
328                     => { Accept => 'application/marc-in-json' } )
329         ->status_is(200);
330
331         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
332                     => { Accept => 'application/marc' } )
333         ->status_is(200);
334
335         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
336                     => { Accept => 'text/plain' } )
337         ->status_is(200)
338         ->content_is($biblio->metadata->record->as_formatted);
339     };
340
341     subtest 'marc-in-json encoding tests' => sub {
342
343         plan tests => 3;
344
345         my $title_with_diacritics = "L'insoutenable légèreté de l'être";
346
347         my $biblio = $builder->build_sample_biblio(
348             {
349                 title  => $title_with_diacritics,
350                 author => "Milan Kundera"
351             }
352         );
353
354         my $result = $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
355                     => { Accept => 'application/marc-in-json' } )
356           ->status_is(200)->tx->res->body;
357
358         my $encoded_title  = Encode::encode( "UTF-8", $title_with_diacritics );
359
360         like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
361     };
362
363     # Hide author in OPAC
364     $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
365     while ( my $subfield = $subfields->next ) {
366         $subfield->set({ hidden => 1 })->store;
367     }
368
369     Koha::Caches->get_instance()->flush_all;
370
371     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
372                  => { Accept => 'text/plain' } )
373       ->status_is(200)
374       ->content_unlike( qr{100\s+_aMilan Kundera} )
375       ->content_like( qr{245\s+_aThe unbearable lightness of being} );
376
377     subtest 'hidden_in_opac tests' => sub {
378
379         plan tests => 6;
380
381         my $biblio_hidden_in_opac = 1;
382
383         my $biblio_class = Test::MockModule->new('Koha::Biblio');
384         # force biblio hidden in OPAC
385         $biblio_class->mock( 'hidden_in_opac', sub { return $biblio_hidden_in_opac; } );
386
387         $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
388                  => { Accept => 'text/plain' } )
389           ->status_is(404, 'hidden_in_opac + anonymous => hidden');
390
391         my $category_override_hidden_items = 0;
392         my $category_class = Test::MockModule->new('Koha::Patron::Category');
393         $category_class->mock( 'override_hidden_items', sub { return $category_override_hidden_items; } );
394         $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
395                  => { Accept => 'text/plain' } )
396           ->status_is(404, "hidden_in_opac + patron whose category doesn't override => hidden");
397
398         # Make the category override
399         $category_override_hidden_items = 1;
400         $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
401                  => { Accept => 'text/plain' } )
402           ->status_is(200, "hidden_in_opac + patron whose category that overrides => displayed");
403
404         t::lib::Mocks::mock_preference('OpacHiddenItems');
405     };
406
407     $biblio->delete;
408     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
409                  => { Accept => 'application/marc' } )
410       ->status_is(404)
411       ->json_is( '/error', 'Object not found.' );
412
413     $schema->storage->txn_rollback;
414 };
415
416 subtest 'pickup_locations() tests' => sub {
417
418     plan tests => 16;
419
420     $schema->storage->txn_begin;
421
422     t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
423
424     # Small trick to ease testing
425     Koha::Libraries->search->update({ pickup_location => 0 });
426
427     my $library_1 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'A', pickup_location => 1 } });
428     my $library_2 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'B', pickup_location => 1 } });
429     my $library_3 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'C', pickup_location => 1 } });
430
431     my $library_1_api = $library_1->to_api();
432     my $library_2_api = $library_2->to_api();
433     my $library_3_api = $library_3->to_api();
434
435     $library_1_api->{needs_override} = Mojo::JSON->false;
436     $library_2_api->{needs_override} = Mojo::JSON->false;
437     $library_3_api->{needs_override} = Mojo::JSON->true;
438
439     my $patron = $builder->build_object(
440         {
441             class => 'Koha::Patrons',
442             value => { userid => 'tomasito', flags => 0 }
443         }
444     );
445     my $password = 'thePassword123';
446     $patron->set_password( { password => $password, skip_validation => 1 } );
447     my $userid = $patron->userid;
448     $builder->build(
449         {
450             source => 'UserPermission',
451             value  => {
452                 borrowernumber => $patron->borrowernumber,
453                 module_bit     => 6,
454                 code           => 'place_holds',
455             },
456         }
457     );
458
459     my $biblio_class = Test::MockModule->new('Koha::Biblio');
460     $biblio_class->mock(
461         'pickup_locations',
462         sub {
463             my ( $self, $params ) = @_;
464             my $mock_patron = $params->{patron};
465             is( $mock_patron->borrowernumber,
466                 $patron->borrowernumber, 'Patron passed correctly' );
467             return Koha::Libraries->search(
468                 {
469                     branchcode => {
470                         '-in' => [
471                             $library_1->branchcode,
472                             $library_2->branchcode
473                         ]
474                     }
475                 },
476                 {   # we make sure no surprises in the order of the result
477                     order_by => { '-asc' => 'marcorgcode' }
478                 }
479             );
480         }
481     );
482
483     my $biblio = $builder->build_sample_biblio;
484
485     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
486           . $biblio->id
487           . "/pickup_locations?patron_id=" . $patron->id )
488       ->json_is( [ $library_1_api, $library_2_api ] );
489
490     # filtering works!
491     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
492           . $biblio->id
493           . '/pickup_locations?'
494           . 'patron_id=' . $patron->id . '&q={"marc_org_code": { "-like": "A%" }}' )
495       ->json_is( [ $library_1_api ] );
496
497     t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
498
499     my $library_4 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 0, marcorgcode => 'X' } });
500     my $library_5 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 1, marcorgcode => 'Y' } });
501
502     my $library_5_api = $library_5->to_api();
503     $library_5_api->{needs_override} = Mojo::JSON->true;
504
505     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
506           . $biblio->id
507           . "/pickup_locations?"
508           . "patron_id=" . $patron->id . "&_order_by=marc_org_code" )
509       ->json_is( [ $library_1_api, $library_2_api, $library_3_api, $library_5_api ] );
510
511     subtest 'Pagination and AllowHoldPolicyOverride tests' => sub {
512
513         plan tests => 27;
514
515         t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
516
517         $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
518           ->json_is( [$library_1_api] )
519           ->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
520           ->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
521           ->header_unlike( 'Link', qr|rel="prev"| )
522           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
523           ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
524           ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
525
526         $t->get_ok( "//$userid:$password@/api/v1/biblios/"
527               . $biblio->id
528               . "/pickup_locations?"
529               . "patron_id="
530               . $patron->id
531               . "&_order_by=marc_org_code"
532               . "&_per_page=1&_page=3" )    # force the needs_override=1 check
533           ->json_is( [$library_3_api] )
534           ->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
535           ->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
536           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="prev"# )
537           ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1.*)>\; rel="next"# )
538           ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
539           ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
540
541         t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
542
543         $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
544           ->json_is( [$library_1_api] )
545           ->header_is( 'X-Total-Count', '2' )
546           ->header_is( 'X-Base-Total-Count', '2' )
547           ->header_unlike( 'Link', qr|rel="prev"| )
548           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
549           ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
550           ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1).*>\; rel="last"# );
551     };
552
553     my $deleted_patron = $builder->build_object({ class => 'Koha::Patrons' });
554     my $deleted_patron_id = $deleted_patron->id;
555     $deleted_patron->delete;
556
557     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
558           . $biblio->id
559           . "/pickup_locations?"
560           . "patron_id=" . $deleted_patron_id )
561       ->status_is( 400 )
562       ->json_is( '/error' => 'Patron not found' );
563
564     $biblio->delete;
565
566     $t->get_ok( "//$userid:$password@/api/v1/biblios/"
567           . $biblio->id
568           . "/pickup_locations?"
569           . "patron_id=" . $patron->id )
570       ->status_is( 404 )
571       ->json_is( '/error' => 'Biblio not found' );
572
573     $schema->storage->txn_rollback;
574 };
575
576 subtest 'get_items_public() tests' => sub {
577
578     plan tests => 15;
579
580     $schema->storage->txn_begin;
581
582     my $override_hidden_items = 0;
583
584     my $mocked_category = Test::MockModule->new('Koha::Patron::Category');
585     $mocked_category->mock(
586         'override_hidden_items',
587         sub {
588             return $override_hidden_items;
589         }
590     );
591
592     my $rules = undef;
593
594     my $mocked_context = Test::MockModule->new('C4::Context');
595     $mocked_context->mock(
596         'yaml_preference',
597         sub {
598             return $rules;
599         }
600     );
601
602     my $patron   = $builder->build_object( { class => 'Koha::Patrons' } );
603     my $password = 'thePassword123';
604     $patron->set_password( { password => $password, skip_validation => 1 } );
605     $patron->discard_changes;
606     my $userid = $patron->userid;
607
608     my $biblio = $builder->build_sample_biblio();
609
610     $t->get_ok(
611         "//$userid:$password@/api/v1/public/biblios/" . $biblio->id . "/items" )
612       ->status_is(200)->json_is( '' => [], 'No items on the biblio' );
613
614     my $item_1 = $builder->build_sample_item( { biblionumber => $biblio->id } );
615     my $item_2 = $builder->build_sample_item(
616         { biblionumber => $biblio->id, withdrawn => 1 } );
617
618     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
619           . $biblio->biblionumber
620           . "/items" )->status_is(200)->json_is(
621         '' => [
622             $item_1->to_api( { public => 1 } ),
623             $item_2->to_api( { public => 1 } )
624         ],
625         'The items are returned'
626           );
627
628     $rules = { withdrawn => ['1'] };
629
630     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
631           . $biblio->biblionumber
632           . "/items" )->status_is(200)->json_is(
633         '' => [ $item_1->to_api( { public => 1 } ) ],
634         'The items are returned, hidden one is not returned'
635           );
636
637     $t->get_ok( "/api/v1/public/biblios/"
638           . $biblio->biblionumber
639           . "/items" )->status_is(200)->json_is(
640         '' => [ $item_1->to_api( { public => 1 } ) ],
641         'Anonymous user, items are returned, hidden one is not returned'
642           );
643
644
645     $override_hidden_items = 1;
646
647     $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
648           . $biblio->biblionumber
649           . "/items" )->status_is(200)->json_is(
650         '' => [
651             $item_1->to_api( { public => 1 } ),
652             $item_2->to_api( { public => 1 } )
653         ],
654         'The items are returned, the patron category has an override'
655           );
656
657     $schema->storage->txn_rollback;
658 };
659
660 subtest 'get_bookings() tests' => sub {
661
662     plan tests => 8;
663
664     $schema->storage->txn_begin;
665
666     my $librarian = $builder->build_object(
667         {
668             class => 'Koha::Patrons',
669             value => { flags => 0 }     # no additional permissions
670         }
671     );
672     $builder->build(
673         {
674             source => 'UserPermission',
675             value  => {
676                 borrowernumber => $librarian->borrowernumber,
677                 module_bit     => 1,
678                 code           => 'manage_bookings',
679             },
680         }
681     );
682     my $password = 'thePassword123';
683     $librarian->set_password( { password => $password, skip_validation => 1 } );
684     my $userid = $librarian->userid;
685
686     my $patron = $builder->build_object(
687         {
688             class => 'Koha::Patrons',
689             value => { flags => 0 }
690         }
691     );
692     $patron->set_password( { password => $password, skip_validation => 1 } );
693     my $unauth_userid = $patron->userid;
694
695     my $biblio = $builder->build_sample_biblio();
696     my $item1  = $builder->build_sample_item( { bookable => 1, biblionumber => $biblio->id } );
697     my $item2  = $builder->build_sample_item( { bookable => 1, biblionumber => $biblio->id } );
698
699     $t->get_ok( "//$unauth_userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(403);
700
701     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(200)
702         ->json_is( '' => [], 'No bookings on the biblio' );
703
704     # One booking
705     my $start_0   = dt_from_string->subtract( days => 2 )->truncate( to => 'day' );
706     my $end_0     = dt_from_string->add( days => 4 )->truncate( to => 'day' );
707     my $booking_0 = $builder->build_object(
708         {
709             class => 'Koha::Bookings',
710             value => {
711                 biblio_id  => $biblio->id,
712                 item_id    => $item1->id,
713                 start_date => $start_0,
714                 end_date   => $end_0
715             }
716         }
717     );
718
719     my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(200)
720         ->tx->res->json;
721
722     is_deeply( $ret, [ $booking_0->to_api ] );
723
724     $schema->storage->txn_rollback;
725 };
726
727 subtest 'get_checkouts() tests' => sub {
728
729     plan tests => 17;
730
731     $schema->storage->txn_begin;
732
733     my $patron = $builder->build_object(
734         {
735             class => 'Koha::Patrons',
736             value => { flags => 0 }
737         }
738     );
739     my $password = 'thePassword123';
740     $patron->set_password( { password => $password, skip_validation => 1 } );
741     $patron->discard_changes;
742     my $userid = $patron->userid;
743
744     my $biblio = $builder->build_sample_biblio();
745     $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
746       ->status_is(403);
747
748     $builder->build(
749         {
750             source => 'UserPermission',
751             value  => {
752                 borrowernumber => $patron->borrowernumber,
753                 module_bit     => 1,
754                 code           => 'circulate_remaining_permissions',
755             },
756         }
757     );
758
759     $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts" )
760         ->status_is( 200, 'circulate_remaining_permissions allows checkouts access' )
761         ->json_is( '' => [], 'No checkouts on the biblio' );
762
763     my $bookings_librarian = $builder->build_object(
764         {
765             class => 'Koha::Patrons',
766             value => { flags => 0 }     # no additional permissions
767         }
768     );
769     $builder->build(
770         {
771             source => 'UserPermission',
772             value  => {
773                 borrowernumber => $bookings_librarian->borrowernumber,
774                 module_bit     => 1,
775                 code           => 'manage_bookings',
776             },
777         }
778     );
779     $bookings_librarian->set_password( { password => $password, skip_validation => 1 } );
780     my $bookings_userid = $bookings_librarian->userid;
781
782     $t->get_ok( "//$bookings_userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts" )
783         ->status_is( 200, 'manage_bookings allows checkouts access' )
784         ->json_is( '' => [], 'No checkouts on the biblio' );
785
786     my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
787     my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
788
789     AddIssue( $patron, $item_1->barcode );
790     AddIssue( $patron, $item_2->barcode );
791
792     my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
793       ->status_is(200)
794       ->tx->res->json;
795
796     my $checkout_1 = Koha::Checkouts->find({ itemnumber => $item_1->id });
797     my $checkout_2 = Koha::Checkouts->find({ itemnumber => $item_2->id });
798
799     is_deeply( $ret, [ $checkout_1->to_api, $checkout_2->to_api ] );
800
801     AddReturn( $item_1->barcode );
802
803     $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
804       ->status_is(200)
805       ->tx->res->json;
806
807     is_deeply( $ret, [ $checkout_2->to_api ] );
808
809     $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts?checked_in=1")
810       ->status_is(200)
811       ->tx->res->json;
812
813     my $old_checkout_1 = Koha::Old::Checkouts->find( $checkout_1->id );
814
815     is_deeply( $ret, [ $old_checkout_1->to_api ] );
816
817     $schema->storage->txn_rollback;
818 };
819
820 subtest 'set_rating() tests' => sub {
821
822     plan tests => 12;
823
824     $schema->storage->txn_begin;
825
826     my $patron = $builder->build_object(
827         {
828             class => 'Koha::Patrons',
829             value => { flags => 0 }
830         }
831     );
832     my $password = 'thePassword123';
833     $patron->set_password( { password => $password, skip_validation => 1 } );
834     $patron->discard_changes;
835     my $userid = $patron->userid;
836
837     my $biblio = $builder->build_sample_biblio();
838     $t->post_ok("/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
839       ->status_is(403);
840
841     $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
842       ->status_is(200)
843       ->json_is( '/rating', '3' )
844       ->json_is( '/average', '3' )
845       ->json_is( '/count', '1' );
846
847     $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => undef })
848       ->status_is(200)
849       ->json_is( '/rating', undef )
850       ->json_is( '/average', '0' )
851       ->json_is( '/count', '0' );
852
853     $schema->storage->txn_rollback;
854
855 };
856
857
858 subtest 'post() tests' => sub {
859
860     plan tests => 13;
861
862     $schema->storage->txn_begin;
863
864     my $patron = $builder->build_object(
865         {
866             class => 'Koha::Patrons',
867             value => { flags => 0 } # no permissions
868         }
869     );
870     my $password = 'thePassword123';
871     $patron->set_password( { password => $password, skip_validation => 1 } );
872     my $userid = $patron->userid;
873
874     my $frameworkcode = 'BKS';
875     my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
876     <record
877         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
878         xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
879         xmlns="http://www.loc.gov/MARC21/slim">
880
881       <leader>01102pam a2200289 a 7500</leader>
882       <controlfield tag="001">2504398</controlfield>
883       <controlfield tag="005">20200421093816.0</controlfield>
884       <controlfield tag="008">920610s1993    caub         s001 0 eng  </controlfield>
885       <datafield tag="010" ind1=" " ind2=" ">
886         <subfield code="a">   92021731 </subfield>
887       </datafield>
888       <datafield tag="020" ind1=" " ind2=" ">
889         <subfield code="a">05200784381 (Test marcxml)</subfield>
890       </datafield>
891       <datafield tag="020" ind1=" " ind2=" ">
892         <subfield code="a">05200784461 (Test marcxml)</subfield>
893       </datafield>
894       <datafield tag="040" ind1=" " ind2=" ">
895         <subfield code="a">DLC</subfield>
896         <subfield code="c">DLC</subfield>
897         <subfield code="d">DLC</subfield>
898       </datafield>
899       <datafield tag="041" ind1="0" ind2=" ">
900         <subfield code="a">enggrc</subfield>
901       </datafield>
902       <datafield tag="050" ind1="0" ind2="0">
903         <subfield code="a">PA522</subfield>
904         <subfield code="b">.M38 1993</subfield>
905       </datafield>
906       <datafield tag="082" ind1="0" ind2="0">
907         <subfield code="a">480</subfield>
908         <subfield code="2">20</subfield>
909       </datafield>
910       <datafield tag="100" ind1="1" ind2=" ">
911         <subfield code="a">Mastronarde, Donald J.</subfield>
912         <subfield code="9">389</subfield>
913       </datafield>
914       <datafield tag="245" ind1="1" ind2="0">
915         <subfield code="a">Introduction to Attic Greek (Using marcxml) /</subfield>
916         <subfield code="c">Donald J. Mastronarde.</subfield>
917       </datafield>
918       <datafield tag="260" ind1=" " ind2=" ">
919         <subfield code="a">Berkeley :</subfield>
920         <subfield code="b">University of California Press,</subfield>
921         <subfield code="c">c1993.</subfield>
922       </datafield>
923       <datafield tag="300" ind1=" " ind2=" ">
924         <subfield code="a">ix, 425 p. :</subfield>
925         <subfield code="b">maps ;</subfield>
926         <subfield code="c">26 cm.</subfield>
927       </datafield>
928       <datafield tag="500" ind1=" " ind2=" ">
929         <subfield code="a">Includes index.</subfield>
930       </datafield>
931       <datafield tag="650" ind1=" " ind2="0">
932         <subfield code="a">Attic Greek dialect</subfield>
933         <subfield code="9">7</subfield>
934       </datafield>
935       <datafield tag="856" ind1="4" ind2="2">
936         <subfield code="3">Contributor biographical information</subfield>
937         <subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
938       </datafield>
939       <datafield tag="856" ind1="4" ind2="2">
940         <subfield code="3">Publisher description</subfield>
941         <subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
942       </datafield>
943       <datafield tag="906" ind1=" " ind2=" ">
944         <subfield code="a">7</subfield>
945         <subfield code="b">cbc</subfield>
946         <subfield code="c">orignew</subfield>
947         <subfield code="d">1</subfield>
948         <subfield code="e">ocip</subfield>
949         <subfield code="f">19</subfield>
950         <subfield code="g">y-gencatlg</subfield>
951       </datafield>
952       <datafield tag="942" ind1=" " ind2=" ">
953         <subfield code="2">ddc</subfield>
954         <subfield code="c">BK</subfield>
955       </datafield>
956       <datafield tag="955" ind1=" " ind2=" ">
957         <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>
958       </datafield>
959       <datafield tag="999" ind1=" " ind2=" ">
960         <subfield code="c">3</subfield>
961         <subfield code="d">3</subfield>
962       </datafield>
963     </record>|;
964
965     my $mij = q|{
966       "fields": [
967         {
968           "001": "2504398"
969         },
970         {
971           "005": "20200421093816.0"
972         },
973         {
974           "008": "920610s1993    caub         s001 0 eng  "
975         },
976         {
977           "010": {
978             "ind1": " ",
979             "subfields": [
980               {
981                 "a": "   92021731 "
982               }
983             ],
984             "ind2": " "
985           }
986         },
987         {
988           "020": {
989             "subfields": [
990               {
991                 "a": "05200784382 (Test mij)"
992               }
993             ],
994             "ind2": " ",
995             "ind1": " "
996           }
997         },
998         {
999           "020": {
1000             "subfields": [
1001               {
1002                 "a": "05200784462 (Test mij)"
1003               }
1004             ],
1005             "ind1": " ",
1006             "ind2": " "
1007           }
1008         },
1009         {
1010           "040": {
1011             "subfields": [
1012               {
1013                 "a": "DLC"
1014               },
1015               {
1016                 "c": "DLC"
1017               },
1018               {
1019                 "d": "DLC"
1020               }
1021             ],
1022             "ind2": " ",
1023             "ind1": " "
1024           }
1025         },
1026         {
1027           "041": {
1028             "ind2": " ",
1029             "subfields": [
1030               {
1031                 "a": "enggrc"
1032               }
1033             ],
1034             "ind1": "0"
1035           }
1036         },
1037         {
1038           "050": {
1039             "subfields": [
1040               {
1041                 "a": "PA522"
1042               },
1043               {
1044                 "b": ".M38 1993"
1045               }
1046             ],
1047             "ind1": "0",
1048             "ind2": "0"
1049           }
1050         },
1051         {
1052           "082": {
1053             "subfields": [
1054               {
1055                 "a": "480"
1056               },
1057               {
1058                 "2": "20"
1059               }
1060             ],
1061             "ind2": "0",
1062             "ind1": "0"
1063           }
1064         },
1065         {
1066           "100": {
1067             "ind2": " ",
1068             "subfields": [
1069               {
1070                 "a": "Mastronarde, Donald J."
1071               },
1072               {
1073                 "9": "389"
1074               }
1075             ],
1076             "ind1": "1"
1077           }
1078         },
1079         {
1080           "245": {
1081             "ind1": "1",
1082             "subfields": [
1083               {
1084                 "a": "Introduction to Attic Greek  (Using mij) /"
1085               },
1086               {
1087                 "c": "Donald J. Mastronarde."
1088               }
1089             ],
1090             "ind2": "0"
1091           }
1092         },
1093         {
1094           "260": {
1095             "subfields": [
1096               {
1097                 "a": "Berkeley :"
1098               },
1099               {
1100                 "b": "University of California Press,"
1101               },
1102               {
1103                 "c": "c1993."
1104               }
1105             ],
1106             "ind2": " ",
1107             "ind1": " "
1108           }
1109         },
1110         {
1111           "300": {
1112             "ind1": " ",
1113             "subfields": [
1114               {
1115                 "a": "ix, 425 p. :"
1116               },
1117               {
1118                 "b": "maps ;"
1119               },
1120               {
1121                 "c": "26 cm."
1122               }
1123             ],
1124             "ind2": " "
1125           }
1126         },
1127         {
1128           "500": {
1129             "subfields": [
1130               {
1131                 "a": "Includes index."
1132               }
1133             ],
1134             "ind1": " ",
1135             "ind2": " "
1136           }
1137         },
1138         {
1139           "650": {
1140             "subfields": [
1141               {
1142                 "a": "Attic Greek dialect"
1143               },
1144               {
1145                 "9": "7"
1146               }
1147             ],
1148             "ind2": "0",
1149             "ind1": " "
1150           }
1151         },
1152         {
1153           "856": {
1154             "subfields": [
1155               {
1156                 "3": "Contributor biographical information"
1157               },
1158               {
1159                 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1160               }
1161             ],
1162             "ind2": "2",
1163             "ind1": "4"
1164           }
1165         },
1166         {
1167           "856": {
1168             "ind1": "4",
1169             "subfields": [
1170               {
1171                 "3": "Publisher description"
1172               },
1173               {
1174                 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
1175               }
1176             ],
1177             "ind2": "2"
1178           }
1179         },
1180         {
1181           "906": {
1182             "subfields": [
1183               {
1184                 "a": "7"
1185               },
1186               {
1187                 "b": "cbc"
1188               },
1189               {
1190                 "c": "orignew"
1191               },
1192               {
1193                 "d": "1"
1194               },
1195               {
1196                 "e": "ocip"
1197               },
1198               {
1199                 "f": "19"
1200               },
1201               {
1202                 "g": "y-gencatlg"
1203               }
1204             ],
1205             "ind1": " ",
1206             "ind2": " "
1207           }
1208         },
1209         {
1210           "942": {
1211             "subfields": [
1212               {
1213                 "2": "ddc"
1214               },
1215               {
1216                 "c": "BK"
1217               }
1218             ],
1219             "ind2": " ",
1220             "ind1": " "
1221           }
1222         },
1223         {
1224           "955": {
1225             "subfields": [
1226               {
1227                 "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"
1228               }
1229             ],
1230             "ind2": " ",
1231             "ind1": " "
1232           }
1233         },
1234         {
1235           "999": {
1236             "subfields": [
1237               {
1238                 "c": "3"
1239               },
1240               {
1241                 "d": "3"
1242               }
1243             ],
1244             "ind1": " ",
1245             "ind2": " "
1246           }
1247         }
1248       ],
1249       "leader": "01102pam a2200289 a 8500"
1250     }|;
1251     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|;
1252
1253     $t->post_ok("//$userid:$password@/api/v1/biblios")
1254       ->status_is(403, 'Not enough permissions makes it return the right code');
1255
1256     # Add permissions
1257     $builder->build(
1258         {
1259             source => 'UserPermission',
1260             value  => {
1261                 borrowernumber => $patron->borrowernumber,
1262                 module_bit     => 9,
1263                 code           => 'edit_catalogue'
1264             }
1265         }
1266     );
1267
1268     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode, "x-record-schema" => 'INVALID'})
1269       ->status_is(400, 'Invalid header x-record-schema');
1270
1271     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
1272       ->status_is(200)
1273       ->json_has('/id');
1274
1275     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode, 'x-confirm-not-duplicate' => 1} => $mij)
1276       ->status_is(200)
1277       ->json_has('/id');
1278
1279     $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1280       ->status_is(200)
1281       ->json_has('/id');
1282
1283     $schema->storage->txn_rollback;
1284 };
1285
1286 subtest 'put() tests' => sub {
1287
1288     plan tests => 14;
1289
1290     $schema->storage->txn_begin;
1291
1292     my $patron = $builder->build_object(
1293         {
1294             class => 'Koha::Patrons',
1295             value => { flags => 0 } # no permissions
1296         }
1297     );
1298     my $password = 'thePassword123';
1299     $patron->set_password( { password => $password, skip_validation => 1 } );
1300     my $userid = $patron->userid;
1301
1302     my $frameworkcode = 'BKS';
1303     my $biblio = $builder->build_sample_biblio({frameworkcode => $frameworkcode});
1304
1305     my $biblionumber = $biblio->biblionumber;
1306
1307     my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
1308     <record
1309         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1310         xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
1311         xmlns="http://www.loc.gov/MARC21/slim">
1312
1313       <leader>01102pam a2200289 a 6500</leader>
1314       <controlfield tag="001">2504398</controlfield>
1315       <controlfield tag="005">20200421093816.0</controlfield>
1316       <controlfield tag="008">920610s1993    caub         s001 0 eng  </controlfield>
1317       <datafield tag="010" ind1=" " ind2=" ">
1318         <subfield code="a">   92021731 </subfield>
1319       </datafield>
1320       <datafield tag="020" ind1=" " ind2=" ">
1321         <subfield code="a">05200784384 (Test json)</subfield>
1322       </datafield>
1323       <datafield tag="020" ind1=" " ind2=" ">
1324         <subfield code="a">05200784464 (Test json)</subfield>
1325       </datafield>
1326       <datafield tag="040" ind1=" " ind2=" ">
1327         <subfield code="a">DLC</subfield>
1328         <subfield code="c">DLC</subfield>
1329         <subfield code="d">DLC</subfield>
1330       </datafield>
1331       <datafield tag="041" ind1="0" ind2=" ">
1332         <subfield code="a">enggrc</subfield>
1333       </datafield>
1334       <datafield tag="050" ind1="0" ind2="0">
1335         <subfield code="a">PA522</subfield>
1336         <subfield code="b">.M38 1993</subfield>
1337       </datafield>
1338       <datafield tag="082" ind1="0" ind2="0">
1339         <subfield code="a">480</subfield>
1340         <subfield code="2">20</subfield>
1341       </datafield>
1342       <datafield tag="100" ind1="1" ind2=" ">
1343         <subfield code="a">Mastronarde, Donald J.</subfield>
1344         <subfield code="9">389</subfield>
1345       </datafield>
1346       <datafield tag="245" ind1="1" ind2="0">
1347         <subfield code="a">Introduction to Attic Greek  (Using marcxml) /</subfield>
1348         <subfield code="c">Donald J. Mastronarde.</subfield>
1349       </datafield>
1350       <datafield tag="260" ind1=" " ind2=" ">
1351         <subfield code="a">Berkeley :</subfield>
1352         <subfield code="b">University of California Press,</subfield>
1353         <subfield code="c">c1993.</subfield>
1354       </datafield>
1355       <datafield tag="300" ind1=" " ind2=" ">
1356         <subfield code="a">ix, 425 p. :</subfield>
1357         <subfield code="b">maps ;</subfield>
1358         <subfield code="c">26 cm.</subfield>
1359       </datafield>
1360       <datafield tag="500" ind1=" " ind2=" ">
1361         <subfield code="a">Includes index.</subfield>
1362       </datafield>
1363       <datafield tag="650" ind1=" " ind2="0">
1364         <subfield code="a">Attic Greek dialect</subfield>
1365         <subfield code="9">7</subfield>
1366       </datafield>
1367       <datafield tag="856" ind1="4" ind2="2">
1368         <subfield code="3">Contributor biographical information</subfield>
1369         <subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
1370       </datafield>
1371       <datafield tag="856" ind1="4" ind2="2">
1372         <subfield code="3">Publisher description</subfield>
1373         <subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
1374       </datafield>
1375       <datafield tag="906" ind1=" " ind2=" ">
1376         <subfield code="a">7</subfield>
1377         <subfield code="b">cbc</subfield>
1378         <subfield code="c">orignew</subfield>
1379         <subfield code="d">1</subfield>
1380         <subfield code="e">ocip</subfield>
1381         <subfield code="f">19</subfield>
1382         <subfield code="g">y-gencatlg</subfield>
1383       </datafield>
1384       <datafield tag="942" ind1=" " ind2=" ">
1385         <subfield code="2">ddc</subfield>
1386         <subfield code="c">BK</subfield>
1387       </datafield>
1388       <datafield tag="955" ind1=" " ind2=" ">
1389         <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>
1390       </datafield>
1391       <datafield tag="999" ind1=" " ind2=" ">
1392         <subfield code="c">3</subfield>
1393         <subfield code="d">3</subfield>
1394       </datafield>
1395     </record>|;
1396
1397     my $mij = q|{
1398       "fields": [
1399         {
1400           "001": "2504398"
1401         },
1402         {
1403           "005": "20200421093816.0"
1404         },
1405         {
1406           "008": "920610s1993    caub         s001 0 eng  "
1407         },
1408         {
1409           "010": {
1410             "ind1": " ",
1411             "subfields": [
1412               {
1413                 "a": "   92021731 "
1414               }
1415             ],
1416             "ind2": " "
1417           }
1418         },
1419         {
1420           "020": {
1421             "subfields": [
1422               {
1423                 "a": "05200784382 (Test mij)"
1424               }
1425             ],
1426             "ind2": " ",
1427             "ind1": " "
1428           }
1429         },
1430         {
1431           "020": {
1432             "subfields": [
1433               {
1434                 "a": "05200784462 (Test mij)"
1435               }
1436             ],
1437             "ind1": " ",
1438             "ind2": " "
1439           }
1440         },
1441         {
1442           "040": {
1443             "subfields": [
1444               {
1445                 "a": "DLC"
1446               },
1447               {
1448                 "c": "DLC"
1449               },
1450               {
1451                 "d": "DLC"
1452               }
1453             ],
1454             "ind2": " ",
1455             "ind1": " "
1456           }
1457         },
1458         {
1459           "041": {
1460             "ind2": " ",
1461             "subfields": [
1462               {
1463                 "a": "enggrc"
1464               }
1465             ],
1466             "ind1": "0"
1467           }
1468         },
1469         {
1470           "050": {
1471             "subfields": [
1472               {
1473                 "a": "PA522"
1474               },
1475               {
1476                 "b": ".M38 1993"
1477               }
1478             ],
1479             "ind1": "0",
1480             "ind2": "0"
1481           }
1482         },
1483         {
1484           "082": {
1485             "subfields": [
1486               {
1487                 "a": "480"
1488               },
1489               {
1490                 "2": "20"
1491               }
1492             ],
1493             "ind2": "0",
1494             "ind1": "0"
1495           }
1496         },
1497         {
1498           "100": {
1499             "ind2": " ",
1500             "subfields": [
1501               {
1502                 "a": "Mastronarde, Donald J."
1503               },
1504               {
1505                 "9": "389"
1506               }
1507             ],
1508             "ind1": "1"
1509           }
1510         },
1511         {
1512           "245": {
1513             "ind1": "1",
1514             "subfields": [
1515               {
1516                 "a": "Introduction to Attic Greek  (Using mij) /"
1517               },
1518               {
1519                 "c": "Donald J. Mastronarde."
1520               }
1521             ],
1522             "ind2": "0"
1523           }
1524         },
1525         {
1526           "260": {
1527             "subfields": [
1528               {
1529                 "a": "Berkeley :"
1530               },
1531               {
1532                 "b": "University of California Press,"
1533               },
1534               {
1535                 "c": "c1993."
1536               }
1537             ],
1538             "ind2": " ",
1539             "ind1": " "
1540           }
1541         },
1542         {
1543           "300": {
1544             "ind1": " ",
1545             "subfields": [
1546               {
1547                 "a": "ix, 425 p. :"
1548               },
1549               {
1550                 "b": "maps ;"
1551               },
1552               {
1553                 "c": "26 cm."
1554               }
1555             ],
1556             "ind2": " "
1557           }
1558         },
1559         {
1560           "500": {
1561             "subfields": [
1562               {
1563                 "a": "Includes index."
1564               }
1565             ],
1566             "ind1": " ",
1567             "ind2": " "
1568           }
1569         },
1570         {
1571           "650": {
1572             "subfields": [
1573               {
1574                 "a": "Attic Greek dialect"
1575               },
1576               {
1577                 "9": "7"
1578               }
1579             ],
1580             "ind2": "0",
1581             "ind1": " "
1582           }
1583         },
1584         {
1585           "856": {
1586             "subfields": [
1587               {
1588                 "3": "Contributor biographical information"
1589               },
1590               {
1591                 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1592               }
1593             ],
1594             "ind2": "2",
1595             "ind1": "4"
1596           }
1597         },
1598         {
1599           "856": {
1600             "ind1": "4",
1601             "subfields": [
1602               {
1603                 "3": "Publisher description"
1604               },
1605               {
1606                 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
1607               }
1608             ],
1609             "ind2": "2"
1610           }
1611         },
1612         {
1613           "906": {
1614             "subfields": [
1615               {
1616                 "a": "7"
1617               },
1618               {
1619                 "b": "cbc"
1620               },
1621               {
1622                 "c": "orignew"
1623               },
1624               {
1625                 "d": "1"
1626               },
1627               {
1628                 "e": "ocip"
1629               },
1630               {
1631                 "f": "19"
1632               },
1633               {
1634                 "g": "y-gencatlg"
1635               }
1636             ],
1637             "ind1": " ",
1638             "ind2": " "
1639           }
1640         },
1641         {
1642           "942": {
1643             "subfields": [
1644               {
1645                 "2": "ddc"
1646               },
1647               {
1648                 "c": "BK"
1649               }
1650             ],
1651             "ind2": " ",
1652             "ind1": " "
1653           }
1654         },
1655         {
1656           "955": {
1657             "subfields": [
1658               {
1659                 "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"
1660               }
1661             ],
1662             "ind2": " ",
1663             "ind1": " "
1664           }
1665         },
1666         {
1667           "999": {
1668             "subfields": [
1669               {
1670                 "c": "3"
1671               },
1672               {
1673                 "d": "3"
1674               }
1675             ],
1676             "ind1": " ",
1677             "ind2": " "
1678           }
1679         }
1680       ],
1681       "leader": "01102pam a2200289 a 8500"
1682     }|;
1683     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|;
1684
1685     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber")
1686       ->status_is(403, 'Not enough permissions makes it return the right code');
1687
1688     # Add permissions
1689     $builder->build(
1690         {
1691             source => 'UserPermission',
1692             value  => {
1693                 borrowernumber => $patron->borrowernumber,
1694                 module_bit     => 9,
1695                 code           => 'edit_catalogue'
1696             }
1697         }
1698     );
1699
1700     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
1701       ->status_is(200)
1702       ->json_has('/id');
1703
1704     $biblio = Koha::Biblios->find($biblionumber);
1705
1706     is($biblio->title, 'Introduction to Attic Greek  (Using marcxml) /');
1707
1708     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode} => $mij)
1709       ->status_is(200)
1710       ->json_has('/id');
1711
1712     $biblio = Koha::Biblios->find($biblionumber);
1713
1714     is($biblio->title, 'Introduction to Attic Greek  (Using mij) /');
1715
1716     $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1717       ->status_is(200)
1718       ->json_has('/id');
1719
1720     $biblio = Koha::Biblios->find($biblionumber);
1721
1722     is($biblio->title, 'Introduction to Attic Greek  (Using usmarc) /');
1723
1724     $schema->storage->txn_rollback;
1725 };
1726
1727 subtest 'list() tests' => sub {
1728
1729     plan tests => 17;
1730
1731     $schema->storage->txn_begin;
1732
1733     my $patron = $builder->build_object(
1734         {
1735             class => 'Koha::Patrons',
1736             value => { flags => 0 }
1737         }
1738     );
1739     my $password = 'thePassword123';
1740     $patron->set_password( { password => $password, skip_validation => 1 } );
1741     $patron->discard_changes;
1742     my $userid = $patron->userid;
1743
1744     t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
1745
1746     my $title_with_diacritics = "L'insoutenable légèreté de l'être";
1747     my $biblio = $builder->build_sample_biblio(
1748         {
1749             title  => $title_with_diacritics,
1750             author => "Milan Kundera"
1751         }
1752     );
1753
1754     my $record = $biblio->metadata->record;
1755     $record->leader('     nam         3  4500');
1756     $biblio->metadata->metadata($record->as_xml_record('UNIMARC'))->store;
1757
1758     my $biblio_id_1 = $biblio->id;
1759
1760     t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
1761     my $biblio_id_2 = $builder->build_sample_biblio->id;
1762
1763     my $query = encode_json( [ { biblio_id => $biblio_id_1 }, { biblio_id => $biblio_id_2 } ] );
1764
1765     $t->get_ok("//$userid:$password@/api/v1/biblios?q=$query")->status_is(403);
1766
1767     $patron->flags(4)->store;
1768
1769     $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/weird+format' } )
1770         ->status_is(400);
1771
1772     $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/json' } )->status_is(200);
1773
1774     my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marcxml+xml' } )
1775         ->status_is(200)->tx->res->body;
1776
1777     my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
1778     like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
1779
1780     $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marc-in-json' } )
1781         ->status_is(200);
1782
1783     $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marc' } )->status_is(200);
1784
1785     $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'text/plain' } )->status_is(200);
1786
1787     # DELETE any biblio with ISBN = TOMAS
1788     Koha::Biblios->search({ 'biblioitem.isbn' => 'TOMAS' }, { join => [ 'biblioitem' ] })
1789                  ->delete;
1790
1791
1792     my $isbn_query = encode_json({ isbn => 'TOMAS' });
1793     $biblio->biblioitem->set({ isbn => 'TOMAS' })->store;
1794     $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$isbn_query" =>
1795           { Accept => 'text/plain' } )
1796       ->status_is(200);
1797
1798     $schema->storage->txn_rollback;
1799 };
1800
1801 subtest 'add_item() tests' => sub {
1802   plan tests => 7;
1803
1804   $schema->storage->txn_begin;
1805
1806   my $patron = $builder->build_object(
1807       {
1808           class => 'Koha::Patrons',
1809           value => { flags => 0 }
1810       }
1811   );
1812   my $password = 'thePassword123';
1813   $patron->set_password( { password => $password, skip_validation => 1 } );
1814   my $userid = $patron->userid;
1815
1816   my $biblio = $builder->build_sample_biblio();
1817   my $biblio_id = $biblio->biblionumber;
1818
1819   my $barcode = 'mybarcode';
1820   my $matching_items = Koha::Items->search({ barcode => $barcode });
1821
1822   while (my $item = $matching_items->next) {
1823     $item->delete;
1824   }
1825
1826   $t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => { external_id => $barcode })
1827     ->status_is(403, 'Not enough permissions to create an item');
1828
1829   # Add permissions
1830   $builder->build(
1831       {
1832           source => 'UserPermission',
1833           value  => {
1834               borrowernumber => $patron->borrowernumber,
1835               module_bit     => 9,
1836               code           => 'edit_catalogue'
1837           }
1838       }
1839   );
1840
1841   $t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => {
1842       external_id => $barcode,
1843     })
1844     ->status_is(201, 'Item created')
1845     ->json_is('/biblio_id', $biblio_id);
1846
1847   my $item = $builder->build_sample_item();
1848
1849   $t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => {
1850       external_id => $item->barcode,
1851     })
1852     ->status_is(409, 'Duplicate barcode');
1853
1854   $schema->storage->txn_rollback;
1855 };
1856
1857 subtest 'update_item() tests' => sub {
1858   plan tests => 7;
1859
1860   $schema->storage->txn_begin;
1861
1862   my $patron = $builder->build_object(
1863       {
1864           class => 'Koha::Patrons',
1865           value => { flags => 0 }
1866       }
1867   );
1868   my $password = 'thePassword123';
1869   $patron->set_password( { password => $password, skip_validation => 1 } );
1870   my $userid = $patron->userid;
1871
1872   my $item = $builder->build_sample_item({ replacementprice => 5 });
1873   my $biblio_id = $item->biblionumber;
1874   my $item_id = $item->itemnumber;
1875
1876   my $biblio = Koha::Biblios->find($item->biblionumber);
1877
1878   my $matching_items = Koha::Items->search({ barcode => $item->barcode });
1879
1880   while (my $mbcitem = $matching_items->next) {
1881     $mbcitem->delete if $mbcitem->biblionumber != $item->biblionumber;
1882   }
1883
1884   $t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => { external_id => 'something' })
1885     ->status_is(403, 'Not enough permissions to update an item');
1886
1887   # Add permissions
1888   $builder->build(
1889       {
1890           source => 'UserPermission',
1891           value  => {
1892               borrowernumber => $patron->borrowernumber,
1893               module_bit     => 9,
1894               code           => 'edit_catalogue'
1895           }
1896       }
1897   );
1898
1899   my $other_item = $builder->build_sample_item();
1900
1901   $t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => {
1902       external_id => $other_item->barcode,
1903     })
1904     ->status_is(409, 'Barcode not unique');
1905
1906   $t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => {
1907       replacement_price => 30,
1908     })
1909     ->status_is(200, 'Item updated')
1910     ->json_is('/replacement_price', 30);
1911
1912   $schema->storage->txn_rollback;
1913 };
1914
1915 subtest 'merge() tests' => sub {
1916     plan tests => 12;
1917     $schema->storage->txn_begin;
1918
1919     my $mij_rec = q|{
1920       "fields": [
1921         {
1922           "001": "2504398"
1923         },
1924         {
1925           "005": "20200421093816.0"
1926         },
1927         {
1928           "008": "920610s1993    caub         s001 0 eng  "
1929         },
1930         {
1931           "010": {
1932             "ind1": " ",
1933             "subfields": [
1934               {
1935                 "a": "   92021731 "
1936               }
1937             ],
1938             "ind2": " "
1939           }
1940         },
1941         {
1942           "020": {
1943             "subfields": [
1944               {
1945                 "a": "05200784462 (Test mij)"
1946               }
1947             ],
1948             "ind1": " ",
1949             "ind2": " "
1950           }
1951         },
1952         {
1953           "040": {
1954             "subfields": [
1955               {
1956                 "a": "DLC"
1957               },
1958               {
1959                 "c": "DLC"
1960               },
1961               {
1962                 "d": "DLC"
1963               }
1964             ],
1965             "ind2": " ",
1966             "ind1": " "
1967           }
1968         },
1969         {
1970           "041": {
1971             "ind2": " ",
1972             "subfields": [
1973               {
1974                 "a": "enggrc"
1975               }
1976             ],
1977             "ind1": "0"
1978           }
1979         },
1980         {
1981           "082": {
1982             "subfields": [
1983               {
1984                 "a": "480"
1985               },
1986               {
1987                 "2": "20"
1988               }
1989             ],
1990             "ind2": "0",
1991             "ind1": "0"
1992           }
1993         },
1994         {
1995           "100": {
1996             "ind2": " ",
1997             "subfields": [
1998               {
1999                 "a": "Mastronarde, Donald J."
2000               },
2001               {
2002                 "9": "389"
2003               }
2004             ],
2005             "ind1": "1"
2006           }
2007         },
2008         {
2009           "245": {
2010             "ind1": "1",
2011             "subfields": [
2012               {
2013                 "a": "Introduction to Attic Greek  (Using mij) /"
2014               },
2015               {
2016                 "c": "Donald J. Mastronarde."
2017               }
2018             ],
2019             "ind2": "0"
2020           }
2021         },
2022         {
2023           "260": {
2024             "subfields": [
2025               {
2026                 "a": "Berkeley :"
2027               },
2028               {
2029                 "b": "University of California Press,"
2030               },
2031               {
2032                 "c": "c1993."
2033               }
2034             ],
2035             "ind2": " ",
2036             "ind1": " "
2037           }
2038         },
2039         {
2040           "300": {
2041             "ind1": " ",
2042             "subfields": [
2043               {
2044                 "a": "ix, 425 p. :"
2045               },
2046               {
2047                 "b": "maps ;"
2048               },
2049               {
2050                 "c": "26 cm."
2051               }
2052             ],
2053             "ind2": " "
2054           }
2055         },
2056         {
2057           "650": {
2058             "subfields": [
2059               {
2060                 "a": "Attic Greek dialect"
2061               },
2062               {
2063                 "9": "7"
2064               }
2065             ],
2066             "ind2": "0",
2067             "ind1": " "
2068           }
2069         },
2070         {
2071           "942": {
2072             "subfields": [
2073               {
2074                 "2": "ddc"
2075               },
2076               {
2077                 "c": "BK"
2078               }
2079             ],
2080             "ind2": " ",
2081             "ind1": " "
2082           }
2083         },
2084         {
2085           "955": {
2086             "subfields": [
2087               {
2088                 "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"
2089               }
2090             ],
2091             "ind2": " ",
2092             "ind1": " "
2093           }
2094         },
2095         {
2096           "999": {
2097             "subfields": [
2098               {
2099                 "c": "3"
2100               },
2101               {
2102                 "d": "3"
2103               }
2104             ],
2105             "ind1": " ",
2106             "ind2": " "
2107           }
2108         }
2109       ],
2110       "leader": "01102pam a2200289 a 8500"
2111     }|;
2112
2113     my $patron = $builder->build_object(
2114         {
2115             class => 'Koha::Patrons',
2116             value => { flags => 0 }
2117         }
2118     );
2119     my $password = 'thePassword123';
2120     $patron->set_password( { password => $password, skip_validation => 1 } );
2121     my $userid = $patron->userid;
2122
2123     my $title_1     = 'Title number 1';
2124     my $title_2     = 'Title number 2';
2125     my $biblio1     = $builder->build_sample_biblio( { title => $title_1 } );
2126     my $biblio2     = $builder->build_sample_biblio( { title => $title_2 } );
2127     my $biblio_id1  = $biblio1->biblionumber;
2128     my $biblio_id2  = $biblio2->biblionumber;
2129     my $json_input1 = '{ "biblio_id_to_merge": "' . $biblio_id2 . '" }';
2130
2131     $t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" =>
2132             { 'Content-Type' => 'application/json', 'Accept' => 'application/marc-in-json' } => $json_input1 )
2133         ->status_is( 403, 'Not enough permissions to merge two bib records' );
2134
2135     # Add permissions
2136     $patron->flags(516)->store;
2137
2138     $t->post_ok(
2139         "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" => { 'Content-Type' => 'application/weird+format' } =>
2140             $json_input1 )->status_is( 400, 'Not correct headers' );
2141
2142     my $result =
2143         $t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" =>
2144             { 'Content-Type' => 'application/json', 'Accept' => 'application/marc-in-json' } => $json_input1 )
2145         ->status_is(200)->tx->res->body;
2146     like( $result, qr/$title_1/, "Merged record has the correct title" );
2147     unlike( $result, qr/$title_2/, "Merged record doesn't have the wrong title" );
2148
2149     my $biblio3     = $builder->build_sample_biblio( { title => 'Title number 3' } );
2150     my $biblio_id3  = $biblio3->biblionumber;
2151     my $json_input2 = '{ "biblio_id_to_merge": "' . $biblio_id3 . '",
2152                          "rules": "override_ext",
2153                          "datarecord": ' . $mij_rec . ' }';
2154     $result =
2155         $t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" =>
2156             { 'Content-Type' => 'application/json', 'Accept' => 'application/marc-in-json' } => $json_input2 )
2157         ->status_is(200)->tx->res->body;
2158     like( $result, qr/Using mij/, "Update with Marc-in-json record" );
2159     unlike( $result, qr/$title_1/, "Change all record with dat in the 'datarecord' field" );
2160
2161     $schema->storage->txn_rollback;
2162     }