3 # This file is part of Koha.
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.
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.
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>.
23 use Test::More tests => 15;
29 use t::lib::TestBuilder;
31 use Mojo::JSON qw(encode_json);
34 use C4::Circulation qw( AddIssue AddReturn );
38 use Koha::DateUtils qw (dt_from_string output_pref);
40 use Koha::Old::Checkouts;
42 use Mojo::JSON qw(encode_json);
44 my $schema = Koha::Database->new->schema;
45 my $builder = t::lib::TestBuilder->new;
47 t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
49 my $t = Test::Mojo->new('Koha::REST::V1');
51 subtest 'get() tests' => sub {
55 $schema->storage->txn_begin;
57 my $patron = $builder->build_object(
59 class => 'Koha::Patrons',
60 value => { flags => 0 }
63 my $password = 'thePassword123';
64 $patron->set_password( { password => $password, skip_validation => 1 } );
65 $patron->discard_changes;
66 my $userid = $patron->userid;
68 my $biblio = $builder->build_sample_biblio({
69 title => 'The unbearable lightness of being',
70 author => 'Milan Kundera'
72 $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber)
75 $patron->flags(4)->store;
77 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
78 => { Accept => 'application/weird+format' } )
81 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
82 => { Accept => 'application/json' } )
84 ->json_is( '/title', 'The unbearable lightness of being' )
85 ->json_is( '/author', 'Milan Kundera' );
87 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
88 => { Accept => 'application/marcxml+xml' } )
91 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
92 => { Accept => 'application/marc-in-json' } )
95 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
96 => { Accept => 'application/marc' } )
99 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
100 => { Accept => 'text/plain' } )
102 ->content_is($biblio->metadata->record->as_formatted);
105 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
106 => { Accept => 'application/marc' } )
108 ->json_is( '/error', 'Bibliographic record not found' );
110 subtest 'marc-in-json encoding tests' => sub {
114 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
116 my $biblio = $builder->build_sample_biblio(
118 title => $title_with_diacritics,
119 author => "Milan Kundera"
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;
127 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
129 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
132 subtest 'marcxml encoding tests' => sub {
135 my $marcflavour = C4::Context->preference('marcflavour');
136 t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
139 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
141 my $biblio = $builder->build_sample_biblio(
143 title => $title_with_diacritics,
144 author => "Milan Kundera"
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;
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;
157 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
159 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
160 t::lib::Mocks::mock_preference('marcflavour', $marcflavour);
163 $schema->storage->txn_rollback;
166 subtest 'get_items() tests' => sub {
170 $schema->storage->txn_begin;
172 my $patron = $builder->build_object(
174 class => 'Koha::Patrons',
175 value => { flags => 0 }
178 my $password = 'thePassword123';
179 $patron->set_password( { password => $password, skip_validation => 1 } );
180 $patron->discard_changes;
181 my $userid = $patron->userid;
183 my $biblio = $builder->build_sample_biblio();
184 $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
187 $patron->flags(4)->store;
189 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
191 ->json_is( '' => [], 'No items on the biblio' );
193 my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
194 my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
196 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items?_order_by=item_id" )
198 ->json_is( '' => [ $item_1->to_api, $item_2->to_api ], 'The items are returned' );
201 "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items" => { "x-koha-embed" => "+strings" } )
203 ->json_has( '/0/_strings/home_library_id/str' => $item_1->holding_branch->branchname, '_strings are embedded' );
205 subtest 'first_hold+strings' => sub {
209 my $patron = $builder->build_object(
211 class => 'Koha::Patrons',
217 $patron->set_password( { password => $password, skip_validation => 1 } );
218 my $userid = $patron->userid;
220 $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1 } } );
222 my $item = $builder->build_sample_item;
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(
230 dt => dt_from_string()->add( days => "3" )->truncate( to => 'day' ), dateformat => 'iso',
235 $t->post_ok( "//$userid:$password@/api/v1/holds" => json => $data )->status_is(201)->json_has('/hold_id');
237 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
238 . $item->biblionumber
239 . "/items" => { "x-koha-embed" => "first_hold+strings" } )->status_is(200)
241 '/0/first_hold/_strings/pickup_library_id' => { type => 'library', str => $pickup_library->branchname } );
244 $schema->storage->txn_rollback;
247 subtest 'delete() tests' => sub {
251 $schema->storage->txn_begin;
253 my $patron = $builder->build_object(
255 class => 'Koha::Patrons',
256 value => { flags => 0 } # no permissions
259 my $password = 'thePassword123';
260 $patron->set_password( { password => $password, skip_validation => 1 } );
261 my $userid = $patron->userid;
263 my $item = $builder->build_sample_item();
264 my $biblio_id = $item->biblionumber;
266 $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
267 ->status_is(403, 'Not enough permissions makes it return the right code');
272 source => 'UserPermission',
274 borrowernumber => $patron->borrowernumber,
276 code => 'edit_catalogue'
282 # Bibs with items cannot be deleted
283 $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
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');
293 $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
296 $schema->storage->txn_rollback;
299 subtest 'get_public() tests' => sub {
303 $schema->storage->txn_begin;
305 my $category = $builder->build_object({ class => 'Koha::Patron::Categories' });
306 my $patron = $builder->build_object(
308 class => 'Koha::Patrons',
310 flags => undef, # opac user
311 categorycode => $category->categorycode
315 my $password = 'thePassword123';
316 $patron->set_password( { password => $password, skip_validation => 1 } );
317 $patron->discard_changes;
318 my $userid = $patron->userid;
320 my $biblio = $builder->build_sample_biblio({
321 title => 'The unbearable lightness of being',
322 author => 'Milan Kundera'
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;
330 Koha::Caches->get_instance()->flush_all;
332 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
333 => { Accept => 'application/weird+format' } )
336 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
337 => { Accept => 'text/plain' } )
339 ->content_like( qr{100\s+_aMilan Kundera} )
340 ->content_like( qr{245\s+_aThe unbearable lightness of being} );
342 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
343 => { Accept => 'application/marcxml+xml' } )
346 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
347 => { Accept => 'application/marc-in-json' } )
350 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
351 => { Accept => 'application/marc' } )
354 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
355 => { Accept => 'text/plain' } )
357 ->content_is($biblio->metadata->record->as_formatted);
359 subtest 'anonymous access' => sub {
362 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
363 => { Accept => 'application/marcxml+xml' } )
366 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
367 => { Accept => 'application/marc-in-json' } )
370 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
371 => { Accept => 'application/marc' } )
374 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
375 => { Accept => 'text/plain' } )
377 ->content_is($biblio->metadata->record->as_formatted);
380 subtest 'marc-in-json encoding tests' => sub {
384 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
386 my $biblio = $builder->build_sample_biblio(
388 title => $title_with_diacritics,
389 author => "Milan Kundera"
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;
397 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
399 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
402 # Hide author in OPAC
403 $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
404 while ( my $subfield = $subfields->next ) {
405 $subfield->set({ hidden => 1 })->store;
408 Koha::Caches->get_instance()->flush_all;
410 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
411 => { Accept => 'text/plain' } )
413 ->content_unlike( qr{100\s+_aMilan Kundera} )
414 ->content_like( qr{245\s+_aThe unbearable lightness of being} );
416 subtest 'hidden_in_opac tests' => sub {
420 my $biblio_hidden_in_opac = 1;
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; } );
426 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
427 => { Accept => 'text/plain' } )
428 ->status_is(404, 'hidden_in_opac + anonymous => hidden');
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");
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");
443 t::lib::Mocks::mock_preference('OpacHiddenItems');
447 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
448 => { Accept => 'application/marc' } )
450 ->json_is( '/error', 'Bibliographic record not found' );
452 $schema->storage->txn_rollback;
455 subtest 'pickup_locations() tests' => sub {
459 $schema->storage->txn_begin;
461 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
463 # Small trick to ease testing
464 Koha::Libraries->search->update({ pickup_location => 0 });
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 } });
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();
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;
478 my $patron = $builder->build_object(
480 class => 'Koha::Patrons',
481 value => { userid => 'tomasito', flags => 0 }
484 my $password = 'thePassword123';
485 $patron->set_password( { password => $password, skip_validation => 1 } );
486 my $userid = $patron->userid;
489 source => 'UserPermission',
491 borrowernumber => $patron->borrowernumber,
493 code => 'place_holds',
498 my $biblio_class = Test::MockModule->new('Koha::Biblio');
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(
510 $library_1->branchcode,
511 $library_2->branchcode
515 { # we make sure no surprises in the order of the result
516 order_by => { '-asc' => 'marcorgcode' }
522 my $biblio = $builder->build_sample_biblio;
524 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
526 . "/pickup_locations?patron_id=" . $patron->id )
527 ->json_is( [ $library_1_api, $library_2_api ] );
530 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
532 . '/pickup_locations?'
533 . 'patron_id=' . $patron->id . '&q={"marc_org_code": { "-like": "A%" }}' )
534 ->json_is( [ $library_1_api ] );
536 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
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' } });
541 my $library_5_api = $library_5->to_api();
542 $library_5_api->{needs_override} = Mojo::JSON->true;
544 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
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 ] );
550 subtest 'Pagination and AllowHoldPolicyOverride tests' => sub {
554 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
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"# );
565 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
567 . "/pickup_locations?"
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"# );
580 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
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"# );
592 my $deleted_patron = $builder->build_object({ class => 'Koha::Patrons' });
593 my $deleted_patron_id = $deleted_patron->id;
594 $deleted_patron->delete;
596 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
598 . "/pickup_locations?"
599 . "patron_id=" . $deleted_patron_id )
601 ->json_is( '/error' => 'Patron not found' );
605 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
607 . "/pickup_locations?"
608 . "patron_id=" . $patron->id )
610 ->json_is( '/error' => 'Bibliographic record not found' );
612 $schema->storage->txn_rollback;
615 subtest 'get_items_public() tests' => sub {
619 $schema->storage->txn_begin;
621 my $override_hidden_items = 0;
623 my $mocked_category = Test::MockModule->new('Koha::Patron::Category');
624 $mocked_category->mock(
625 'override_hidden_items',
627 return $override_hidden_items;
633 my $mocked_context = Test::MockModule->new('C4::Context');
634 $mocked_context->mock(
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;
647 my $biblio = $builder->build_sample_biblio();
650 "//$userid:$password@/api/v1/public/biblios/" . $biblio->id . "/items" )
651 ->status_is(200)->json_is( '' => [], 'No items on the biblio' );
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 } );
657 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
658 . $biblio->biblionumber
659 . "/items" )->status_is(200)->json_is(
661 $item_1->to_api( { public => 1 } ),
662 $item_2->to_api( { public => 1 } )
664 'The items are returned'
667 $rules = { withdrawn => ['1'] };
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'
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'
684 $override_hidden_items = 1;
686 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
687 . $biblio->biblionumber
688 . "/items" )->status_is(200)->json_is(
690 $item_1->to_api( { public => 1 } ),
691 $item_2->to_api( { public => 1 } )
693 'The items are returned, the patron category has an override'
696 $schema->storage->txn_rollback;
699 subtest 'get_bookings() tests' => sub {
703 $schema->storage->txn_begin;
705 my $librarian = $builder->build_object(
707 class => 'Koha::Patrons',
708 value => { flags => 0 } # no additional permissions
713 source => 'UserPermission',
715 borrowernumber => $librarian->borrowernumber,
717 code => 'manage_bookings',
721 my $password = 'thePassword123';
722 $librarian->set_password( { password => $password, skip_validation => 1 } );
723 my $userid = $librarian->userid;
725 my $patron = $builder->build_object(
727 class => 'Koha::Patrons',
728 value => { flags => 0 }
731 $patron->set_password( { password => $password, skip_validation => 1 } );
732 my $unauth_userid = $patron->userid;
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 } );
738 $t->get_ok( "//$unauth_userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(403);
740 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(200)
741 ->json_is( '' => [], 'No bookings on the biblio' );
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(
748 class => 'Koha::Bookings',
750 biblio_id => $biblio->id,
751 item_id => $item1->id,
752 start_date => $start_0,
758 my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(200)
761 is_deeply( $ret, [ $booking_0->to_api ] );
763 $schema->storage->txn_rollback;
766 subtest 'get_checkouts() tests' => sub {
770 $schema->storage->txn_begin;
772 my $patron = $builder->build_object(
774 class => 'Koha::Patrons',
775 value => { flags => 0 }
778 my $password = 'thePassword123';
779 $patron->set_password( { password => $password, skip_validation => 1 } );
780 $patron->discard_changes;
781 my $userid = $patron->userid;
783 my $biblio = $builder->build_sample_biblio();
784 $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
789 source => 'UserPermission',
791 borrowernumber => $patron->borrowernumber,
793 code => 'circulate_remaining_permissions',
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' );
802 my $bookings_librarian = $builder->build_object(
804 class => 'Koha::Patrons',
805 value => { flags => 0 } # no additional permissions
810 source => 'UserPermission',
812 borrowernumber => $bookings_librarian->borrowernumber,
814 code => 'manage_bookings',
818 $bookings_librarian->set_password( { password => $password, skip_validation => 1 } );
819 my $bookings_userid = $bookings_librarian->userid;
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' );
825 my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
826 my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
828 AddIssue( $patron, $item_1->barcode );
829 AddIssue( $patron, $item_2->barcode );
831 my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
835 my $checkout_1 = Koha::Checkouts->find({ itemnumber => $item_1->id });
836 my $checkout_2 = Koha::Checkouts->find({ itemnumber => $item_2->id });
838 is_deeply( $ret, [ $checkout_1->to_api, $checkout_2->to_api ] );
840 AddReturn( $item_1->barcode );
842 $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
846 is_deeply( $ret, [ $checkout_2->to_api ] );
848 $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts?checked_in=1")
852 my $old_checkout_1 = Koha::Old::Checkouts->find( $checkout_1->id );
854 is_deeply( $ret, [ $old_checkout_1->to_api ] );
856 $schema->storage->txn_rollback;
859 subtest 'set_rating() tests' => sub {
863 $schema->storage->txn_begin;
865 my $patron = $builder->build_object(
867 class => 'Koha::Patrons',
868 value => { flags => 0 }
871 my $password = 'thePassword123';
872 $patron->set_password( { password => $password, skip_validation => 1 } );
873 $patron->discard_changes;
874 my $userid = $patron->userid;
876 my $biblio = $builder->build_sample_biblio();
877 $t->post_ok("/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
880 $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
882 ->json_is( '/rating', '3' )
883 ->json_is( '/average', '3' )
884 ->json_is( '/count', '1' );
886 $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => undef })
888 ->json_is( '/rating', undef )
889 ->json_is( '/average', '0' )
890 ->json_is( '/count', '0' );
892 $schema->storage->txn_rollback;
897 subtest 'post() tests' => sub {
901 $schema->storage->txn_begin;
903 my $patron = $builder->build_object(
905 class => 'Koha::Patrons',
906 value => { flags => 0 } # no permissions
909 my $password = 'thePassword123';
910 $patron->set_password( { password => $password, skip_validation => 1 } );
911 my $userid = $patron->userid;
913 my $frameworkcode = 'BKS';
914 my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
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">
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>
927 <datafield tag="020" ind1=" " ind2=" ">
928 <subfield code="a">05200784381 (Test marcxml)</subfield>
930 <datafield tag="020" ind1=" " ind2=" ">
931 <subfield code="a">05200784461 (Test marcxml)</subfield>
933 <datafield tag="040" ind1=" " ind2=" ">
934 <subfield code="a">DLC</subfield>
935 <subfield code="c">DLC</subfield>
936 <subfield code="d">DLC</subfield>
938 <datafield tag="041" ind1="0" ind2=" ">
939 <subfield code="a">enggrc</subfield>
941 <datafield tag="050" ind1="0" ind2="0">
942 <subfield code="a">PA522</subfield>
943 <subfield code="b">.M38 1993</subfield>
945 <datafield tag="082" ind1="0" ind2="0">
946 <subfield code="a">480</subfield>
947 <subfield code="2">20</subfield>
949 <datafield tag="100" ind1="1" ind2=" ">
950 <subfield code="a">Mastronarde, Donald J.</subfield>
951 <subfield code="9">389</subfield>
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>
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>
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>
967 <datafield tag="500" ind1=" " ind2=" ">
968 <subfield code="a">Includes index.</subfield>
970 <datafield tag="650" ind1=" " ind2="0">
971 <subfield code="a">Attic Greek dialect</subfield>
972 <subfield code="9">7</subfield>
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>
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>
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>
991 <datafield tag="942" ind1=" " ind2=" ">
992 <subfield code="2">ddc</subfield>
993 <subfield code="c">BK</subfield>
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>
998 <datafield tag="999" ind1=" " ind2=" ">
999 <subfield code="c">3</subfield>
1000 <subfield code="d">3</subfield>
1010 "005": "20200421093816.0"
1013 "008": "920610s1993 caub s001 0 eng "
1030 "a": "05200784382 (Test mij)"
1041 "a": "05200784462 (Test mij)"
1109 "a": "Mastronarde, Donald J."
1123 "a": "Introduction to Attic Greek (Using mij) /"
1126 "c": "Donald J. Mastronarde."
1139 "b": "University of California Press,"
1170 "a": "Includes index."
1181 "a": "Attic Greek dialect"
1195 "3": "Contributor biographical information"
1198 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1210 "3": "Publisher description"
1213 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
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"
1288 "leader": "01102pam a2200289 a 8500"
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
\1e0
\1faenggrc
\1e00
\1faPA522
\1fb.M38 1993
\1e00
\1fa480
\1f220
\1e1
\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|;
1292 $t->post_ok("//$userid:$password@/api/v1/biblios")
1293 ->status_is(403, 'Not enough permissions makes it return the right code');
1298 source => 'UserPermission',
1300 borrowernumber => $patron->borrowernumber,
1302 code => 'edit_catalogue'
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');
1310 $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
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)
1318 $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1322 subtest 'x-record-source-id header tests' => sub {
1327 $builder->build_object( { class => 'Koha::RecordSources', value => { can_be_edited => 0 } } );
1330 "//$userid:$password@/api/v1/biblios" => {
1331 'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode,
1332 'x-record-source-id' => $record_source->id
1334 )->status_is(403)->json_is( '/error' => 'You do not have permission to set the record source' );
1336 # Add required subpermission
1339 source => 'UserPermission',
1341 borrowernumber => $patron->id,
1343 code => 'set_record_sources'
1349 "//$userid:$password@/api/v1/biblios" => {
1350 'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode,
1351 'x-record-source-id' => $record_source->id
1356 $schema->storage->txn_rollback;
1359 subtest 'put() tests' => sub {
1363 $schema->storage->txn_begin;
1365 my $patron = $builder->build_object(
1367 class => 'Koha::Patrons',
1368 value => { flags => 0 } # no permissions
1371 my $password = 'thePassword123';
1372 $patron->set_password( { password => $password, skip_validation => 1 } );
1373 my $userid = $patron->userid;
1375 my $frameworkcode = 'BKS';
1376 my $biblio = $builder->build_sample_biblio({frameworkcode => $frameworkcode});
1378 my $biblionumber = $biblio->biblionumber;
1380 my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
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">
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>
1393 <datafield tag="020" ind1=" " ind2=" ">
1394 <subfield code="a">05200784384 (Test json)</subfield>
1396 <datafield tag="020" ind1=" " ind2=" ">
1397 <subfield code="a">05200784464 (Test json)</subfield>
1399 <datafield tag="040" ind1=" " ind2=" ">
1400 <subfield code="a">DLC</subfield>
1401 <subfield code="c">DLC</subfield>
1402 <subfield code="d">DLC</subfield>
1404 <datafield tag="041" ind1="0" ind2=" ">
1405 <subfield code="a">enggrc</subfield>
1407 <datafield tag="050" ind1="0" ind2="0">
1408 <subfield code="a">PA522</subfield>
1409 <subfield code="b">.M38 1993</subfield>
1411 <datafield tag="082" ind1="0" ind2="0">
1412 <subfield code="a">480</subfield>
1413 <subfield code="2">20</subfield>
1415 <datafield tag="100" ind1="1" ind2=" ">
1416 <subfield code="a">Mastronarde, Donald J.</subfield>
1417 <subfield code="9">389</subfield>
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>
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>
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>
1433 <datafield tag="500" ind1=" " ind2=" ">
1434 <subfield code="a">Includes index.</subfield>
1436 <datafield tag="650" ind1=" " ind2="0">
1437 <subfield code="a">Attic Greek dialect</subfield>
1438 <subfield code="9">7</subfield>
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>
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>
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>
1457 <datafield tag="942" ind1=" " ind2=" ">
1458 <subfield code="2">ddc</subfield>
1459 <subfield code="c">BK</subfield>
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>
1464 <datafield tag="999" ind1=" " ind2=" ">
1465 <subfield code="c">3</subfield>
1466 <subfield code="d">3</subfield>
1476 "005": "20200421093816.0"
1479 "008": "920610s1993 caub s001 0 eng "
1496 "a": "05200784382 (Test mij)"
1507 "a": "05200784462 (Test mij)"
1575 "a": "Mastronarde, Donald J."
1589 "a": "Introduction to Attic Greek (Using mij) /"
1592 "c": "Donald J. Mastronarde."
1605 "b": "University of California Press,"
1636 "a": "Includes index."
1647 "a": "Attic Greek dialect"
1661 "3": "Contributor biographical information"
1664 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1676 "3": "Publisher description"
1679 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
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"
1754 "leader": "01102pam a2200289 a 8500"
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
\1e0
\1faenggrc
\1e00
\1faPA522
\1fb.M38 1993
\1e00
\1fa480
\1f220
\1e1
\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|;
1758 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber")
1759 ->status_is(403, 'Not enough permissions makes it return the right code');
1764 source => 'UserPermission',
1766 borrowernumber => $patron->borrowernumber,
1768 code => 'edit_catalogue'
1773 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
1777 $biblio = Koha::Biblios->find($biblionumber);
1779 is($biblio->title, 'Introduction to Attic Greek (Using marcxml) /');
1781 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode} => $mij)
1785 $biblio = Koha::Biblios->find($biblionumber);
1787 is($biblio->title, 'Introduction to Attic Greek (Using mij) /');
1789 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1793 $biblio = Koha::Biblios->find($biblionumber);
1795 is($biblio->title, 'Introduction to Attic Greek (Using usmarc) /');
1797 $schema->storage->txn_rollback;
1800 subtest 'list() tests' => sub {
1804 $schema->storage->txn_begin;
1806 my $patron = $builder->build_object(
1808 class => 'Koha::Patrons',
1809 value => { flags => 0 }
1812 my $password = 'thePassword123';
1813 $patron->set_password( { password => $password, skip_validation => 1 } );
1814 $patron->discard_changes;
1815 my $userid = $patron->userid;
1817 t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
1819 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
1820 my $biblio = $builder->build_sample_biblio(
1822 title => $title_with_diacritics,
1823 author => "Milan Kundera"
1827 my $record = $biblio->metadata->record;
1828 $record->leader(' nam 3 4500');
1829 $biblio->metadata->metadata($record->as_xml_record('UNIMARC'))->store;
1831 my $biblio_id_1 = $biblio->id;
1833 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
1834 my $biblio_id_2 = $builder->build_sample_biblio->id;
1836 my $query = encode_json( [ { biblio_id => $biblio_id_1 }, { biblio_id => $biblio_id_2 } ] );
1838 $t->get_ok("//$userid:$password@/api/v1/biblios?q=$query")->status_is(403);
1840 $patron->flags(4)->store;
1842 $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/weird+format' } )
1845 $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/json' } )->status_is(200);
1847 my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marcxml+xml' } )
1848 ->status_is(200)->tx->res->body;
1850 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
1851 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
1853 $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marc-in-json' } )
1856 $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marc' } )->status_is(200);
1858 $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'text/plain' } )->status_is(200);
1860 # DELETE any biblio with ISBN = TOMAS
1861 Koha::Biblios->search({ 'biblioitem.isbn' => 'TOMAS' }, { join => [ 'biblioitem' ] })
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' } )
1871 $schema->storage->txn_rollback;
1874 subtest 'add_item() tests' => sub {
1877 $schema->storage->txn_begin;
1879 my $patron = $builder->build_object(
1881 class => 'Koha::Patrons',
1882 value => { flags => 0 }
1885 my $password = 'thePassword123';
1886 $patron->set_password( { password => $password, skip_validation => 1 } );
1887 my $userid = $patron->userid;
1889 my $biblio = $builder->build_sample_biblio();
1890 my $biblio_id = $biblio->biblionumber;
1892 my $barcode = 'mybarcode';
1893 my $matching_items = Koha::Items->search({ barcode => $barcode });
1895 while (my $item = $matching_items->next) {
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');
1905 source => 'UserPermission',
1907 borrowernumber => $patron->borrowernumber,
1909 code => 'edit_catalogue'
1914 $t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => {
1915 external_id => $barcode,
1917 ->status_is(201, 'Item created')
1918 ->json_is('/biblio_id', $biblio_id);
1920 my $item = $builder->build_sample_item();
1922 $t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => {
1923 external_id => $item->barcode,
1925 ->status_is(409, 'Duplicate barcode');
1927 $schema->storage->txn_rollback;
1930 subtest 'update_item() tests' => sub {
1933 $schema->storage->txn_begin;
1935 my $patron = $builder->build_object(
1937 class => 'Koha::Patrons',
1938 value => { flags => 0 }
1941 my $password = 'thePassword123';
1942 $patron->set_password( { password => $password, skip_validation => 1 } );
1943 my $userid = $patron->userid;
1945 my $item = $builder->build_sample_item({ replacementprice => 5 });
1946 my $biblio_id = $item->biblionumber;
1947 my $item_id = $item->itemnumber;
1949 my $biblio = Koha::Biblios->find($item->biblionumber);
1951 my $matching_items = Koha::Items->search({ barcode => $item->barcode });
1953 while (my $mbcitem = $matching_items->next) {
1954 $mbcitem->delete if $mbcitem->biblionumber != $item->biblionumber;
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');
1963 source => 'UserPermission',
1965 borrowernumber => $patron->borrowernumber,
1967 code => 'edit_catalogue'
1972 my $other_item = $builder->build_sample_item();
1974 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => {
1975 external_id => $other_item->barcode,
1977 ->status_is(409, 'Barcode not unique');
1979 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => {
1980 replacement_price => 30,
1982 ->status_is(200, 'Item updated')
1983 ->json_is('/replacement_price', 30);
1985 $schema->storage->txn_rollback;
1988 subtest 'merge() tests' => sub {
1990 $schema->storage->txn_begin;
1998 "005": "20200421093816.0"
2001 "008": "920610s1993 caub s001 0 eng "
2018 "a": "05200784462 (Test mij)"
2072 "a": "Mastronarde, Donald J."
2086 "a": "Introduction to Attic Greek (Using mij) /"
2089 "c": "Donald J. Mastronarde."
2102 "b": "University of California Press,"
2133 "a": "Attic Greek dialect"
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"
2183 "leader": "01102pam a2200289 a 8500"
2186 my $patron = $builder->build_object(
2188 class => 'Koha::Patrons',
2189 value => { flags => 0 }
2192 my $password = 'thePassword123';
2193 $patron->set_password( { password => $password, skip_validation => 1 } );
2194 my $userid = $patron->userid;
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 . '" }';
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' );
2209 $patron->flags(516)->store;
2212 "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" => { 'Content-Type' => 'application/weird+format' } =>
2213 $json_input1 )->status_is( 400, 'Not correct headers' );
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" );
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 . ' }';
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" );
2234 $schema->storage->txn_rollback;