Bug 30708: Add tests
[koha.git] / t / db_dependent / Koha / Biblio.t
1 #!/usr/bin/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 Test::More tests => 30;
21 use Test::Exception;
22 use Test::Warn;
23
24 use C4::Biblio qw( AddBiblio ModBiblio ModBiblioMarc );
25 use C4::Circulation qw( AddIssue AddReturn );
26
27 use Koha::Database;
28 use Koha::Cache::Memory::Lite;
29 use Koha::Caches;
30 use Koha::Acquisition::Orders;
31 use Koha::AuthorisedValueCategories;
32 use Koha::AuthorisedValues;
33 use Koha::MarcSubfieldStructures;
34 use Koha::Exception;
35
36 use MARC::Field;
37 use MARC::Record;
38
39 use t::lib::TestBuilder;
40 use t::lib::Mocks;
41 use Test::MockModule;
42
43 BEGIN {
44     use_ok('Koha::Biblio');
45     use_ok('Koha::Biblios');
46 }
47
48 my $schema  = Koha::Database->new->schema;
49 my $builder = t::lib::TestBuilder->new;
50
51 subtest 'metadata() tests' => sub {
52
53     plan tests => 4;
54
55     $schema->storage->txn_begin;
56
57     my $title = 'Oranges and Peaches';
58
59     my $record = MARC::Record->new();
60     my $field = MARC::Field->new('245','','','a' => $title);
61     $record->append_fields( $field );
62     my ($biblionumber) = C4::Biblio::AddBiblio($record, '');
63
64     my $biblio = Koha::Biblios->find( $biblionumber );
65     is( ref $biblio, 'Koha::Biblio', 'Found a Koha::Biblio object' );
66
67     my $metadata = $biblio->metadata;
68     is( ref $metadata, 'Koha::Biblio::Metadata', 'Method metadata() returned a Koha::Biblio::Metadata object' );
69
70     my $record2 = $metadata->record;
71     is( ref $record2, 'MARC::Record', 'Method record() returned a MARC::Record object' );
72
73     is( $record2->field('245')->subfield("a"), $title, 'Title in 245$a matches title from original record object' );
74
75     $schema->storage->txn_rollback;
76 };
77
78 subtest 'hidden_in_opac() tests' => sub {
79
80     plan tests => 6;
81
82     $schema->storage->txn_begin;
83
84     my $biblio = $builder->build_sample_biblio();
85     my $rules  = { withdrawn => [ 2 ] };
86
87     t::lib::Mocks::mock_preference( 'OpacHiddenItemsHidesRecord', 0 );
88
89     ok(
90         !$biblio->hidden_in_opac({ rules => $rules }),
91         'Biblio not hidden if there is no item attached (!OpacHiddenItemsHidesRecord)'
92     );
93
94     t::lib::Mocks::mock_preference( 'OpacHiddenItemsHidesRecord', 1 );
95
96     ok(
97         !$biblio->hidden_in_opac({ rules => $rules }),
98         'Biblio not hidden if there is no item attached (OpacHiddenItemsHidesRecord)'
99     );
100
101     my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
102     my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
103
104     $item_1->withdrawn( 1 )->store->discard_changes;
105     $item_2->withdrawn( 1 )->store->discard_changes;
106
107     ok( !$biblio->hidden_in_opac({ rules => $rules }), 'Biblio not hidden' );
108
109     $item_2->withdrawn( 2 )->store->discard_changes;
110     $biblio->discard_changes; # refresh
111
112     ok( !$biblio->hidden_in_opac({ rules => $rules }), 'Biblio not hidden' );
113
114     $item_1->withdrawn( 2 )->store->discard_changes;
115     $biblio->discard_changes; # refresh
116
117     ok( $biblio->hidden_in_opac({ rules => $rules }), 'Biblio hidden' );
118
119     t::lib::Mocks::mock_preference( 'OpacHiddenItemsHidesRecord', 0 );
120     ok(
121         !$biblio->hidden_in_opac( { rules => $rules } ),
122         'Biblio hidden (!OpacHiddenItemsHidesRecord)'
123     );
124
125
126     $schema->storage->txn_rollback;
127 };
128
129 subtest 'items() tests' => sub {
130
131     plan tests => 3;
132
133     $schema->storage->txn_begin;
134
135     my $biblio = $builder->build_sample_biblio();
136
137     is( $biblio->items->count, 0, 'No items, count is 0' );
138
139     my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
140     my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
141
142     my $items = $biblio->items;
143     is( ref($items), 'Koha::Items', 'Returns a Koha::Items resultset' );
144     is( $items->count, 2, 'Two items in resultset' );
145
146     $schema->storage->txn_rollback;
147
148 };
149
150 subtest 'get_coins and get_openurl' => sub {
151
152     plan tests => 4;
153
154     $schema->storage->txn_begin;
155
156     my $builder = t::lib::TestBuilder->new;
157     my $biblio = $builder->build_sample_biblio({
158             title => 'Title 1',
159             author => 'Author 1'
160         });
161     is(
162         $biblio->get_coins,
163         'ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Title%201&amp;rft.au=Author%201',
164         'GetCOinsBiblio returned right metadata'
165     );
166
167     my $record = MARC::Record->new();
168     $record->append_fields( MARC::Field->new('100','','','a' => 'Author 2'), MARC::Field->new('880','','','a' => 'Something') );
169     my ( $biblionumber ) = C4::Biblio::AddBiblio($record, '');
170     my $biblio_no_title = Koha::Biblios->find($biblionumber);
171     is(
172         $biblio_no_title->get_coins,
173         'ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.au=Author%202',
174         'GetCOinsBiblio returned right metadata if biblio does not have a title'
175     );
176
177     t::lib::Mocks::mock_preference("OpenURLResolverURL", "https://koha.example.com/");
178     is(
179         $biblio->get_openurl,
180         'https://koha.example.com/?ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Title%201&amp;rft.au=Author%201',
181         'Koha::Biblio->get_openurl returned right URL'
182     );
183
184     t::lib::Mocks::mock_preference("OpenURLResolverURL", "https://koha.example.com/?client_id=ci1");
185     is(
186         $biblio->get_openurl,
187         'https://koha.example.com/?client_id=ci1&amp;ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook&amp;rft.genre=book&amp;rft.btitle=Title%201&amp;rft.au=Author%201',
188         'Koha::Biblio->get_openurl returned right URL'
189     );
190
191     $schema->storage->txn_rollback;
192 };
193
194 subtest 'is_serial() tests' => sub {
195
196     plan tests => 3;
197
198     $schema->storage->txn_begin;
199
200     my $biblio = $builder->build_sample_biblio();
201
202     $biblio->serial( 1 )->store->discard_changes;
203     ok( $biblio->is_serial, 'Bibliographic record is serial' );
204
205     $biblio->serial( 0 )->store->discard_changes;
206     ok( !$biblio->is_serial, 'Bibliographic record is not serial' );
207
208     my $record = $biblio->metadata->record;
209     $record->leader('00142nas a22     7a 4500');
210     ModBiblio($record, $biblio->biblionumber );
211     $biblio = Koha::Biblios->find($biblio->biblionumber);
212
213     ok( $biblio->is_serial, 'Bibliographic record is serial' );
214
215     $schema->storage->txn_rollback;
216 };
217
218 subtest 'pickup_locations() tests' => sub {
219
220     plan tests => 11;
221
222     $schema->storage->txn_begin;
223
224     Koha::CirculationRules->search->delete;
225     Koha::CirculationRules->set_rules(
226         {
227             categorycode => undef,
228             itemtype     => undef,
229             branchcode   => undef,
230             rules        => {
231                 reservesallowed => 25,
232             }
233         }
234     );
235
236     my $root1 = $builder->build_object( { class => 'Koha::Library::Groups', value => { ft_local_hold_group => 1 } } );
237     my $root2 = $builder->build_object( { class => 'Koha::Library::Groups', value => { ft_local_hold_group => 1 } } );
238     my $root3 = $builder->build_object( { class => 'Koha::Library::Groups', value => { ft_local_hold_group => 1 } } );
239
240     my $library1 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, branchname => 'zzz' } } );
241     my $library2 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, branchname => 'AAA' } } );
242     my $library3 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 0, branchname => 'FFF' } } );
243     my $library4 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, branchname => 'CCC' } } );
244     my $library5 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, branchname => 'eee' } } );
245     my $library6 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, branchname => 'BBB' } } );
246     my $library7 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, branchname => 'DDD' } } );
247     my $library8 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 0, branchname => 'GGG' } } );
248
249     our @branchcodes = map { $_->branchcode } ($library1, $library2, $library3, $library4, $library5, $library6, $library7, $library8);
250
251     Koha::CirculationRules->set_rules(
252         {
253             branchcode => $library1->branchcode,
254             itemtype   => undef,
255             rules => {
256                 holdallowed => 'from_home_library',
257                 hold_fulfillment_policy => 'any',
258                 returnbranch => 'any'
259             }
260         }
261     );
262
263     Koha::CirculationRules->set_rules(
264         {
265             branchcode => $library2->branchcode,
266             itemtype   => undef,
267             rules => {
268                 holdallowed => 'from_local_hold_group',
269                 hold_fulfillment_policy => 'holdgroup',
270                 returnbranch => 'any'
271             }
272         }
273     );
274
275     Koha::CirculationRules->set_rules(
276         {
277             branchcode => $library3->branchcode,
278             itemtype   => undef,
279             rules => {
280                 holdallowed => 'from_local_hold_group',
281                 hold_fulfillment_policy => 'patrongroup',
282                 returnbranch => 'any'
283             }
284         }
285     );
286
287     Koha::CirculationRules->set_rules(
288         {
289             branchcode => $library4->branchcode,
290             itemtype   => undef,
291             rules => {
292                 holdallowed => 'from_any_library',
293                 hold_fulfillment_policy => 'holdingbranch',
294                 returnbranch => 'any'
295             }
296         }
297     );
298
299     Koha::CirculationRules->set_rules(
300         {
301             branchcode => $library5->branchcode,
302             itemtype   => undef,
303             rules => {
304                 holdallowed => 'from_any_library',
305                 hold_fulfillment_policy => 'homebranch',
306                 returnbranch => 'any'
307             }
308         }
309     );
310
311     Koha::CirculationRules->set_rules(
312         {
313             branchcode => $library6->branchcode,
314             itemtype   => undef,
315             rules => {
316                 holdallowed => 'from_home_library',
317                 hold_fulfillment_policy => 'holdgroup',
318                 returnbranch => 'any'
319             }
320         }
321     );
322
323     Koha::CirculationRules->set_rules(
324         {
325             branchcode => $library7->branchcode,
326             itemtype   => undef,
327             rules => {
328                 holdallowed => 'from_local_hold_group',
329                 hold_fulfillment_policy => 'holdingbranch',
330                 returnbranch => 'any'
331             }
332         }
333     );
334
335
336     Koha::CirculationRules->set_rules(
337         {
338             branchcode => $library8->branchcode,
339             itemtype   => undef,
340             rules => {
341                 holdallowed => 'from_any_library',
342                 hold_fulfillment_policy => 'patrongroup',
343                 returnbranch => 'any'
344             }
345         }
346     );
347
348     my $group1_1 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root1->id, branchcode => $library1->branchcode } } );
349     my $group1_2 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root1->id, branchcode => $library2->branchcode } } );
350
351     my $group2_3 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root2->id, branchcode => $library3->branchcode } } );
352     my $group2_4 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root2->id, branchcode => $library4->branchcode } } );
353
354     my $group3_5 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root3->id, branchcode => $library5->branchcode } } );
355     my $group3_6 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root3->id, branchcode => $library6->branchcode } } );
356     my $group3_7 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root3->id, branchcode => $library7->branchcode } } );
357     my $group3_8 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root3->id, branchcode => $library8->branchcode } } );
358
359     my $biblio1  = $builder->build_sample_biblio({ title => '1' });
360     my $biblio2  = $builder->build_sample_biblio({ title => '2' });
361
362     throws_ok
363       { $biblio1->pickup_locations }
364       'Koha::Exceptions::MissingParameter',
365       'Exception thrown on missing parameter';
366
367     is( $@->parameter, 'patron', 'Exception param correctly set' );
368
369     my $item1_1  = $builder->build_sample_item({
370         biblionumber     => $biblio1->biblionumber,
371         homebranch       => $library1->branchcode,
372         holdingbranch    => $library2->branchcode,
373     })->store;
374
375     my $item1_3  = $builder->build_sample_item({
376         biblionumber     => $biblio1->biblionumber,
377         homebranch       => $library3->branchcode,
378         holdingbranch    => $library4->branchcode,
379     })->store;
380
381     my $item1_7  = $builder->build_sample_item({
382         biblionumber     => $biblio1->biblionumber,
383         homebranch       => $library7->branchcode,
384         holdingbranch    => $library4->branchcode,
385     })->store;
386
387     my $item2_2  = $builder->build_sample_item({
388         biblionumber     => $biblio2->biblionumber,
389         homebranch       => $library2->branchcode,
390         holdingbranch    => $library1->branchcode,
391     })->store;
392
393     my $item2_4  = $builder->build_sample_item({
394         biblionumber     => $biblio2->biblionumber,
395         homebranch       => $library4->branchcode,
396         holdingbranch    => $library3->branchcode,
397     })->store;
398
399     my $item2_6  = $builder->build_sample_item({
400         biblionumber     => $biblio2->biblionumber,
401         homebranch       => $library6->branchcode,
402         holdingbranch    => $library4->branchcode,
403     })->store;
404
405     my $patron1 = $builder->build_object( { class => 'Koha::Patrons', value => { firstname=>'1', branchcode => $library1->branchcode } } );
406     my $patron8 = $builder->build_object( { class => 'Koha::Patrons', value => { firstname=>'8', branchcode => $library8->branchcode } } );
407
408     my $results = {
409         "ItemHomeLibrary-1-1" => 6,
410         "ItemHomeLibrary-1-8" => 1,
411         "ItemHomeLibrary-2-1" => 2,
412         "ItemHomeLibrary-2-8" => 0,
413         "PatronLibrary-1-1" => 6,
414         "PatronLibrary-1-8" => 3,
415         "PatronLibrary-2-1" => 0,
416         "PatronLibrary-2-8" => 3,
417     };
418
419     sub _doTest {
420         my ( $cbranch, $biblio, $patron, $results ) = @_;
421         t::lib::Mocks::mock_preference('ReservesControlBranch', $cbranch);
422
423         my @pl = map {
424             my $pickup_location = $_;
425             grep { $pickup_location->branchcode eq $_ } @branchcodes
426         } $biblio->pickup_locations( { patron => $patron } )->as_list;
427
428         ok(
429             scalar(@pl) == $results->{ $cbranch . '-'
430                   . $biblio->title . '-'
431                   . $patron->firstname },
432             'ReservesControlBranch: '
433               . $cbranch
434               . ', biblio'
435               . $biblio->title
436               . ', patron'
437               . $patron->firstname
438               . ' should return '
439               . $results->{ $cbranch . '-'
440                   . $biblio->title . '-'
441                   . $patron->firstname }
442               . ' but returns '
443               . scalar(@pl)
444         );
445     }
446
447     foreach my $cbranch ('ItemHomeLibrary','PatronLibrary') {
448         my $cache = Koha::Cache::Memory::Lite->get_instance();
449         $cache->flush(); # needed since we change ReservesControlBranch
450         foreach my $biblio ($biblio1, $biblio2) {
451             foreach my $patron ($patron1, $patron8) {
452                 _doTest($cbranch, $biblio, $patron, $results);
453             }
454         }
455     }
456
457     my @pl_names = map { $_->branchname } $biblio1->pickup_locations( { patron => $patron1 } )->as_list;
458     my $pl_ori_str = join('|', @pl_names);
459     my $pl_sorted_str = join('|', sort { lc($a) cmp lc($b) } @pl_names);
460     ok(
461         $pl_ori_str eq $pl_sorted_str,
462         'Libraries must be sorted by name'
463     );
464     $schema->storage->txn_rollback;
465 };
466
467 subtest 'to_api() tests' => sub {
468
469     $schema->storage->txn_begin;
470
471     my $biblio = $builder->build_sample_biblio();
472     my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
473
474     my $biblioitem_api = $biblio->biblioitem->to_api;
475     my $biblio_api     = $biblio->to_api;
476
477     plan tests => (scalar keys %{ $biblioitem_api }) + 1;
478
479     foreach my $key ( keys %{ $biblioitem_api } ) {
480         is( $biblio_api->{$key}, $biblioitem_api->{$key}, "$key is added to the biblio object" );
481     }
482
483     $biblio_api = $biblio->to_api({ embed => { items => {} } });
484     is_deeply( $biblio_api->{items}, [ $item->to_api ], 'Item correctly embedded' );
485
486     $schema->storage->txn_rollback;
487 };
488
489 subtest 'suggestions() tests' => sub {
490
491     plan tests => 3;
492
493     $schema->storage->txn_begin;
494
495     my $biblio     = $builder->build_sample_biblio();
496
497     is( ref($biblio->suggestions), 'Koha::Suggestions', 'Return type is correct' );
498
499     is_deeply(
500         $biblio->suggestions->unblessed,
501         [],
502         '->suggestions returns an empty Koha::Suggestions resultset'
503     );
504
505     my $suggestion = $builder->build_object(
506         {
507             class => 'Koha::Suggestions',
508             value => { biblionumber => $biblio->biblionumber }
509         }
510     );
511
512     my $suggestions = $biblio->suggestions->unblessed;
513
514     is_deeply(
515         $biblio->suggestions->unblessed,
516         [ $suggestion->unblessed ],
517         '->suggestions returns the related Koha::Suggestion objects'
518     );
519
520     $schema->storage->txn_rollback;
521 };
522
523 subtest 'get_marc_components() tests' => sub {
524
525     plan tests => 5;
526
527     $schema->storage->txn_begin;
528
529     my ($host_bibnum) = C4::Biblio::AddBiblio(host_record(), '');
530     my $host_biblio = Koha::Biblios->find($host_bibnum);
531     t::lib::Mocks::mock_preference( 'SearchEngine', 'Zebra' );
532     my $search_mod = Test::MockModule->new( 'Koha::SearchEngine::Zebra::Search' );
533     $search_mod->mock( 'search_compat', \&search_component_record2 );
534
535     my $components = $host_biblio->get_marc_components;
536     is( ref($components), 'ARRAY', 'Return type is correct' );
537
538     is_deeply(
539         $components,
540         [],
541         '->get_marc_components returns an empty ARRAY'
542     );
543
544     $search_mod->unmock( 'search_compat');
545     $search_mod->mock( 'search_compat', \&search_component_record1 );
546     my $component_record = component_record1()->as_xml();
547
548     is_deeply(
549         $host_biblio->get_marc_components,
550         [$component_record],
551         '->get_marc_components returns the related component part record'
552     );
553     $search_mod->unmock( 'search_compat');
554
555     $search_mod->mock( 'search_compat',
556         sub { Koha::Exception->throw("error searching analytics") }
557     );
558     warning_like { $components = $host_biblio->get_marc_components }
559         qr{Warning from search_compat: .* 'error searching analytics'};
560
561     is_deeply(
562         $host_biblio->object_messages,
563         [
564             {
565                 type    => 'error',
566                 message => 'component_search',
567                 payload => "Exception 'Koha::Exception' thrown 'error searching analytics'\n"
568             }
569         ]
570     );
571     $search_mod->unmock( 'search_compat');
572
573     $schema->storage->txn_rollback;
574 };
575
576 subtest 'get_components_query' => sub {
577     plan tests => 12;
578
579     my $biblio = $builder->build_sample_biblio();
580     my $biblionumber = $biblio->biblionumber;
581     my $record = $biblio->metadata->record;
582
583     foreach my $engine ('Zebra','Elasticsearch'){
584         t::lib::Mocks::mock_preference( 'SearchEngine', $engine );
585
586         t::lib::Mocks::mock_preference( 'UseControlNumber', '0' );
587         t::lib::Mocks::mock_preference( 'ComponentSortField', 'author' );
588         t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'za' );
589         my ( $comp_query, $comp_query_str, $comp_sort ) = $biblio->get_components_query;
590         is($comp_query_str, 'Host-item:("Some boring read")', "$engine: UseControlNumber disabled");
591         is($comp_sort, "author_za", "$engine: UseControlNumber disabled sort is correct");
592
593         t::lib::Mocks::mock_preference( 'UseControlNumber', '1' );
594         t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'az' );
595         my $marc_001_field = MARC::Field->new('001', $biblionumber);
596         $record->append_fields($marc_001_field);
597         C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
598         $biblio = Koha::Biblios->find( $biblio->biblionumber);
599
600         ( $comp_query, $comp_query_str, $comp_sort ) = $biblio->get_components_query;
601         is($comp_query_str, "(rcn:\"$biblionumber\" AND (bib-level:a OR bib-level:b))", "$engine: UseControlNumber enabled without MarcOrgCode");
602         is($comp_sort, "author_az", "$engine: UseControlNumber enabled without MarcOrgCode sort is correct");
603
604         my $marc_003_field = MARC::Field->new('003', 'OSt');
605         $record->append_fields($marc_003_field);
606         C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
607         $biblio = Koha::Biblios->find( $biblio->biblionumber);
608
609         t::lib::Mocks::mock_preference( 'ComponentSortField', 'title' );
610         t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'asc' );
611         ( $comp_query, $comp_query_str, $comp_sort ) = $biblio->get_components_query;
612         is($comp_query_str, "(((rcn:\"$biblionumber\" AND cni:\"OSt\") OR rcn:\"OSt $biblionumber\") AND (bib-level:a OR bib-level:b))", "$engine: UseControlNumber enabled with MarcOrgCode");
613         is($comp_sort, "title_asc", "$engine: UseControlNumber enabled with MarcOrgCode sort if correct");
614         $record->delete_field($marc_003_field);
615     }
616 };
617
618 subtest 'get_volumes_query' => sub {
619     plan tests => 3;
620
621     my $biblio       = $builder->build_sample_biblio();
622     my $biblionumber = $biblio->biblionumber;
623     my $record       = $biblio->metadata->record;
624
625     # Ensure our mocked record is captured as a set or monographic series
626     my $ldr = $record->leader();
627     substr( $ldr, 19, 1 ) = 'a';
628     $record->leader($ldr);
629     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
630     $biblio = Koha::Biblios->find( $biblio->biblionumber );
631
632     t::lib::Mocks::mock_preference( 'UseControlNumber', '0' );
633     is(
634         $biblio->get_volumes_query,
635         "(title-series,phr:(\"Some boring read\") OR Host-item,phr:(\"Some boring read\") NOT (bib-level:a OR bib-level:b))",
636         "UseControlNumber disabled"
637     );
638
639     t::lib::Mocks::mock_preference( 'UseControlNumber', '1' );
640     my $marc_001_field = MARC::Field->new( '001', $biblionumber );
641     $record->append_fields($marc_001_field);
642     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
643     $biblio = Koha::Biblios->find( $biblio->biblionumber );
644
645
646     is(
647         $biblio->get_volumes_query, "(rcn:$biblionumber NOT (bib-level:a OR bib-level:b))",
648         "UseControlNumber enabled without MarcOrgCode"
649     );
650
651     my $marc_003_field = MARC::Field->new( '003', 'OSt' );
652     $record->append_fields($marc_003_field);
653     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
654     $biblio = Koha::Biblios->find( $biblio->biblionumber );
655
656     is(
657         $biblio->get_volumes_query,
658         "(((rcn:$biblionumber AND cni:OSt) OR rcn:\"OSt $biblionumber\") NOT (bib-level:a OR bib-level:b))",
659         "UseControlNumber enabled with MarcOrgCode"
660     );
661 };
662
663 subtest 'orders() and active_orders() tests' => sub {
664
665     plan tests => 5;
666
667     $schema->storage->txn_begin;
668
669     my $biblio = $builder->build_sample_biblio();
670
671     my $orders        = $biblio->orders;
672     my $active_orders = $biblio->active_orders;
673
674     is( ref($orders), 'Koha::Acquisition::Orders', 'Result type is correct' );
675     is( $biblio->orders->count, $biblio->active_orders->count, '->orders->count returns the count for the resultset' );
676
677     # Add a couple orders
678     foreach (1..2) {
679         $builder->build_object(
680             {
681                 class => 'Koha::Acquisition::Orders',
682                 value => {
683                     biblionumber => $biblio->biblionumber,
684                     datecancellationprinted => '2019-12-31'
685                 }
686             }
687         );
688     }
689
690     $builder->build_object(
691         {
692             class => 'Koha::Acquisition::Orders',
693             value => {
694                 biblionumber => $biblio->biblionumber,
695                 datecancellationprinted => undef
696             }
697         }
698     );
699
700     $orders = $biblio->orders;
701     $active_orders = $biblio->active_orders;
702
703     is( ref($orders), 'Koha::Acquisition::Orders', 'Result type is correct' );
704     is( ref($active_orders), 'Koha::Acquisition::Orders', 'Result type is correct' );
705     is( $orders->count, $active_orders->count + 2, '->active_orders->count returns the rigt count' );
706
707     $schema->storage->txn_rollback;
708 };
709
710 subtest 'tickets() tests' => sub {
711
712     plan tests => 4;
713
714     $schema->storage->txn_begin;
715
716     my $biblio = $builder->build_sample_biblio();
717     my $tickets = $biblio->tickets;
718     is( ref($tickets), 'Koha::Tickets', 'Koha::Biblio->tickets should return a Koha::Tickets object' );
719     is( $tickets->count, 0, 'Koha::Biblio->tickets should return a count of 0 when there are no related tickets' );
720
721     # Add two tickets
722     foreach (1..2) {
723         $builder->build_object(
724             {
725                 class => 'Koha::Tickets',
726                 value => { biblio_id => $biblio->biblionumber }
727             }
728         );
729     }
730
731     $tickets = $biblio->tickets;
732     is( ref($tickets), 'Koha::Tickets', 'Koha::Biblio->tickets should return a Koha::Tickets object' );
733     is( $tickets->count, 2, 'Koha::Biblio->tickets should return the correct number of tickets' );
734
735     $schema->storage->txn_rollback;
736 };
737
738 subtest 'subscriptions() tests' => sub {
739
740     plan tests => 4;
741
742     $schema->storage->txn_begin;
743
744     my $biblio = $builder->build_sample_biblio;
745
746     my $subscriptions = $biblio->subscriptions;
747     is( ref($subscriptions), 'Koha::Subscriptions',
748         'Koha::Biblio->subscriptions should return a Koha::Subscriptions object'
749     );
750     is( $subscriptions->count, 0, 'Koha::Biblio->subscriptions should return the correct number of subscriptions');
751
752     # Add two subscriptions
753     foreach (1..2) {
754         $builder->build_object(
755             {
756                 class => 'Koha::Subscriptions',
757                 value => { biblionumber => $biblio->biblionumber }
758             }
759         );
760     }
761
762     $subscriptions = $biblio->subscriptions;
763     is( ref($subscriptions), 'Koha::Subscriptions',
764         'Koha::Biblio->subscriptions should return a Koha::Subscriptions object'
765     );
766     is( $subscriptions->count, 2, 'Koha::Biblio->subscriptions should return the correct number of subscriptions');
767
768     $schema->storage->txn_rollback;
769 };
770
771 subtest 'get_marc_notes() MARC21 tests' => sub {
772     plan tests => 14;
773
774     $schema->storage->txn_begin;
775
776     t::lib::Mocks::mock_preference( 'NotesToHide', '520' );
777
778     my $av = $builder->build_object( { class => 'Koha::AuthorisedValues' } );
779
780     my $biblio = $builder->build_sample_biblio;
781     my $record = $biblio->metadata->record;
782     $record->append_fields(
783         MARC::Field->new( '500', '', '', a => 'Note1' ),
784         MARC::Field->new( '505', '', '', a => 'Note2', u => 'http://someserver.com' ),
785         MARC::Field->new( '520', '', '', a => 'Note3 skipped' ),
786         MARC::Field->new( '541', '0', '', a => 'Note4 skipped on opac' ),
787         MARC::Field->new( '544', '', '', a => 'Note5' ),
788         MARC::Field->new( '590', '', '', a => $av->authorised_value ),
789         MARC::Field->new( '545', '', '', a => 'Invisible on OPAC' ),
790     );
791
792     my $mss = Koha::MarcSubfieldStructures->find({tagfield => "590", tagsubfield => "a", frameworkcode => $biblio->frameworkcode });
793     $mss->update({ authorised_value => $av->category });
794
795     $mss = Koha::MarcSubfieldStructures->find({tagfield => "545", tagsubfield => "a", frameworkcode => $biblio->frameworkcode });
796     $mss->update({ hidden => 1 });
797
798     my $cache = Koha::Caches->get_instance;
799     $cache->clear_from_cache("MarcStructure-0-");
800     $cache->clear_from_cache("MarcStructure-1-");
801     $cache->clear_from_cache("MarcSubfieldStructure-");
802     $cache->clear_from_cache("MarcCodedFields-");
803
804     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
805     $biblio = Koha::Biblios->find( $biblio->biblionumber);
806
807     my $notes = $biblio->get_marc_notes;
808     is( $notes->[0]->{marcnote}, 'Note1', 'First note' );
809     is( $notes->[1]->{marcnote}, 'Note2', 'Second note' );
810     is( $notes->[2]->{marcnote}, 'http://someserver.com', 'URL separated' );
811     is( $notes->[3]->{marcnote}, 'Note4 skipped on opac',"Note shows if not opac (Hidden by Indicator)" );
812     is( $notes->[4]->{marcnote}, 'Note5', 'Fifth note' );
813     is( $notes->[5]->{marcnote}, $av->lib, 'Authorised value is correctly parsed to show description rather than code' );
814     is( $notes->[6]->{marcnote}, 'Invisible on OPAC', 'Note shows if not opac (Hidden by framework)' );
815     is( @$notes, 7, 'No more notes' );
816     $notes = $biblio->get_marc_notes({ opac => 1 });
817     is( $notes->[0]->{marcnote}, 'Note1', 'First note' );
818     is( $notes->[1]->{marcnote}, 'Note2', 'Second note' );
819     is( $notes->[2]->{marcnote}, 'http://someserver.com', 'URL separated' );
820     is( $notes->[3]->{marcnote}, 'Note5', 'Fifth note shows after fourth skipped' );
821     is( $notes->[4]->{marcnote}, $av->lib_opac, 'Authorised value is correctly parsed for OPAC to show description rather than code' );
822     is( @$notes, 5, 'No more notes' );
823
824     $cache->clear_from_cache("MarcStructure-0-");
825     $cache->clear_from_cache("MarcStructure-1-");
826     $cache->clear_from_cache("MarcSubfieldStructure-");
827     $cache->clear_from_cache("MarcCodedFields-");
828
829     $schema->storage->txn_rollback;
830 };
831
832 subtest 'get_marc_notes() UNIMARC tests' => sub {
833     plan tests => 3;
834
835     $schema->storage->txn_begin;
836
837     t::lib::Mocks::mock_preference( 'NotesToHide', '310' );
838     t::lib::Mocks::mock_preference( 'marcflavour', 'UNIMARC' );
839
840     my $biblio = $builder->build_sample_biblio;
841     my $record = $biblio->metadata->record;
842     $record->append_fields(
843         MARC::Field->new( '300', '', '', a => 'Note1' ),
844         MARC::Field->new( '300', '', '', a => 'Note2' ),
845         MARC::Field->new( '310', '', '', a => 'Note3 skipped' ),
846     );
847     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
848     $biblio = Koha::Biblios->find( $biblio->biblionumber);
849     my $notes = $biblio->get_marc_notes({ marcflavour => 'UNIMARC' });
850     is( $notes->[0]->{marcnote}, 'Note1', 'First note' );
851     is( $notes->[1]->{marcnote}, 'Note2', 'Second note' );
852     is( @$notes, 2, 'No more notes' );
853
854     t::lib::Mocks::mock_preference( 'marcflavour', 'MARC21' );
855     $schema->storage->txn_rollback;
856 };
857
858 subtest 'host_items() tests' => sub {
859     plan tests => 7;
860
861     $schema->storage->txn_begin;
862
863     my $biblio = $builder->build_sample_biblio( { frameworkcode => '' } );
864
865     t::lib::Mocks::mock_preference( 'EasyAnalyticalRecords', 1 );
866     my $host_items = $biblio->host_items;
867     is( ref($host_items),   'Koha::Items' );
868     is( $host_items->count, 0 );
869
870     my $item_1 =
871       $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
872     my $host_item_1 = $builder->build_sample_item;
873     my $host_item_2 = $builder->build_sample_item;
874
875     my $record = $biblio->metadata->record;
876     $record->append_fields(
877         MARC::Field->new(
878             '773', '', '',
879             9 => $host_item_1->itemnumber,
880             9 => $host_item_2->itemnumber
881         ),
882     );
883     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
884     $biblio = $biblio->get_from_storage;
885     $host_items = $biblio->host_items;
886     is( $host_items->count, 2 );
887     is_deeply( [ $host_items->get_column('itemnumber') ],
888         [ $host_item_1->itemnumber, $host_item_2->itemnumber ] );
889
890     t::lib::Mocks::mock_preference( 'EasyAnalyticalRecords', 0 );
891     $host_items = $biblio->host_items;
892     is( ref($host_items),   'Koha::Items' );
893     is( $host_items->count, 0 );
894
895     subtest 'test host_items param in items()' => sub {
896         plan tests => 4;
897
898         my $items = $biblio->items;
899         is( $items->count, 1, "Without host_items param we only get the items on the biblio");
900         $items = $biblio->items({ host_items => 1 });
901         is( $items->count, 3, "With param host_items we get the biblio items plus analytics");
902         is( ref($items), 'Koha::Items', "We correctly get an Items object");
903         is_deeply( [ $items->get_column('itemnumber') ],
904             [ $item_1->itemnumber, $host_item_1->itemnumber, $host_item_2->itemnumber ] );
905     };
906
907     $schema->storage->txn_rollback;
908 };
909
910 subtest 'article_requests() tests' => sub {
911
912     plan tests => 3;
913
914     $schema->storage->txn_begin;
915
916     my $item   = $builder->build_sample_item;
917     my $biblio = $item->biblio;
918
919     my $article_requests = $biblio->article_requests;
920     is( ref($article_requests), 'Koha::ArticleRequests',
921         'In scalar context, type is correct' );
922     is( $article_requests->count, 0, 'No article requests' );
923
924     foreach my $i ( 0 .. 3 ) {
925
926         my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
927
928         Koha::ArticleRequest->new(
929             {
930                 borrowernumber => $patron->id,
931                 biblionumber   => $biblio->id,
932                 itemnumber     => $item->id,
933                 title          => $biblio->title,
934             }
935         )->request;
936     }
937
938     $article_requests = $biblio->article_requests;
939     is( $article_requests->count, 4, '4 article requests' );
940
941     $schema->storage->txn_rollback;
942 };
943
944 subtest 'current_checkouts() and old_checkouts() tests' => sub {
945
946     plan tests => 4;
947
948     $schema->storage->txn_begin;
949
950     my $library = $builder->build_object({ class => 'Koha::Libraries' });
951
952     my $patron_1 = $builder->build_object({ class => 'Koha::Patrons' });
953     my $patron_2 = $builder->build_object({ class => 'Koha::Patrons' });
954
955     my $item_1 = $builder->build_sample_item;
956     my $item_2 = $builder->build_sample_item({ biblionumber => $item_1->biblionumber });
957
958     t::lib::Mocks::mock_userenv({ branchcode => $library->id });
959
960     AddIssue( $patron_1, $item_1->barcode );
961     AddIssue( $patron_1, $item_2->barcode );
962
963     AddReturn( $item_1->barcode );
964     AddIssue( $patron_2, $item_1->barcode );
965
966     my $biblio = $item_1->biblio;
967     my $current_checkouts = $biblio->current_checkouts;
968     my $old_checkouts = $biblio->old_checkouts;
969
970     is( ref($current_checkouts), 'Koha::Checkouts', 'Type is correct' );
971     is( ref($old_checkouts), 'Koha::Old::Checkouts', 'Type is correct' );
972
973     is( $current_checkouts->count, 2, 'Count is correct for current checkouts' );
974     is( $old_checkouts->count, 1, 'Count is correct for old checkouts' );
975
976     $schema->storage->txn_rollback;
977 };
978
979 subtest 'get_marc_contributors() tests' => sub {
980
981     plan tests => 2;
982
983     $schema->storage->txn_begin;
984
985     my $biblio = $builder->build_sample_biblio({ author => 'Main author' });
986     my $record = $biblio->metadata->record;
987
988     # add author information
989     my $field = MARC::Field->new('700','1','','a' => 'Jefferson, Thomas');
990     $record->append_fields($field);
991     $field = MARC::Field->new('701','1','','d' => 'Secondary author 2');
992     $record->append_fields($field);
993
994     # get record
995     C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
996     $biblio = Koha::Biblios->find( $biblio->biblionumber );
997
998     is( @{$biblio->get_marc_authors}, 3, 'get_marc_authors retrieves correct number of author subfields' );
999     is( @{$biblio->get_marc_contributors}, 2, 'get_marc_contributors retrieves correct number of author subfields' );
1000     $schema->storage->txn_rollback;
1001 };
1002
1003 subtest 'Recalls tests' => sub {
1004
1005     plan tests => 13;
1006
1007     $schema->storage->txn_begin;
1008
1009     my $item1 = $builder->build_sample_item;
1010     my $biblio = $item1->biblio;
1011     my $branchcode = $item1->holdingbranch;
1012     my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1013     my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1014     my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1015     my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
1016     t::lib::Mocks::mock_userenv({ patron => $patron1 });
1017
1018     my $recall1 = Koha::Recall->new(
1019         {   patron_id         => $patron1->borrowernumber,
1020             created_date      => \'NOW()',
1021             biblio_id         => $biblio->biblionumber,
1022             pickup_library_id => $branchcode,
1023             item_id           => $item1->itemnumber,
1024             expiration_date   => undef,
1025             item_level        => 1
1026         }
1027     )->store;
1028     my $recall2 = Koha::Recall->new(
1029         {   patron_id         => $patron2->borrowernumber,
1030             created_date      => \'NOW()',
1031             biblio_id         => $biblio->biblionumber,
1032             pickup_library_id => $branchcode,
1033             item_id           => undef,
1034             expiration_date   => undef,
1035             item_level        => 0
1036         }
1037     )->store;
1038     my $recall3 = Koha::Recall->new(
1039         {   patron_id         => $patron3->borrowernumber,
1040             created_date      => \'NOW()',
1041             biblio_id         => $biblio->biblionumber,
1042             pickup_library_id => $branchcode,
1043             item_id           => $item1->itemnumber,
1044             expiration_date   => undef,
1045             item_level        => 1
1046         }
1047     )->store;
1048
1049     my $recalls = $biblio->recalls;
1050     is( $recalls->count, 3, 'Correctly get number of recalls for biblio' );
1051
1052     $recall1->set_cancelled;
1053     $recall2->set_expired({ interface => 'COMMANDLINE' });
1054
1055     is( $recalls->count, 3, 'Correctly get number of recalls for biblio' );
1056     is( $recalls->filter_by_current->count, 1, 'Correctly get number of active recalls for biblio' );
1057
1058     t::lib::Mocks::mock_preference('UseRecalls', 0);
1059     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
1060
1061     t::lib::Mocks::mock_preference("UseRecalls", 1);
1062     $item1->update({ notforloan => 1 });
1063     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
1064
1065     $item1->update({ notforloan => 0 });
1066     Koha::CirculationRules->set_rules({
1067         branchcode => $branchcode,
1068         categorycode => $patron1->categorycode,
1069         itemtype => $item1->effective_itemtype,
1070         rules => {
1071             recalls_allowed => 0,
1072             recalls_per_record => 1,
1073             on_shelf_recalls => 'all',
1074         },
1075     });
1076     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
1077
1078     Koha::CirculationRules->set_rules({
1079         branchcode => $branchcode,
1080         categorycode => $patron1->categorycode,
1081         itemtype => $item1->effective_itemtype,
1082         rules => {
1083             recalls_allowed => 1,
1084             recalls_per_record => 1,
1085             on_shelf_recalls => 'all',
1086         },
1087     });
1088     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
1089     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
1090
1091     $recall1->set_cancelled;
1092     C4::Circulation::AddIssue( $patron1, $item2->barcode );
1093     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
1094
1095     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
1096
1097     Koha::CirculationRules->set_rules({
1098         branchcode => $branchcode,
1099         categorycode => $patron1->categorycode,
1100         itemtype => $item1->effective_itemtype,
1101         rules => {
1102             recalls_allowed => 1,
1103             recalls_per_record => 1,
1104             on_shelf_recalls => 'any',
1105         },
1106     });
1107     C4::Circulation::AddReturn( $item2->barcode, $branchcode );
1108     is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
1109
1110     $recall2->set_cancelled;
1111     C4::Circulation::AddIssue( $patron2, $item2->barcode );
1112     C4::Circulation::AddIssue( $patron2, $item1->barcode );
1113     is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
1114
1115     $item1->update({ withdrawn => 1 });
1116     is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
1117
1118     $schema->storage->txn_rollback;
1119 };
1120
1121 subtest 'ill_requests() tests' => sub {
1122
1123     plan tests => 3;
1124
1125     $schema->storage->txn_begin;
1126
1127     my $biblio = $builder->build_sample_biblio;
1128
1129     my $rs = $biblio->ill_requests;
1130     is( ref($rs), 'Koha::Illrequests' );
1131     is( $rs->count, 0, 'No linked requests' );
1132
1133     foreach ( 1..10 ) {
1134         $builder->build_object(
1135             {
1136                 class => 'Koha::Illrequests',
1137                 value => { biblio_id => $biblio->id }
1138             }
1139         );
1140     }
1141
1142     is( $biblio->ill_requests->count, 10, 'Linked requests are present' );
1143
1144     $schema->storage->txn_rollback;
1145 };
1146
1147 subtest 'item_groups() tests' => sub {
1148
1149     plan tests => 6;
1150
1151     $schema->storage->txn_begin;
1152
1153     my $biblio = $builder->build_sample_biblio();
1154
1155     my @item_groups = $biblio->item_groups->as_list;
1156     is( scalar(@item_groups), 0, 'Got zero item groups');
1157
1158     my $item_group_1 = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id } )->store();
1159
1160     @item_groups = $biblio->item_groups->as_list;
1161     is( scalar(@item_groups), 1, 'Got one item group');
1162     is( $item_groups[0]->id, $item_group_1->id, 'Got correct item group');
1163
1164     my $item_group_2 = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id } )->store();
1165
1166     @item_groups = $biblio->item_groups->as_list;
1167     is( scalar(@item_groups), 2, 'Got two item groups');
1168     is( $item_groups[0]->id, $item_group_1->id, 'Got correct item group 1');
1169     is( $item_groups[1]->id, $item_group_2->id, 'Got correct item group 2');
1170
1171     $schema->storage->txn_rollback;
1172 };
1173
1174 subtest 'normalized_isbn' => sub {
1175     plan tests => 1;
1176
1177     # We will move the tests from GetNormalizedISBN here when it will get replaced
1178     my $biblio = $builder->build_sample_biblio();
1179     $biblio->biblioitem->set( { isbn => '9781250067128 | 125006712X' } )->store;
1180     is(
1181         $biblio->normalized_isbn, C4::Koha::GetNormalizedISBN( $biblio->biblioitem->isbn ),
1182         'normalized_isbn is a wrapper around C4::Koha::GetNormalizedISBN'
1183     );
1184 };
1185
1186 subtest 'normalized_upc' => sub {
1187     plan tests => 1;
1188
1189     # We will move the tests from GetNormalizedUPC here when it will get replaced
1190     # Note that only a single test exist and it's not really meaningful...
1191     my $biblio = $builder->build_sample_biblio();
1192     is(
1193         $biblio->normalized_upc, C4::Koha::GetNormalizedUPC( $biblio->metadata->record ),
1194         'normalized_upc is a wrapper around C4::Koha::GetNormalizedUPC'
1195     );
1196 };
1197
1198 subtest 'normalized_oclc' => sub {
1199     plan tests => 1;
1200
1201     # We will move the tests from GetNormalizedOCLC here when it will get replaced
1202     # Note that only a single test exist and it's not really meaningful...
1203     my $biblio = $builder->build_sample_biblio();
1204     is(
1205         $biblio->normalized_oclc, C4::Koha::GetNormalizedOCLCNumber( $biblio->metadata->record ),
1206         'normalized_oclc is a wrapper around C4::Koha::GetNormalizedOCLCNumber'
1207     );
1208 };
1209
1210 subtest 'ratings' => sub {
1211     plan tests => 1;
1212     # See t/db_dependent/Koha/Ratings.t
1213     ok(1);
1214 };
1215
1216 subtest 'opac_summary_html' => sub {
1217
1218     plan tests => 2;
1219
1220     my $author = 'my author';
1221     my $title  = 'my title';
1222     my $isbn   = '9781250067128 | 125006712X';
1223     my $biblio = $builder->build_sample_biblio( { author => $author, title => $title } );
1224     $biblio->biblioitem->set( { isbn => '9781250067128 | 125006712X' } )->store;
1225
1226     t::lib::Mocks::mock_preference( 'OPACMySummaryHTML', '' );
1227     is( $biblio->opac_summary_html, '', 'opac_summary_html returns empty string if pref is off' );
1228
1229     t::lib::Mocks::mock_preference(
1230         'OPACMySummaryHTML',
1231         'Replace {AUTHOR}, {TITLE}, {ISBN} AND {BIBLIONUMBER} please'
1232     );
1233     is(
1234         $biblio->opac_summary_html,
1235         sprintf( 'Replace %s, %s, %s AND %s please', $author, $title, $biblio->normalized_isbn, $biblio->biblionumber ),
1236         'opac_summary_html replaces the different patterns'
1237     );
1238 };
1239
1240 sub component_record1 {
1241     my $marc = MARC::Record->new;
1242     $marc->append_fields(
1243         MARC::Field->new( '001', '3456' ),
1244         MARC::Field->new( '245', '', '', a => 'Some title 1' ),
1245         MARC::Field->new( '773', '', '', w => '(FIRST)1234' ),
1246     );
1247     return $marc;
1248 }
1249 sub search_component_record1 {
1250     my @results = ( component_record1()->as_xml() );
1251     return ( undef, { biblioserver => { RECORDS => \@results, hits => 1 } }, 1 );
1252 }
1253
1254 sub search_component_record2 {
1255     my @results;
1256     return ( undef, { biblioserver => { RECORDS => \@results, hits => 0 } }, 0 );
1257 }
1258
1259 sub host_record {
1260     my $marc = MARC::Record->new;
1261     $marc->append_fields(
1262         MARC::Field->new( '001', '1234' ),
1263         MARC::Field->new( '003', 'FIRST' ),
1264         MARC::Field->new( '245', '', '', a => 'Some title' ),
1265     );
1266     return $marc;
1267 }