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 => 12;
29 use t::lib::TestBuilder;
32 use C4::Circulation qw( AddIssue AddReturn );
37 use Koha::Old::Checkouts;
39 my $schema = Koha::Database->new->schema;
40 my $builder = t::lib::TestBuilder->new;
42 t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
44 my $t = Test::Mojo->new('Koha::REST::V1');
46 subtest 'get() tests' => sub {
50 $schema->storage->txn_begin;
52 my $patron = $builder->build_object(
54 class => 'Koha::Patrons',
55 value => { flags => 0 }
58 my $password = 'thePassword123';
59 $patron->set_password( { password => $password, skip_validation => 1 } );
60 $patron->discard_changes;
61 my $userid = $patron->userid;
63 my $biblio = $builder->build_sample_biblio({
64 title => 'The unbearable lightness of being',
65 author => 'Milan Kundera'
67 $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber)
70 $patron->flags(4)->store;
72 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
73 => { Accept => 'application/weird+format' } )
76 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
77 => { Accept => 'application/json' } )
79 ->json_is( '/title', 'The unbearable lightness of being' )
80 ->json_is( '/author', 'Milan Kundera' );
82 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
83 => { Accept => 'application/marcxml+xml' } )
86 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
87 => { Accept => 'application/marc-in-json' } )
90 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
91 => { Accept => 'application/marc' } )
94 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
95 => { Accept => 'text/plain' } )
97 ->content_is($biblio->metadata->record->as_formatted);
100 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
101 => { Accept => 'application/marc' } )
103 ->json_is( '/error', 'Object not found.' );
105 subtest 'marc-in-json encoding tests' => sub {
109 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
111 my $biblio = $builder->build_sample_biblio(
113 title => $title_with_diacritics,
114 author => "Milan Kundera"
118 my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
119 => { Accept => 'application/marc-in-json' } )
120 ->status_is(200)->tx->res->body;
122 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
124 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
127 subtest 'marcxml encoding tests' => sub {
130 my $marcflavour = C4::Context->preference('marcflavour');
131 t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
134 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
136 my $biblio = $builder->build_sample_biblio(
138 title => $title_with_diacritics,
139 author => "Milan Kundera"
143 my $record = $biblio->metadata->record;
144 $record->leader(' nam 3 4500');
145 $biblio->metadata->metadata($record->as_xml_record('UNIMARC'));
146 $biblio->metadata->store;
148 my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
149 => { Accept => 'application/marcxml+xml' } )
150 ->status_is(200)->tx->res->body;
152 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
154 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
155 t::lib::Mocks::mock_preference('marcflavour', $marcflavour);
158 $schema->storage->txn_rollback;
161 subtest 'get_items() tests' => sub {
165 $schema->storage->txn_begin;
167 my $patron = $builder->build_object(
169 class => 'Koha::Patrons',
170 value => { flags => 0 }
173 my $password = 'thePassword123';
174 $patron->set_password( { password => $password, skip_validation => 1 } );
175 $patron->discard_changes;
176 my $userid = $patron->userid;
178 my $biblio = $builder->build_sample_biblio();
179 $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
182 $patron->flags(4)->store;
184 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
186 ->json_is( '' => [], 'No items on the biblio' );
188 my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
189 my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
191 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
193 ->json_is( '' => [ $item_1->to_api, $item_2->to_api ], 'The items are returned' );
195 $schema->storage->txn_rollback;
198 subtest 'delete() tests' => sub {
202 $schema->storage->txn_begin;
204 my $patron = $builder->build_object(
206 class => 'Koha::Patrons',
207 value => { flags => 0 } # no permissions
210 my $password = 'thePassword123';
211 $patron->set_password( { password => $password, skip_validation => 1 } );
212 my $userid = $patron->userid;
214 my $item = $builder->build_sample_item();
215 my $biblio_id = $item->biblionumber;
217 $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
218 ->status_is(403, 'Not enough permissions makes it return the right code');
223 source => 'UserPermission',
225 borrowernumber => $patron->borrowernumber,
227 code => 'edit_catalogue'
233 # Bibs with items cannot be deleted
234 $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
239 # Bibs with no items can be deleted
240 $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
241 ->status_is(204, 'SWAGGER3.2.4')
242 ->content_is('', 'SWAGGER3.3.4');
244 $t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
247 $schema->storage->txn_rollback;
250 subtest 'get_public() tests' => sub {
254 $schema->storage->txn_begin;
256 my $category = $builder->build_object({ class => 'Koha::Patron::Categories' });
257 my $patron = $builder->build_object(
259 class => 'Koha::Patrons',
261 flags => undef, # opac user
262 categorycode => $category->categorycode
266 my $password = 'thePassword123';
267 $patron->set_password( { password => $password, skip_validation => 1 } );
268 $patron->discard_changes;
269 my $userid = $patron->userid;
271 my $biblio = $builder->build_sample_biblio({
272 title => 'The unbearable lightness of being',
273 author => 'Milan Kundera'
276 # Make sure author in shown in the OPAC
277 my $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
278 while ( my $subfield = $subfields->next ) {
279 $subfield->set({ hidden => -1 })->store;
281 Koha::Caches->get_instance()->flush_all;
283 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
284 => { Accept => 'application/weird+format' } )
287 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
288 => { Accept => 'text/plain' } )
290 ->content_like( qr{100\s+_aMilan Kundera} )
291 ->content_like( qr{245\s+_aThe unbearable lightness of being} );
293 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
294 => { Accept => 'application/marcxml+xml' } )
297 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
298 => { Accept => 'application/marc-in-json' } )
301 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
302 => { Accept => 'application/marc' } )
305 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
306 => { Accept => 'text/plain' } )
308 ->content_is($biblio->metadata->record->as_formatted);
310 subtest 'anonymous access' => sub {
313 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
314 => { Accept => 'application/marcxml+xml' } )
317 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
318 => { Accept => 'application/marc-in-json' } )
321 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
322 => { Accept => 'application/marc' } )
325 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
326 => { Accept => 'text/plain' } )
328 ->content_is($biblio->metadata->record->as_formatted);
331 subtest 'marc-in-json encoding tests' => sub {
335 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
337 my $biblio = $builder->build_sample_biblio(
339 title => $title_with_diacritics,
340 author => "Milan Kundera"
344 my $result = $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
345 => { Accept => 'application/marc-in-json' } )
346 ->status_is(200)->tx->res->body;
348 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
350 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
353 # Hide author in OPAC
354 $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
355 while ( my $subfield = $subfields->next ) {
356 $subfield->set({ hidden => 1 })->store;
359 Koha::Caches->get_instance()->flush_all;
361 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
362 => { Accept => 'text/plain' } )
364 ->content_unlike( qr{100\s+_aMilan Kundera} )
365 ->content_like( qr{245\s+_aThe unbearable lightness of being} );
367 subtest 'hidden_in_opac tests' => sub {
371 my $biblio_hidden_in_opac = 1;
373 my $biblio_class = Test::MockModule->new('Koha::Biblio');
374 # force biblio hidden in OPAC
375 $biblio_class->mock( 'hidden_in_opac', sub { return $biblio_hidden_in_opac; } );
377 $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
378 => { Accept => 'text/plain' } )
379 ->status_is(404, 'hidden_in_opac + anonymous => hidden');
381 my $category_override_hidden_items = 0;
382 my $category_class = Test::MockModule->new('Koha::Patron::Category');
383 $category_class->mock( 'override_hidden_items', sub { return $category_override_hidden_items; } );
384 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
385 => { Accept => 'text/plain' } )
386 ->status_is(404, "hidden_in_opac + patron whose category doesn't override => hidden");
388 # Make the category override
389 $category_override_hidden_items = 1;
390 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
391 => { Accept => 'text/plain' } )
392 ->status_is(200, "hidden_in_opac + patron whose category that overrides => displayed");
394 t::lib::Mocks::mock_preference('OpacHiddenItems');
398 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
399 => { Accept => 'application/marc' } )
401 ->json_is( '/error', 'Object not found.' );
403 $schema->storage->txn_rollback;
406 subtest 'pickup_locations() tests' => sub {
410 $schema->storage->txn_begin;
412 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
414 # Small trick to ease testing
415 Koha::Libraries->search->update({ pickup_location => 0 });
417 my $library_1 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'A', pickup_location => 1 } });
418 my $library_2 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'B', pickup_location => 1 } });
419 my $library_3 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'C', pickup_location => 1 } });
421 my $library_1_api = $library_1->to_api();
422 my $library_2_api = $library_2->to_api();
423 my $library_3_api = $library_3->to_api();
425 $library_1_api->{needs_override} = Mojo::JSON->false;
426 $library_2_api->{needs_override} = Mojo::JSON->false;
427 $library_3_api->{needs_override} = Mojo::JSON->true;
429 my $patron = $builder->build_object(
431 class => 'Koha::Patrons',
432 value => { userid => 'tomasito', flags => 0 }
435 my $password = 'thePassword123';
436 $patron->set_password( { password => $password, skip_validation => 1 } );
437 my $userid = $patron->userid;
440 source => 'UserPermission',
442 borrowernumber => $patron->borrowernumber,
444 code => 'place_holds',
449 my $biblio_class = Test::MockModule->new('Koha::Biblio');
453 my ( $self, $params ) = @_;
454 my $mock_patron = $params->{patron};
455 is( $mock_patron->borrowernumber,
456 $patron->borrowernumber, 'Patron passed correctly' );
457 return Koha::Libraries->search(
461 $library_1->branchcode,
462 $library_2->branchcode
466 { # we make sure no surprises in the order of the result
467 order_by => { '-asc' => 'marcorgcode' }
473 my $biblio = $builder->build_sample_biblio;
475 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
477 . "/pickup_locations?patron_id=" . $patron->id )
478 ->json_is( [ $library_1_api, $library_2_api ] );
481 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
483 . '/pickup_locations?'
484 . 'patron_id=' . $patron->id . '&q={"marc_org_code": { "-like": "A%" }}' )
485 ->json_is( [ $library_1_api ] );
487 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
489 my $library_4 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 0, marcorgcode => 'X' } });
490 my $library_5 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 1, marcorgcode => 'Y' } });
492 my $library_5_api = $library_5->to_api();
493 $library_5_api->{needs_override} = Mojo::JSON->true;
495 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
497 . "/pickup_locations?"
498 . "patron_id=" . $patron->id . "&_order_by=marc_org_code" )
499 ->json_is( [ $library_1_api, $library_2_api, $library_3_api, $library_5_api ] );
501 subtest 'Pagination and AllowHoldPolicyOverride tests' => sub {
505 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
507 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
508 ->json_is( [$library_1_api] )
509 ->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
510 ->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
511 ->header_unlike( 'Link', qr|rel="prev"| )
512 ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
513 ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
514 ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
516 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
518 . "/pickup_locations?"
521 . "&_order_by=marc_org_code"
522 . "&_per_page=1&_page=3" ) # force the needs_override=1 check
523 ->json_is( [$library_3_api] )
524 ->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
525 ->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
526 ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="prev"# )
527 ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1.*)>\; rel="next"# )
528 ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
529 ->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
531 t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
533 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
534 ->json_is( [$library_1_api] )
535 ->header_is( 'X-Total-Count', '2' )
536 ->header_is( 'X-Base-Total-Count', '2' )
537 ->header_unlike( 'Link', qr|rel="prev"| )
538 ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
539 ->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
540 ->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1).*>\; rel="last"# );
543 my $deleted_patron = $builder->build_object({ class => 'Koha::Patrons' });
544 my $deleted_patron_id = $deleted_patron->id;
545 $deleted_patron->delete;
547 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
549 . "/pickup_locations?"
550 . "patron_id=" . $deleted_patron_id )
552 ->json_is( '/error' => 'Patron not found' );
556 $t->get_ok( "//$userid:$password@/api/v1/biblios/"
558 . "/pickup_locations?"
559 . "patron_id=" . $patron->id )
561 ->json_is( '/error' => 'Biblio not found' );
563 $schema->storage->txn_rollback;
566 subtest 'get_items_public() tests' => sub {
570 $schema->storage->txn_begin;
572 my $override_hidden_items = 0;
574 my $mocked_category = Test::MockModule->new('Koha::Patron::Category');
575 $mocked_category->mock(
576 'override_hidden_items',
578 return $override_hidden_items;
584 my $mocked_context = Test::MockModule->new('C4::Context');
585 $mocked_context->mock(
592 my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
593 my $password = 'thePassword123';
594 $patron->set_password( { password => $password, skip_validation => 1 } );
595 $patron->discard_changes;
596 my $userid = $patron->userid;
598 my $biblio = $builder->build_sample_biblio();
601 "//$userid:$password@/api/v1/public/biblios/" . $biblio->id . "/items" )
602 ->status_is(200)->json_is( '' => [], 'No items on the biblio' );
604 my $item_1 = $builder->build_sample_item( { biblionumber => $biblio->id } );
605 my $item_2 = $builder->build_sample_item(
606 { biblionumber => $biblio->id, withdrawn => 1 } );
608 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
609 . $biblio->biblionumber
610 . "/items" )->status_is(200)->json_is(
612 $item_1->to_api( { public => 1 } ),
613 $item_2->to_api( { public => 1 } )
615 'The items are returned'
618 $rules = { withdrawn => ['1'] };
620 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
621 . $biblio->biblionumber
622 . "/items" )->status_is(200)->json_is(
623 '' => [ $item_1->to_api( { public => 1 } ) ],
624 'The items are returned, hidden one is not returned'
627 $t->get_ok( "/api/v1/public/biblios/"
628 . $biblio->biblionumber
629 . "/items" )->status_is(200)->json_is(
630 '' => [ $item_1->to_api( { public => 1 } ) ],
631 'Anonymous user, items are returned, hidden one is not returned'
635 $override_hidden_items = 1;
637 $t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
638 . $biblio->biblionumber
639 . "/items" )->status_is(200)->json_is(
641 $item_1->to_api( { public => 1 } ),
642 $item_2->to_api( { public => 1 } )
644 'The items are returned, the patron category has an override'
647 $schema->storage->txn_rollback;
650 subtest 'get_checkouts() tests' => sub {
654 $schema->storage->txn_begin;
656 my $patron = $builder->build_object(
658 class => 'Koha::Patrons',
659 value => { flags => 0 }
662 my $password = 'thePassword123';
663 $patron->set_password( { password => $password, skip_validation => 1 } );
664 $patron->discard_changes;
665 my $userid = $patron->userid;
667 my $biblio = $builder->build_sample_biblio();
668 $t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
671 $patron->flags(1)->store; # circulate permissions
673 $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
675 ->json_is( '' => [], 'No checkouts on the biblio' );
677 my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
678 my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
680 AddIssue( $patron->unblessed, $item_1->barcode );
681 AddIssue( $patron->unblessed, $item_2->barcode );
683 my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
687 my $checkout_1 = Koha::Checkouts->find({ itemnumber => $item_1->id });
688 my $checkout_2 = Koha::Checkouts->find({ itemnumber => $item_2->id });
690 is_deeply( $ret, [ $checkout_1->to_api, $checkout_2->to_api ] );
692 AddReturn( $item_1->barcode );
694 $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
698 is_deeply( $ret, [ $checkout_2->to_api ] );
700 $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts?checked_in=1")
704 my $old_checkout_1 = Koha::Old::Checkouts->find( $checkout_1->id );
706 is_deeply( $ret, [ $old_checkout_1->to_api ] );
708 $schema->storage->txn_rollback;
711 subtest 'set_rating() tests' => sub {
715 $schema->storage->txn_begin;
717 my $patron = $builder->build_object(
719 class => 'Koha::Patrons',
720 value => { flags => 0 }
723 my $password = 'thePassword123';
724 $patron->set_password( { password => $password, skip_validation => 1 } );
725 $patron->discard_changes;
726 my $userid = $patron->userid;
728 my $biblio = $builder->build_sample_biblio();
729 $t->post_ok("/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
732 $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
734 ->json_is( '/rating', '3' )
735 ->json_is( '/average', '3' )
736 ->json_is( '/count', '1' );
738 $t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => undef })
740 ->json_is( '/rating', undef )
741 ->json_is( '/average', '0' )
742 ->json_is( '/count', '0' );
744 $schema->storage->txn_rollback;
749 subtest 'post() tests' => sub {
753 $schema->storage->txn_begin;
755 my $patron = $builder->build_object(
757 class => 'Koha::Patrons',
758 value => { flags => 0 } # no permissions
761 my $password = 'thePassword123';
762 $patron->set_password( { password => $password, skip_validation => 1 } );
763 my $userid = $patron->userid;
765 my $frameworkcode = 'BKS';
766 my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
768 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
769 xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
770 xmlns="http://www.loc.gov/MARC21/slim">
772 <leader>01102pam a2200289 a 7500</leader>
773 <controlfield tag="001">2504398</controlfield>
774 <controlfield tag="005">20200421093816.0</controlfield>
775 <controlfield tag="008">920610s1993 caub s001 0 eng </controlfield>
776 <datafield tag="010" ind1=" " ind2=" ">
777 <subfield code="a"> 92021731 </subfield>
779 <datafield tag="020" ind1=" " ind2=" ">
780 <subfield code="a">05200784381 (Test marcxml)</subfield>
782 <datafield tag="020" ind1=" " ind2=" ">
783 <subfield code="a">05200784461 (Test marcxml)</subfield>
785 <datafield tag="040" ind1=" " ind2=" ">
786 <subfield code="a">DLC</subfield>
787 <subfield code="c">DLC</subfield>
788 <subfield code="d">DLC</subfield>
790 <datafield tag="041" ind1="0" ind2=" ">
791 <subfield code="a">enggrc</subfield>
793 <datafield tag="050" ind1="0" ind2="0">
794 <subfield code="a">PA522</subfield>
795 <subfield code="b">.M38 1993</subfield>
797 <datafield tag="082" ind1="0" ind2="0">
798 <subfield code="a">480</subfield>
799 <subfield code="2">20</subfield>
801 <datafield tag="100" ind1="1" ind2=" ">
802 <subfield code="a">Mastronarde, Donald J.</subfield>
803 <subfield code="9">389</subfield>
805 <datafield tag="245" ind1="1" ind2="0">
806 <subfield code="a">Introduction to Attic Greek (Using marcxml) /</subfield>
807 <subfield code="c">Donald J. Mastronarde.</subfield>
809 <datafield tag="260" ind1=" " ind2=" ">
810 <subfield code="a">Berkeley :</subfield>
811 <subfield code="b">University of California Press,</subfield>
812 <subfield code="c">c1993.</subfield>
814 <datafield tag="300" ind1=" " ind2=" ">
815 <subfield code="a">ix, 425 p. :</subfield>
816 <subfield code="b">maps ;</subfield>
817 <subfield code="c">26 cm.</subfield>
819 <datafield tag="500" ind1=" " ind2=" ">
820 <subfield code="a">Includes index.</subfield>
822 <datafield tag="650" ind1=" " ind2="0">
823 <subfield code="a">Attic Greek dialect</subfield>
824 <subfield code="9">7</subfield>
826 <datafield tag="856" ind1="4" ind2="2">
827 <subfield code="3">Contributor biographical information</subfield>
828 <subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
830 <datafield tag="856" ind1="4" ind2="2">
831 <subfield code="3">Publisher description</subfield>
832 <subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
834 <datafield tag="906" ind1=" " ind2=" ">
835 <subfield code="a">7</subfield>
836 <subfield code="b">cbc</subfield>
837 <subfield code="c">orignew</subfield>
838 <subfield code="d">1</subfield>
839 <subfield code="e">ocip</subfield>
840 <subfield code="f">19</subfield>
841 <subfield code="g">y-gencatlg</subfield>
843 <datafield tag="942" ind1=" " ind2=" ">
844 <subfield code="2">ddc</subfield>
845 <subfield code="c">BK</subfield>
847 <datafield tag="955" ind1=" " ind2=" ">
848 <subfield code="a">pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93</subfield>
850 <datafield tag="999" ind1=" " ind2=" ">
851 <subfield code="c">3</subfield>
852 <subfield code="d">3</subfield>
862 "005": "20200421093816.0"
865 "008": "920610s1993 caub s001 0 eng "
882 "a": "05200784382 (Test mij)"
893 "a": "05200784462 (Test mij)"
961 "a": "Mastronarde, Donald J."
975 "a": "Introduction to Attic Greek (Using mij) /"
978 "c": "Donald J. Mastronarde."
991 "b": "University of California Press,"
1022 "a": "Includes index."
1033 "a": "Attic Greek dialect"
1047 "3": "Contributor biographical information"
1050 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1062 "3": "Publisher description"
1065 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
1118 "a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93"
1140 "leader": "01102pam a2200289 a 8500"
1142 my $marc = q|01102pam a2200289 a 9500001000800000005001700008008004100025010001700066020002800083020003500111040001800146041001100164050002100175082001200196100003200208245005800240260005600298300003300354500002000387650002700407856009500434856008700529906004500616942001200661955013000673999000900803
\1e2504398
\1e20200421093816.0
\1e920610s1993 caub s001 0 eng
\1e \1fa 92021731
\1e \1fa05200784383 (Test usmarc)
\1e \1fa05200784463 (Test usmarc)
\1e \1faDLC
\1fcDLC
\1fdDLC
\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|;
1144 $t->post_ok("//$userid:$password@/api/v1/biblios")
1145 ->status_is(403, 'Not enough permissions makes it return the right code');
1150 source => 'UserPermission',
1152 borrowernumber => $patron->borrowernumber,
1154 code => 'edit_catalogue'
1159 $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode, "x-record-schema" => 'INVALID'})
1160 ->status_is(400, 'Invalid header x-record-schema');
1162 $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
1166 $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode, 'x-confirm-not-duplicate' => 1} => $mij)
1170 $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1174 $schema->storage->txn_rollback;
1177 subtest 'put() tests' => sub {
1181 $schema->storage->txn_begin;
1183 my $patron = $builder->build_object(
1185 class => 'Koha::Patrons',
1186 value => { flags => 0 } # no permissions
1189 my $password = 'thePassword123';
1190 $patron->set_password( { password => $password, skip_validation => 1 } );
1191 my $userid = $patron->userid;
1193 my $frameworkcode = 'BKS';
1194 my $biblio = $builder->build_sample_biblio({frameworkcode => $frameworkcode});
1196 my $biblionumber = $biblio->biblionumber;
1198 my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
1200 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1201 xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
1202 xmlns="http://www.loc.gov/MARC21/slim">
1204 <leader>01102pam a2200289 a 6500</leader>
1205 <controlfield tag="001">2504398</controlfield>
1206 <controlfield tag="005">20200421093816.0</controlfield>
1207 <controlfield tag="008">920610s1993 caub s001 0 eng </controlfield>
1208 <datafield tag="010" ind1=" " ind2=" ">
1209 <subfield code="a"> 92021731 </subfield>
1211 <datafield tag="020" ind1=" " ind2=" ">
1212 <subfield code="a">05200784384 (Test json)</subfield>
1214 <datafield tag="020" ind1=" " ind2=" ">
1215 <subfield code="a">05200784464 (Test json)</subfield>
1217 <datafield tag="040" ind1=" " ind2=" ">
1218 <subfield code="a">DLC</subfield>
1219 <subfield code="c">DLC</subfield>
1220 <subfield code="d">DLC</subfield>
1222 <datafield tag="041" ind1="0" ind2=" ">
1223 <subfield code="a">enggrc</subfield>
1225 <datafield tag="050" ind1="0" ind2="0">
1226 <subfield code="a">PA522</subfield>
1227 <subfield code="b">.M38 1993</subfield>
1229 <datafield tag="082" ind1="0" ind2="0">
1230 <subfield code="a">480</subfield>
1231 <subfield code="2">20</subfield>
1233 <datafield tag="100" ind1="1" ind2=" ">
1234 <subfield code="a">Mastronarde, Donald J.</subfield>
1235 <subfield code="9">389</subfield>
1237 <datafield tag="245" ind1="1" ind2="0">
1238 <subfield code="a">Introduction to Attic Greek (Using marcxml) /</subfield>
1239 <subfield code="c">Donald J. Mastronarde.</subfield>
1241 <datafield tag="260" ind1=" " ind2=" ">
1242 <subfield code="a">Berkeley :</subfield>
1243 <subfield code="b">University of California Press,</subfield>
1244 <subfield code="c">c1993.</subfield>
1246 <datafield tag="300" ind1=" " ind2=" ">
1247 <subfield code="a">ix, 425 p. :</subfield>
1248 <subfield code="b">maps ;</subfield>
1249 <subfield code="c">26 cm.</subfield>
1251 <datafield tag="500" ind1=" " ind2=" ">
1252 <subfield code="a">Includes index.</subfield>
1254 <datafield tag="650" ind1=" " ind2="0">
1255 <subfield code="a">Attic Greek dialect</subfield>
1256 <subfield code="9">7</subfield>
1258 <datafield tag="856" ind1="4" ind2="2">
1259 <subfield code="3">Contributor biographical information</subfield>
1260 <subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
1262 <datafield tag="856" ind1="4" ind2="2">
1263 <subfield code="3">Publisher description</subfield>
1264 <subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
1266 <datafield tag="906" ind1=" " ind2=" ">
1267 <subfield code="a">7</subfield>
1268 <subfield code="b">cbc</subfield>
1269 <subfield code="c">orignew</subfield>
1270 <subfield code="d">1</subfield>
1271 <subfield code="e">ocip</subfield>
1272 <subfield code="f">19</subfield>
1273 <subfield code="g">y-gencatlg</subfield>
1275 <datafield tag="942" ind1=" " ind2=" ">
1276 <subfield code="2">ddc</subfield>
1277 <subfield code="c">BK</subfield>
1279 <datafield tag="955" ind1=" " ind2=" ">
1280 <subfield code="a">pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93</subfield>
1282 <datafield tag="999" ind1=" " ind2=" ">
1283 <subfield code="c">3</subfield>
1284 <subfield code="d">3</subfield>
1294 "005": "20200421093816.0"
1297 "008": "920610s1993 caub s001 0 eng "
1314 "a": "05200784382 (Test mij)"
1325 "a": "05200784462 (Test mij)"
1393 "a": "Mastronarde, Donald J."
1407 "a": "Introduction to Attic Greek (Using mij) /"
1410 "c": "Donald J. Mastronarde."
1423 "b": "University of California Press,"
1454 "a": "Includes index."
1465 "a": "Attic Greek dialect"
1479 "3": "Contributor biographical information"
1482 "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
1494 "3": "Publisher description"
1497 "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
1550 "a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93"
1572 "leader": "01102pam a2200289 a 8500"
1574 my $marc = q|01116pam a2200289 a 4500001000800000005001700008008004100025010001700066020002800083020002800111040001800139041001100157050002100168082001200189100003200201245007500233260005600308300003300364500002000397650002700417856009500444856008700539906004500626942001200671955013000683999001300813
\1e2504398
\1e20221223213433.0
\1e920610s1993 caub s001 0 eng
\1e \1fa 92021731
\1e \1fa05200784384 (Test json)
\1e \1fa05200784464 (Test json)
\1e \1faDLC
\1fcDLC
\1fdDLC
\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|;
1576 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber")
1577 ->status_is(403, 'Not enough permissions makes it return the right code');
1582 source => 'UserPermission',
1584 borrowernumber => $patron->borrowernumber,
1586 code => 'edit_catalogue'
1591 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
1595 $biblio = Koha::Biblios->find($biblionumber);
1597 is($biblio->title, 'Introduction to Attic Greek (Using marcxml) /');
1599 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode} => $mij)
1603 $biblio = Koha::Biblios->find($biblionumber);
1605 is($biblio->title, 'Introduction to Attic Greek (Using mij) /');
1607 $t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
1611 $biblio = Koha::Biblios->find($biblionumber);
1613 is($biblio->title, 'Introduction to Attic Greek (Using usmarc) /');
1615 $schema->storage->txn_rollback;
1618 subtest 'list() tests' => sub {
1622 $schema->storage->txn_begin;
1624 my $patron = $builder->build_object(
1626 class => 'Koha::Patrons',
1627 value => { flags => 0 }
1630 my $password = 'thePassword123';
1631 $patron->set_password( { password => $password, skip_validation => 1 } );
1632 $patron->discard_changes;
1633 my $userid = $patron->userid;
1635 t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
1637 my $title_with_diacritics = "L'insoutenable légèreté de l'être";
1638 my $biblio = $builder->build_sample_biblio(
1640 title => $title_with_diacritics,
1641 author => "Milan Kundera"
1645 my $record = $biblio->metadata->record;
1646 $record->leader(' nam 3 4500');
1647 $biblio->metadata->metadata($record->as_xml_record('UNIMARC'))->store;
1649 my $biblionumber1 = $biblio->biblionumber;
1651 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
1652 my $biblionumber2 = $builder->build_sample_biblio->biblionumber;
1655 "[{\"biblionumber\": \"$biblionumber1\"}, {\"biblionumber\": \"$biblionumber2\"}]";
1657 "//$userid:$password@/api/v1/biblios/" => { 'x-koha-query' => $search }
1660 $patron->flags(4)->store;
1662 $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1663 { Accept => 'application/weird+format', 'x-koha-query' => $search } )
1666 $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1667 { Accept => 'application/json', 'x-koha-query' => $search } )
1670 my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1671 { Accept => 'application/marcxml+xml', 'x-koha-query' => $search } )
1672 ->status_is(200)->tx->res->body;
1674 my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
1675 like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
1677 $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1678 { Accept => 'application/marc-in-json', 'x-koha-query' => $search } )
1681 $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1682 { Accept => 'application/marc', 'x-koha-query' => $search } )
1685 $t->get_ok( "//$userid:$password@/api/v1/biblios/" =>
1686 { Accept => 'text/plain', 'x-koha-query' => $search } )
1689 $schema->storage->txn_rollback;
1692 subtest 'add_item() tests' => sub {
1695 $schema->storage->txn_begin;
1697 my $patron = $builder->build_object(
1699 class => 'Koha::Patrons',
1700 value => { flags => 0 }
1703 my $password = 'thePassword123';
1704 $patron->set_password( { password => $password, skip_validation => 1 } );
1705 my $userid = $patron->userid;
1707 my $biblio = $builder->build_sample_biblio();
1708 my $biblio_id = $biblio->biblionumber;
1710 my $barcode = 'mybarcode';
1711 my $matching_items = Koha::Items->search({ barcode => $barcode });
1713 while (my $item = $matching_items->next) {
1717 $t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => { external_id => $barcode })
1718 ->status_is(403, 'Not enough permissions to create an item');
1723 source => 'UserPermission',
1725 borrowernumber => $patron->borrowernumber,
1727 code => 'edit_catalogue'
1732 $t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => {
1733 external_id => $barcode,
1735 ->status_is(201, 'Item created')
1736 ->json_is('/biblio_id', $biblio_id);
1738 $schema->storage->txn_rollback;