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