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 GetMarcBiblio );
28 use Test::More tests => 24;
31 use Koha::MarcOverlayRules;
35 my $schema = Koha::Database->schema;
36 $schema->storage->txn_begin;
38 t::lib::Mocks::mock_preference('MARCOverlayRules', '1');
40 Koha::MarcOverlayRules->search->delete;
44 my $record = MARC::Record->new;
46 for my $f ( @$fields ) {
49 for my $i ( 1 .. (scalar(@$f) / 2) ) {
51 push @subfields, $f->[$ii - 1], $f->[$ii];
53 push @marc_fields, MARC::Field->new($tag, '','', @subfields);
56 $record->append_fields(@marc_fields);
62 my $orig_record = build_record([
63 [ '250', 'a', '250 bottles of beer on the wall' ],
64 [ '250', 'a', '256 bottles of beer on the wall' ],
65 [ '500', 'a', 'One bottle of beer in the fridge' ],
69 my $incoming_record = build_record(
71 ['250', 'a', '256 bottles of beer on the wall'], # Unchanged
72 ['250', 'a', '251 bottles of beer on the wall'], # Appended
73 #['250', 'a', '250 bottles of beer on the wall'], # Removed
74 #['500', 'a', 'One bottle of beer in the fridge'], # Deleted
75 ['501', 'a', 'One cold bottle of beer in the fridge'], # Added
76 ['501', 'a', 'Two cold bottles of beer in the fridge'], # Added
80 # Test default behavior when MARCOverlayRules is enabled, but no rules defined (overwrite)
81 subtest 'No rule defined' => sub {
84 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
87 $merged_record->as_formatted,
88 $incoming_record->as_formatted,
89 'Incoming record used as it with no rules defined'
94 my $rule = Koha::MarcOverlayRules->find_or_create({
104 subtest 'Record fields has been protected when matched merge all rule operations are set to "0"' => sub {
107 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
110 $merged_record->as_formatted,
111 $orig_record->as_formatted,
112 'Record not modified if all op=0'
117 subtest '"Add new" - Only new fields has been added when add = 1, append = 0, remove = 0, delete = 0' => sub {
129 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
131 my $expected_record = build_record([
132 [ '250', 'a', '250 bottles of beer on the wall' ], # original
133 [ '250', 'a', '256 bottles of beer on the wall' ], # original
134 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
135 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
136 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
139 $merged_record->as_formatted,
140 $expected_record->as_formatted,
141 'Add fields from the incoming record that are not in the original record'
146 subtest 'Only appended fields has been added when add = 0, append = 1, remove = 0, delete = 0' => sub {
158 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
160 my $expected_record = build_record([
161 [ '250', 'a', '250 bottles of beer on the wall' ], # original
162 [ '250', 'a', '256 bottles of beer on the wall' ], # original
163 # "251" field has been appended
164 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
165 # "500" field has retained its original value
166 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
170 $merged_record->as_formatted,
171 $expected_record->as_formatted,
172 'Add fields from the incoming record that are in the original record'
177 subtest '"Add and append" - add = 1, append = 1, remove = 0, delete = 0' => sub {
189 my $expected_record = build_record([
190 [ '250', 'a', '250 bottles of beer on the wall' ], # original
191 [ '250', 'a', '256 bottles of beer on the wall' ], # original
192 # "251" field has been appended
193 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
194 # "500" field has retained its original value
195 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
196 # "501" fields have been added
197 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
198 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
201 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
204 $merged_record->as_formatted,
205 $expected_record->as_formatted,
206 'Appended and added fields have been added'
211 subtest 'Record fields has been only removed when add = 0, append = 0, remove = 1, delete = 0' => sub {
223 # Warning - not obvious as the 500 is untouched
224 my $expected_record = build_record([
225 # "250" field has been removed
226 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
227 # "500" field has retained its original value
228 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
231 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
234 $merged_record->as_formatted,
235 $expected_record->as_formatted,
236 'Fields not in the incoming record are removed'
241 subtest 'Record fields has been added and removed when add = 1, append = 0, remove = 1, delete = 0' => sub {
253 my $expected_record = build_record([
254 # "250" field has been removed
255 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
256 # "500" field has retained its original value
257 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
258 # "501" fields have been added
259 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
260 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
263 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
266 $merged_record->as_formatted,
267 $expected_record->as_formatted,
268 'Fields not in the incoming record are removed, fields not in the original record have been added'
273 subtest 'Record fields has been appended and removed when add = 0, append = 1, remove = 1, delete = 0' => sub {
285 my $expected_record = build_record([
286 # "250" field has been appended and removed
287 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
288 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
289 # "500" field has retained its original value
290 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
293 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
296 $merged_record->as_formatted,
297 $expected_record->as_formatted,
298 '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'
303 subtest 'Record fields has been added, appended and removed when add = 0, append = 1, remove = 1, delete = 0' => sub {
315 my $expected_record = build_record([
316 # "250" field has been appended and removed
317 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
318 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
319 # "500" field has retained its original value
320 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
321 # "501" fields have been added
322 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
323 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
326 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
329 $merged_record->as_formatted,
330 $expected_record->as_formatted,
331 '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'
336 subtest 'Record fields has been deleted when add = 0, append = 0, remove = 0, delete = 1' => sub {
348 # FIXME the tooltip for delete is saying
349 # "If the original record has fields matching the rule tag, but no fields with this are found in the incoming record"
350 # But it does not seem to do that
351 my $expected_record = build_record([
352 # "250" fields have retained their original value
353 [ '250', 'a', '250 bottles of beer on the wall' ], # original
354 [ '250', 'a', '256 bottles of beer on the wall' ], # original
357 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
360 $merged_record->as_formatted,
361 $expected_record->as_formatted,
362 'Only fields in original and incoming are kept, but incoming values are ignored'
367 subtest 'Record fields has been added and deleted when add = 1, append = 0, remove = 0, delete = 1' => sub {
379 # Warning - is there a use case in real-life for this combinaison?
380 my $expected_record = build_record([
381 # "250" field have retained their original value
382 [ '250', 'a', '250 bottles of beer on the wall' ], # original
383 [ '250', 'a', '256 bottles of beer on the wall' ], # original
384 # "500" field has been removed
385 # "501" fields have been added
386 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
387 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
390 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
393 $merged_record->as_formatted,
394 $expected_record->as_formatted,
395 'Fields from the incoming records are kept, but keep the value from the original record if they already existed'
400 subtest 'Record fields has been appended and deleted when add = 0, append = 1, remove = 0, delete = 1' => sub {
412 my $expected_record = build_record([
413 # "250" field has been appended
414 [ '250', 'a', '250 bottles of beer on the wall' ], # original
415 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
416 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
417 # "500" field has been removed
420 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
423 $merged_record->as_formatted,
424 $expected_record->as_formatted,
425 'Only fields that already existed are appended'
430 subtest 'Record fields has been added, appended and deleted when add = 1, append = 1, remove = 0, delete = 1' => sub {
442 my $expected_record = build_record([
443 # "250" field has been appended
444 [ '250', 'a', '250 bottles of beer on the wall' ], # original
445 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
446 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
447 # "500" field has been removed
448 # "501" fields have been added
449 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
450 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
453 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
456 $merged_record->as_formatted,
457 $expected_record->as_formatted,
458 'Fields in the incoming record are added and appended, fields not in the original record are removed'
463 subtest 'Record fields has been removed and deleted when add = 0, append = 0, remove = 1, delete = 1' => sub {
475 my $expected_record = build_record([
476 # "250" field has been removed
477 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
478 # "500" field has been removed
481 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
484 $merged_record->as_formatted,
485 $expected_record->as_formatted,
491 subtest 'Record fields has been added, removed and deleted when add = 1, append = 0, remove = 1, delete = 1' => sub {
503 my $expected_record = build_record([
504 # "250" field has been appended
505 [ '250', 'a', '256 bottles of beer on the wall' ], # original and incoming
506 # "500" field has been removed
507 # "501" fields have been added
508 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
509 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
512 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
515 $merged_record->as_formatted,
516 $expected_record->as_formatted,
517 'Union for existing fields, new fields are added'
522 subtest 'Record fields has been appended, removed and deleted when add = 0, append = 1, remove = 1, delete = 1' => sub {
534 my $expected_record = build_record([
535 # "250" field has been appended and removed
536 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
537 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
538 # "500" field has been removed
541 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
544 $merged_record->as_formatted,
545 $expected_record->as_formatted,
546 'Fields from incoming replace original record. Existing fields not in incoming are removed'
551 subtest 'Record fields has been overwritten when add = 1, append = 1, remove = 1, delete = 1' => sub {
563 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
566 $merged_record->as_formatted,
567 $incoming_record->as_formatted,
568 'Incoming record erase original record'
573 subtest 'subfields order' => sub {
585 my $incoming_record = build_record(
587 [ '250', 'a', '256 bottles of beer on the wall' ],
588 [ '250', 'a', '250 bottles of beer on the wall' ],
589 [ '500', 'a', 'One bottle of beer in the fridge' ],
593 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
596 $merged_record->as_formatted,
597 $orig_record->as_formatted,
598 'Original record not modified - order of subfields not modified'
610 $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
613 $merged_record->as_formatted,
614 $incoming_record->as_formatted,
615 'Original record modified - order of subfields has been modified'
620 # Test rule tag specificity
622 # Protect field 500 with more specific tag value
623 my $skip_all_rule = Koha::MarcOverlayRules->find_or_create({
633 subtest '"500" field has been protected when rule matching on tag "500" is add = 0, append = 0, remove = 0, delete = 0' => sub {
636 my $expected_record = build_record([
637 # "250" field has been appended
638 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
639 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
640 # "500" field has retained its original value
641 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
642 # "501" fields have been added
643 [ '501', 'a', 'One cold bottle of beer in the fridge' ], # incoming
644 [ '501', 'a', 'Two cold bottles of beer in the fridge' ], # incoming
647 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
650 $merged_record->as_formatted,
651 $expected_record->as_formatted,
652 'All fields are erased by incoming record but 500 is protected'
657 # Test regexp matching
658 subtest '"5XX" fields has been protected when rule matching on regexp "5\d{2}" is add = 0, append = 0, remove = 0, delete = 0' => sub {
667 my $expected_record = build_record([
668 # "250" field has been appended
669 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
670 [ '250', 'a', '251 bottles of beer on the wall' ], # incoming
671 # "500" field has retained its original value
672 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
675 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
678 $merged_record->as_formatted,
679 $expected_record->as_formatted,
680 'Fields are erased by incoming record but 500 is protected and 501 is not added'
685 $skip_all_rule->delete();
687 # Test module specificity, the 0 all rule should no longer be included in set of applied rules
688 subtest 'Record fields has been overwritten when non wild card rule with filter match is add = 1, append = 1, remove = 1, delete = 1' => sub {
697 my $merged_record = Koha::MarcOverlayRules->merge_records($orig_record, $incoming_record, { 'source' => 'test' });
700 $merged_record->as_formatted,
701 $incoming_record->as_formatted,
702 'Only existing rule is not for us, erasing' # FIXME Is this comment correct?
707 subtest 'An exception is thrown when append = 1, remove = 0 is set for control field rule' => sub {
709 my $exception = try {
710 Koha::MarcOverlayRules->validate({
719 ok(defined $exception, "Exception was caught");
720 ok($exception->isa('Koha::Exceptions::MarcOverlayRule::InvalidControlFieldActions'), "Exception is of correct class");
723 subtest 'An exception is thrown when rule tag is set to invalid regexp' => sub {
726 my $exception = try {
727 Koha::MarcOverlayRules->validate({
734 ok(defined $exception, "Exception was caught");
735 ok($exception->isa('Koha::Exceptions::MarcOverlayRule::InvalidTagRegExp'), "Exception is of correct class");
739 subtest 'context option in ModBiblio is handled correctly' => sub {
754 my ($biblionumber, $biblioitemnumber) = AddBiblio($orig_record, '');
756 # Since marc merc rules are not run on save, only update
757 # saved record should be identical to orig_record
758 my $saved_record = GetMarcBiblio({ biblionumber => $biblionumber });
760 my @all_fields = $saved_record->fields();
761 # Koha also adds 999c field, therefore 4 not 3
763 my $expected_record = build_record([
764 # "250" field has been appended
765 [ '250', 'a', '250 bottles of beer on the wall' ], # original
766 [ '250', 'a', '256 bottles of beer on the wall' ], # incoming
767 [ '500', 'a', 'One bottle of beer in the fridge' ], # original
768 [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ], # created by AddBiblio
771 # Make sure leader is equal after AddBiblio
772 $expected_record->leader($saved_record->leader());
775 $saved_record->as_formatted,
776 $expected_record->as_formatted,
779 $saved_record->append_fields(
780 MARC::Field->new('250', '', '', 'a' => '251 bottles of beer on the wall'), # Appended
781 MARC::Field->new('500', '', '', 'a' => 'One cold bottle of beer in the fridge'), # Appended
784 ModBiblio($saved_record, $biblionumber, '', { overlay_context => { 'source' => 'test' } });
786 my $updated_record = GetMarcBiblio({ biblionumber => $biblionumber });
788 $expected_record = build_record([
789 # "250" field has been appended
790 [ '250', 'a', '250 bottles of beer on the wall' ],
791 [ '250', 'a', '256 bottles of beer on the wall' ],
792 [ '500', 'a', 'One bottle of beer in the fridge' ],
793 [ '500', 'a', 'One cold bottle of beer in the fridge' ],
794 [ '999', 'c', $biblionumber, 'd', $biblioitemnumber ], # created by AddBiblio
797 # Make sure leader is equal after ModBiblio
798 $expected_record->leader($updated_record->leader());
801 $updated_record->as_formatted,
802 $expected_record->as_formatted,
805 # To trigger removal from search index etc
806 DelBiblio($biblionumber);
809 # Explicityly delete rule to trigger clearing of cache
812 $schema->storage->txn_rollback;