Koha-clms/t/db_dependent/Koha/Patron/Modifications.t

519 lines
18 KiB
Perl
Executable File

#!/usr/bin/perl
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <https://www.gnu.org/licenses>.
use Modern::Perl;
use utf8;
use Test::More tests => 8;
use Test::NoWarnings;
use Test::Exception;
use t::lib::TestBuilder;
use t::lib::Mocks;
use Digest::MD5 qw( md5_base64 md5_hex );
use Try::Tiny;
use C4::Context;
use C4::Members;
use Koha::Patrons;
use Koha::Patron::Attribute;
BEGIN {
use_ok('Koha::Patron::Modification');
use_ok('Koha::Patron::Modifications');
}
my $schema = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;
subtest 'new() tests' => sub {
plan tests => 3;
$schema->storage->txn_begin;
Koha::Patron::Modifications->search->delete;
my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
# Create new pending modification
Koha::Patron::Modification->new(
{
borrowernumber => $patron->borrowernumber,
verification_token => '1234567890',
changed_fields => 'surname,firstname',
surname => 'Hall',
firstname => 'Kyle'
}
)->store();
## Get the new pending modification
my $borrower = Koha::Patron::Modifications->find( { verification_token => '1234567890' } );
## Verify we get the same data
is(
$borrower->surname, 'Hall',
'Found modification has matching surname'
);
throws_ok {
Koha::Patron::Modification->new(
{
verification_token => '1234567890',
changed_fields => 'surname,firstname',
surname => 'Hall',
firstname => 'Daria'
}
)->store();
}
'Koha::Exceptions::Patron::Modification::DuplicateVerificationToken',
'Attempting to add a duplicate verification raises the correct exception';
like(
"$@",
qr/Duplicate verification token 1234567890/,
'Exception carries the right message'
);
$schema->storage->txn_rollback;
};
subtest 'store( extended_attributes ) tests' => sub {
plan tests => 4;
$schema->storage->txn_begin;
Koha::Patron::Modifications->search->delete;
my $patron = $builder->build( { source => 'Borrower' } )->{borrowernumber};
my $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
my $valid_json_text = '[{"code":"CODE","value":"VALUE"}]';
my $invalid_json_text = '[{"code":"CODE";"value":"VALUE"}]';
Koha::Patron::Modification->new(
{
verification_token => $verification_token,
changed_fields => 'borrowernumber,surname,extended_attributes',
borrowernumber => $patron,
surname => 'Hall',
extended_attributes => $valid_json_text
}
)->store();
my $patron_modification = Koha::Patron::Modifications->search( { borrowernumber => $patron } )->next;
is(
$patron_modification->surname,
'Hall', 'Patron modification correctly stored with valid JSON data'
);
is(
$patron_modification->extended_attributes,
$valid_json_text,
'Patron modification correctly stored with valid JSON data'
);
$verification_token = md5_hex( time() . {} . rand() . {} . $$ );
throws_ok {
Koha::Patron::Modification->new(
{
verification_token => $verification_token,
changed_fields => 'borrowernumber,surname,extended_attributes',
borrowernumber => $patron,
surname => 'Hall',
extended_attributes => $invalid_json_text
}
)->store();
}
'Koha::Exceptions::Patron::Modification::InvalidData',
'Trying to store invalid JSON in extended_attributes field raises exception';
like( "$@", qr/The passed extended_attributes is not valid JSON/ );
$schema->storage->txn_rollback;
};
subtest 'approve tests' => sub {
plan tests => 20;
$schema->storage->txn_begin;
Koha::Patron::Modifications->search->delete;
my $patron_hashref = $builder->build( { source => 'Borrower' } );
$builder->build( { source => 'BorrowerAttributeType', value => { code => 'CODE_1' } } );
$builder->build( { source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } } );
my $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
my $valid_json_text = '[{"code":"CODE_1","value":"VALUE_1"},{"code":"CODE_2","value":0}]';
my $patron_modification = Koha::Patron::Modification->new(
{
verification_token => $verification_token,
changed_fields => 'borrowernumber,firstname,extended_attributes',
borrowernumber => $patron_hashref->{borrowernumber},
firstname => 'Kyle',
extended_attributes => $valid_json_text
}
)->store();
ok(
$patron_modification->approve,
'Patron modification correctly approved'
);
my $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
isnt(
$patron->firstname,
$patron_hashref->{firstname},
'Patron modification changed firstname'
);
is(
$patron->firstname, 'Kyle',
'Patron modification set the right firstname'
);
my $patron_attributes = $patron->extended_attributes;
my $attribute_1 = $patron_attributes->next;
is(
$attribute_1->code,
'CODE_1', 'Patron modification correctly saved attribute code'
);
is(
$attribute_1->attribute,
'VALUE_1', 'Patron modification correctly saved attribute value'
);
my $attribute_2 = $patron_attributes->next;
is(
$attribute_2->code,
'CODE_2', 'Patron modification correctly saved attribute code'
);
is(
$attribute_2->attribute,
0, 'Patron modification correctly saved attribute with value 0, not confused with delete'
);
# Create a new Koha::Patron::Modification, skip extended_attributes to
# bypass checks
$patron_modification = Koha::Patron::Modification->new(
{
verification_token => $verification_token,
changed_fields => 'borrowernumber,firstname',
borrowernumber => $patron_hashref->{borrowernumber},
firstname => 'Kylie'
}
)->store();
# Add invalid JSON to extended attributes
$patron_modification->extended_attributes('[{"code":"CODE";"values:VALUES"}]');
throws_ok { $patron_modification->approve }
'Koha::Exceptions::Patron::Modification::InvalidData',
'The right exception is thrown if invalid data is on extended_attributes';
$patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
isnt( $patron->firstname, 'Kylie', 'Patron modification didn\'t apply' );
# Try changing only a portion of the attributes
my $bigger_json = '[{"code":"CODE_2","value":"Tomasito"},{"code":"CODE_2","value":"None"}]';
$verification_token = md5_hex( time() . {} . rand() . {} . $$ );
$patron_modification = Koha::Patron::Modification->new(
{
verification_token => $verification_token,
changed_fields => 'borrowernumber,extended_attributes',
borrowernumber => $patron->borrowernumber,
extended_attributes => $bigger_json
}
)->store();
ok(
$patron_modification->approve,
'Patron modification correctly approved'
);
my @patron_attributes = map { $_->unblessed }
Koha::Patron::Attributes->search( { borrowernumber => $patron->borrowernumber } )->as_list;
is(
$patron_attributes[0]->{code},
'CODE_1', 'Untouched attribute type is preserved (code)'
);
is(
$patron_attributes[0]->{attribute},
'VALUE_1', 'Untouched attribute type is preserved (attribute)'
);
is(
$patron_attributes[1]->{code},
'CODE_2', 'Attribute updated correctly (code)'
);
is(
$patron_attributes[1]->{attribute},
'None', 'Attribute updated correctly (attribute)'
);
is(
$patron_attributes[2]->{code},
'CODE_2', 'Attribute updated correctly (code)'
);
is(
$patron_attributes[2]->{attribute},
'Tomasito', 'Attribute updated correctly (attribute)'
);
my $empty_code_json = '[{"code":"CODE_2","value":""}]';
$verification_token = md5_hex( time() . {} . rand() . {} . $$ );
$patron_modification = Koha::Patron::Modification->new(
{
verification_token => $verification_token,
changed_fields => 'borrowernumber,extended_attributes',
borrowernumber => $patron->borrowernumber,
extended_attributes => $empty_code_json
}
)->store();
ok(
$patron_modification->approve,
'Patron modification correctly approved'
);
$patron_attributes =
map { $_->unblessed } Koha::Patron::Attributes->search( { borrowernumber => $patron->borrowernumber } );
is(
$patron_attributes[0]->{code},
'CODE_1', 'Untouched attribute type is preserved (code)'
);
is(
$patron_attributes[0]->{attribute},
'VALUE_1', 'Untouched attribute type is preserved (attribute)'
);
my $count =
Koha::Patron::Attributes->search( { borrowernumber => $patron->borrowernumber, code => 'CODE_2' } )->count;
is( $count, 0, 'Attributes deleted when modification contained an empty one' );
$schema->storage->txn_rollback;
};
subtest 'pending_count() and pending() tests' => sub {
plan tests => 16;
$schema->storage->txn_begin;
Koha::Patron::Modifications->search->delete;
my $library_1 = $builder->build( { source => 'Branch' } )->{branchcode};
my $library_2 = $builder->build( { source => 'Branch' } )->{branchcode};
$builder->build( { source => 'BorrowerAttributeType', value => { code => 'CODE_1' } } );
$builder->build( { source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } } );
my $patron_1 = $builder->build( { source => 'Borrower', value => { branchcode => $library_1, flags => 1 } } );
my $patron_2 = $builder->build( { source => 'Borrower', value => { branchcode => $library_2 } } );
my $patron_3 = $builder->build( { source => 'Borrower', value => { branchcode => $library_2 } } );
$patron_1 = Koha::Patrons->find( $patron_1->{borrowernumber} );
$patron_2 = Koha::Patrons->find( $patron_2->{borrowernumber} );
$patron_3 = Koha::Patrons->find( $patron_3->{borrowernumber} );
my $verification_token_1 = md5_hex( time() . {} . rand() . {} . $$ );
my $verification_token_2 = md5_hex( time() . {} . rand() . {} . $$ );
my $verification_token_3 = md5_hex( time() . {} . rand() . {} . $$ );
Koha::Patron::Attribute->new(
{ borrowernumber => $patron_1->borrowernumber, code => 'CODE_1', attribute => 'hello' } )->store();
Koha::Patron::Attribute->new(
{ borrowernumber => $patron_2->borrowernumber, code => 'CODE_2', attribute => 'bye' } )->store();
my $modification_1 = Koha::Patron::Modification->new(
{
verification_token => $verification_token_1,
changed_fields => 'borrowernumber,surname,firstname,extended_attributes',
borrowernumber => $patron_1->borrowernumber,
surname => 'Hall',
firstname => 'Kyle',
extended_attributes => '[{"code":"CODE_1","value":""}]'
}
)->store();
is(
Koha::Patron::Modifications->pending_count,
1, 'pending_count() correctly returns 1'
);
my $modification_2 = Koha::Patron::Modification->new(
{
verification_token => $verification_token_2,
changed_fields => 'borrowernumber,surname,firstname,extended_attributes',
borrowernumber => $patron_2->borrowernumber,
surname => 'Smith',
firstname => 'Sandy',
extended_attributes => '[{"code":"CODE_2","value":"año"},{"code":"CODE_2","value":"ciao"}]'
}
)->store();
my $modification_3 = Koha::Patron::Modification->new(
{
verification_token => $verification_token_3,
changed_fields => 'borrowernumber,surname,firstname',
borrowernumber => $patron_3->borrowernumber,
surname => 'Smithy',
firstname => 'Sandy'
}
)->store();
is(
Koha::Patron::Modifications->pending_count,
3, 'pending_count() correctly returns 3'
);
my $pending = Koha::Patron::Modifications->pending();
is( scalar @{$pending}, 3, 'pending() returns an array with 3 elements' );
my @filtered_modifications = grep { $_->{borrowernumber} eq $patron_1->borrowernumber } @{$pending};
my $p1_pm = $filtered_modifications[0];
my $p1_pm_attribute_1 = $p1_pm->{extended_attributes}->[0];
is(
scalar @{ $p1_pm->{extended_attributes} }, 1,
'patron 1 has modification has one pending attribute modification'
);
is( ref($p1_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
is( $p1_pm_attribute_1->attribute, '', 'patron 1 has an empty value for the attribute' );
@filtered_modifications = grep { $_->{borrowernumber} eq $patron_2->borrowernumber } @{$pending};
my $p2_pm = $filtered_modifications[0];
is( scalar @{ $p2_pm->{extended_attributes} }, 2, 'patron 2 has 2 attribute modifications' );
my $p2_pm_attribute_1 = $p2_pm->{extended_attributes}->[0];
my $p2_pm_attribute_2 = $p2_pm->{extended_attributes}->[1];
is( ref($p2_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
is( ref($p2_pm_attribute_2), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
is( $p2_pm_attribute_1->attribute, 'año', 'patron modification has the right attribute change' );
is( $p2_pm_attribute_2->attribute, 'ciao', 'patron modification has the right attribute change' );
t::lib::Mocks::mock_userenv( { patron => $patron_1 } );
is(
Koha::Patron::Modifications->pending_count($library_1),
1, 'pending_count() correctly returns 1 if filtered by library'
);
is(
Koha::Patron::Modifications->pending_count($library_2),
2, 'pending_count() correctly returns 2 if filtered by library'
);
$modification_1->approve;
is(
Koha::Patron::Modifications->pending_count,
2, 'pending_count() correctly returns 2'
);
$modification_2->approve;
is(
Koha::Patron::Modifications->pending_count,
1, 'pending_count() correctly returns 1'
);
$modification_3->approve;
is(
Koha::Patron::Modifications->pending_count,
0, 'pending_count() correctly returns 0'
);
$schema->storage->txn_rollback;
};
subtest 'dateofbirth tests' => sub {
plan tests => 7;
$schema->storage->txn_begin;
# Cleaning the field
my $patron = $builder->build_object(
{ class => 'Koha::Patrons', value => { dateofbirth => '1980-01-01', surname => 'a_surname' } } );
my $patron_modification = Koha::Patron::Modification->new(
{
verification_token => '1234567890',
changed_fields => 'borrowernumber,dateofbirth',
borrowernumber => $patron->borrowernumber,
dateofbirth => undef
}
)->store;
$patron_modification->approve;
$patron->discard_changes;
is( $patron->dateofbirth, undef, 'dateofbirth must a been set to NULL if required' );
# FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
Koha::Patron::Modifications->search( { borrowernumber => $patron->borrowernumber } )->delete;
# Adding a dateofbirth
$patron_modification = Koha::Patron::Modification->new(
{
verification_token => '1234567890',
changed_fields => 'borrowernumber,dateofbirth',
borrowernumber => $patron->borrowernumber,
dateofbirth => '1980-02-02'
}
)->store;
$patron_modification->approve;
$patron->discard_changes;
is( $patron->dateofbirth, '1980-02-02', 'dateofbirth must a been set' );
is( $patron->surname, 'a_surname', 'surname must not be updated' );
# FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
Koha::Patron::Modifications->search( { borrowernumber => $patron->borrowernumber } )->delete;
# Modifying a dateofbirth
$patron_modification = Koha::Patron::Modification->new(
{
verification_token => '1234567890',
changed_fields => 'borrowernumber,dateofbirth',
borrowernumber => $patron->borrowernumber,
dateofbirth => '1980-03-03',
surname => undef
}
)->store;
$patron_modification->approve;
$patron->discard_changes;
is( $patron->dateofbirth, '1980-03-03', 'dateofbirth must a been updated' );
is( $patron->surname, 'a_surname', 'surname must not be updated' );
# FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
Koha::Patron::Modifications->search( { borrowernumber => $patron->borrowernumber } )->delete;
# Modifying something else
$patron_modification = Koha::Patron::Modification->new(
{
verification_token => '1234567890',
changed_fields => 'borrowernumber,surname',
borrowernumber => $patron->borrowernumber,
surname => 'another_surname',
dateofbirth => undef
}
)->store;
$patron_modification->approve;
$patron->discard_changes;
is( $patron->surname, 'another_surname', 'surname must be updated' );
is( $patron->dateofbirth, '1980-03-03', 'dateofbirth should not have been updated if not needed' );
$schema->storage->txn_rollback;
};