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>.
25 use C4::Biblio qw( AddBiblio ModBiblio DelBiblio );
29 use Test::More tests => 24;
32 use Koha::MarcOverlayRules;
36 my $schema = Koha::Database->schema;
37 $schema->storage->txn_begin;
39 t::lib::Mocks::mock_preference('MARCOverlayRules', '1');
41 Koha::MarcOverlayRules->search->delete;
45 my $record = MARC::Record->new;
47 for my $f ( @$fields ) {
50 for my $i ( 1 .. (scalar(@$f) / 2) ) {
52 push @subfields, $f->[$ii - 1], $f->[$ii];
54 push @marc_fields, MARC::Field->new($tag, '','', @subfields);
57 $record->append_fields(@marc_fields);
63 my $orig_record = build_record([
64 [ '250', 'a', '250 bottles of beer on the wall' ],
65 [ '250', 'a', '256 bottles of beer on the wall' ],
66 [ '500', 'a', 'One bottle of beer in the fridge' ],
70 my $incoming_record = build_record(
72 ['250', 'a', '256 bottles of beer on the wall'], # Unchanged
73 ['250', 'a', '251 bottles of beer on the wall'], # Appended
74 #['250', 'a', '250 bottles of beer on the wall'], # Removed
75 #['500', 'a', 'One bottle of beer in the fridge'], # Deleted
76 ['501', 'a', 'One cold bottle of beer in the fridge'], # Added
77 ['501', 'a', 'Two cold bottles of beer in the fridge'], # Added
81 # Test default behavior when MARCOverlayRules is enabled, but no rules defined (overwrite)
82 subtest 'No rule defined' => sub {
85 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
88 $merged_record->as_formatted,
89 $incoming_record->as_formatted,
90 'Incoming record used as it with no rules defined'
95 my $rule = Koha::MarcOverlayRules->find_or_create({
105 subtest 'Record fields has been protected when matched merge all rule operations are set to "0"' => sub {
108 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
111 $merged_record->as_formatted,
112 $orig_record->as_formatted,
113 'Record not modified if all op=0'
118 subtest '"Add new" - Only new fields has been added when add = 1, append = 0, remove = 0, delete = 0' => sub {
130 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
132 my $expected_record = build_record([
133 [ '250', 'a', '250 bottles of beer on the wall' ], # original
134 [ '250', 'a', '256 bottles of beer on the wall' ], # original
135 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
136 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
137 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
140 $merged_record->as_formatted,
141 $expected_record->as_formatted,
142 'Add fields from the incoming record that are not in the original record'
147 subtest 'Only appended fields has been added when add = 0, append = 1, remove = 0, delete = 0' => sub {
159 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
161 my $expected_record = build_record([
162 [ '250', 'a', '250 bottles of beer on the wall' ], # original
163 [ '250', 'a', '256 bottles of beer on the wall' ], # original
164 # "251" field has been appended
165 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
166 # "500" field has retained its original value
167 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
171 $merged_record->as_formatted,
172 $expected_record->as_formatted,
173 'Add fields from the incoming record that are in the original record'
178 subtest '"Add and append" - add = 1, append = 1, remove = 0, delete = 0' => sub {
190 my $expected_record = build_record([
191 [ '250', 'a', '250 bottles of beer on the wall' ], # original
192 [ '250', 'a', '256 bottles of beer on the wall' ], # original
193 # "251" field has been appended
194 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
195 # "500" field has retained its original value
196 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
197 # "501" fields have been added
198 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
199 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
202 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
205 $merged_record->as_formatted,
206 $expected_record->as_formatted,
207 'Appended and added fields have been added'
212 subtest 'Record fields has been only removed when add = 0, append = 0, remove = 1, delete = 0' => sub {
224 # Warning - not obvious as the 500 is untouched
225 my $expected_record = build_record([
226 # "250" field has been removed
227 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
228 # "500" field has retained its original value
229 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
232 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
235 $merged_record->as_formatted,
236 $expected_record->as_formatted,
237 'Fields not in the incoming record are removed'
242 subtest 'Record fields has been added and removed when add = 1, append = 0, remove = 1, delete = 0' => sub {
254 my $expected_record = build_record([
255 # "250" field has been removed
256 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
257 # "500" field has retained its original value
258 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
259 # "501" fields have been added
260 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
261 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
264 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
267 $merged_record->as_formatted,
268 $expected_record->as_formatted,
269 'Fields not in the incoming record are removed, fields not in the original record have been added'
274 subtest 'Record fields has been appended and removed when add = 0, append = 1, remove = 1, delete = 0' => sub {
286 my $expected_record = build_record([
287 # "250" field has been appended and removed
288 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
289 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
290 # "500" field has retained its original value
291 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
294 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
297 $merged_record->as_formatted,
298 $expected_record->as_formatted,
299 'Fields in the incoming record replace fields from the original record, fields only in the original record has been kept, fields not in the original record have been skipped'
304 subtest 'Record fields has been added, appended and removed when add = 0, append = 1, remove = 1, delete = 0' => sub {
316 my $expected_record = build_record([
317 # "250" field has been appended and removed
318 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
319 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
320 # "500" field has retained its original value
321 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
322 # "501" fields have been added
323 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
324 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
327 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
330 $merged_record->as_formatted,
331 $expected_record->as_formatted,
332 'Fields in the incoming record replace fields from the original record, fields only in the original record has been kept, fields not in the original record have been added'
337 subtest 'Record fields has been deleted when add = 0, append = 0, remove = 0, delete = 1' => sub {
349 # FIXME the tooltip for delete is saying
350 # "If the original record has fields matching the rule tag, but no fields with this are found in the incoming record"
351 # But it does not seem to do that
352 my $expected_record = build_record([
353 # "250" fields have retained their original value
354 [ '250', 'a', '250 bottles of beer on the wall' ], # original
355 [ '250', 'a', '256 bottles of beer on the wall' ], # original
358 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
361 $merged_record->as_formatted,
362 $expected_record->as_formatted,
363 'Only fields in original and incoming are kept, but incoming values are ignored'
368 subtest 'Record fields has been added and deleted when add = 1, append = 0, remove = 0, delete = 1' => sub {
380 # Warning - is there a use case in real-life for this combinaison?
381 my $expected_record = build_record([
382 # "250" field have retained their original value
383 [ '250', 'a', '250 bottles of beer on the wall' ], # original
384 [ '250', 'a', '256 bottles of beer on the wall' ], # original
385 # "500" field has been removed
386 # "501" fields have been added
387 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
388 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
391 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
394 $merged_record->as_formatted,
395 $expected_record->as_formatted,
396 'Fields from the incoming records are kept, but keep the value from the original record if they already existed'
401 subtest 'Record fields has been appended and deleted when add = 0, append = 1, remove = 0, delete = 1' => sub {
413 my $expected_record = build_record([
414 # "250" field has been appended
415 [ '250', 'a', '250 bottles of beer on the wall' ], # original
416 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
417 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
418 # "500" field has been removed
421 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
424 $merged_record->as_formatted,
425 $expected_record->as_formatted,
426 'Only fields that already existed are appended'
431 subtest 'Record fields has been added, appended and deleted when add = 1, append = 1, remove = 0, delete = 1' => sub {
443 my $expected_record = build_record([
444 # "250" field has been appended
445 [ '250', 'a', '250 bottles of beer on the wall' ], # original
446 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
447 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
448 # "500" field has been removed
449 # "501" fields have been added
450 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
451 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
454 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
457 $merged_record->as_formatted,
458 $expected_record->as_formatted,
459 'Fields in the incoming record are added and appended, fields not in the original record are removed'
464 subtest 'Record fields has been removed and deleted when add = 0, append = 0, remove = 1, delete = 1' => sub {
476 my $expected_record = build_record([
477 # "250" field has been removed
478 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
479 # "500" field has been removed
482 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
485 $merged_record->as_formatted,
486 $expected_record->as_formatted,
492 subtest 'Record fields has been added, removed and deleted when add = 1, append = 0, remove = 1, delete = 1' => sub {
504 my $expected_record = build_record([
505 # "250" field has been appended
506 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
507 # "500" field has been removed
508 # "501" fields have been added
509 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
510 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
513 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
516 $merged_record->as_formatted,
517 $expected_record->as_formatted,
518 'Union for existing fields, new fields are added'
523 subtest 'Record fields has been appended, removed and deleted when add = 0, append = 1, remove = 1, delete = 1' => sub {
535 my $expected_record = build_record([
536 # "250" field has been appended and removed
537 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
538 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
539 # "500" field has been removed
542 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
545 $merged_record->as_formatted,
546 $expected_record->as_formatted,
547 'Fields from incoming replace original record. Existing fields not in incoming are removed'
552 subtest 'Record fields has been overwritten when add = 1, append = 1, remove = 1, delete = 1' => sub {
564 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
567 $merged_record->as_formatted,
568 $incoming_record->as_formatted,
569 'Incoming record erase original record'
574 subtest 'subfields order' => sub {
586 my $incoming_record = build_record(
588 [ '250', 'a', '256 bottles of beer on the wall' ],
589 [ '250', 'a', '250 bottles of beer on the wall' ],
590 [ '500', 'a', 'One bottle of beer in the fridge' ],
594 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
597 $merged_record->as_formatted,
598 $orig_record->as_formatted,
599 'Original record not modified - order of subfields not modified'
611 $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
614 $merged_record->as_formatted,
615 $incoming_record->as_formatted,
616 'Original record modified - order of subfields has been modified'
621 # Test rule tag specificity
623 # Protect field 500 with more specific tag value
624 my $skip_all_rule = Koha::MarcOverlayRules->find_or_create({
634 subtest '"500" field has been protected when rule matching on tag "500" is add = 0, append = 0, remove = 0, delete = 0' => sub {
637 my $expected_record = build_record([
638 # "250" field has been appended
639 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
640 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
641 # "500" field has retained its original value
642 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
643 # "501" fields have been added
644 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
645 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
648 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
651 $merged_record->as_formatted,
652 $expected_record->as_formatted,
653 'All fields are erased by incoming record but 500 is protected'
658 # Test regexp matching
659 subtest '"5XX" fields has been protected when rule matching on regexp "5\d{2}" is add = 0, append = 0, remove = 0, delete = 0' => sub {
668 my $expected_record = build_record([
669 # "250" field has been appended
670 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
671 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
672 # "500" field has retained its original value
673 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
676 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
679 $merged_record->as_formatted,
680 $expected_record->as_formatted,
681 'Fields are erased by incoming record but 500 is protected and 501 is not added'
686 $skip_all_rule->delete();
688 # Test module specificity, the 0 all rule should no longer be included in set of applied rules
689 subtest 'Record fields has been overwritten when non wild card rule with filter match is add = 1, append = 1, remove = 1, delete = 1' => sub {
698 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
701 $merged_record->as_formatted,
702 $incoming_record->as_formatted,
703 'Only existing rule is not for us, erasing' # FIXME Is this comment correct?
708 subtest 'An exception is thrown when append = 1, remove = 0 is set for control field rule' => sub {
710 my $exception = try {
711 Koha::MarcOverlayRules->validate({
720 ok(defined $exception, "Exception was caught");
721 ok($exception->isa('Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions'), "Exception is of correct class");
724 subtest 'An exception is thrown when rule tag is set to invalid regexp' => sub {
727 my $exception = try {
728 Koha::MarcOverlayRules->validate({
735 ok(defined $exception, "Exception was caught");
736 ok($exception->isa('Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp'), "Exception is of correct class");
740 subtest 'context option in ModBiblio is handled correctly' => sub {
755 my ($biblionumber, $biblioitemnumber) = AddBiblio($orig_record, '');
757 # Since marc merc rules are not run on save, only update
758 # saved record should be identical to orig_record
759 my $biblio = Koha::Biblios->find($biblionumber);
760 my $saved_record = $biblio->metadata->record;
762 my @all_fields = $saved_record->fields();
763 # Koha also adds 999c field, therefore 4 not 3
765 my $expected_record = build_record([
766 # "250" field has been appended
767 [ '250', 'a', '250 bottles of beer on the wall' ], # original
768 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
769 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
770 [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ], # created by AddBiblio
773 # Make sure leader is equal after AddBiblio
774 $expected_record->leader($saved_record->leader());
777 $saved_record->as_formatted,
778 $expected_record->as_formatted,
781 $saved_record->append_fields(
782 MARC::Field->new('250', '', '', 'a' => '251 bottles of beer on the wall'), # Appended
783 MARC::Field->new('500', '', '', 'a' => 'One cold bottle of beer in the fridge'), # Appended
786 ModBiblio($saved_record, $biblionumber, '', { overlay_context => { 'source' => 'test' } });
788 my $updated_record = $biblio->get_from_storage->metadata->record;
790 $expected_record = build_record([
791 # "250" field has been appended
792 [ '250', 'a', '250 bottles of beer on the wall' ],
793 [ '250', 'a', '256 bottles of beer on the wall' ],
794 [ '500', 'a', 'One bottle of beer in the fridge' ],
795 [ '500', 'a', 'One cold bottle of beer in the fridge' ],
796 [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ], # created by AddBiblio
799 # Make sure leader is equal after ModBiblio
800 $expected_record->leader($updated_record->leader());
803 $updated_record->as_formatted,
804 $expected_record->as_formatted,
807 # To trigger removal from search index etc
808 DelBiblio($biblionumber);
811 # Explicityly delete rule to trigger clearing of cache
814 $schema->storage->txn_rollback;