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>.
22 use Test::More tests => 7;
25 use t::lib::TestBuilder;
28 use Digest::MD5 qw( md5_base64 md5_hex );
33 use C4::Members::Attributes qw( GetBorrowerAttributes );
35 use Koha::Patron::Attribute;
38 use_ok('Koha::Patron::Modification');
39 use_ok('Koha::Patron::Modifications');
42 my $schema = Koha::Database->new->schema;
43 my $builder = t::lib::TestBuilder->new;
45 subtest 'new() tests' => sub {
49 $schema->storage->txn_begin;
51 Koha::Patron::Modifications->search->delete;
53 # Create new pending modification
54 Koha::Patron::Modification->new(
55 { verification_token => '1234567890',
56 changed_fields => 'surname,firstname',
62 ## Get the new pending modification
63 my $borrower = Koha::Patron::Modifications->find(
64 { verification_token => '1234567890' } );
66 ## Verify we get the same data
67 is( $borrower->surname, 'Hall',
68 'Found modification has matching surname' );
71 Koha::Patron::Modification->new(
72 { verification_token => '1234567890',
73 changed_fields => 'surname,firstname',
79 'Koha::Exceptions::Patron::Modification::DuplicateVerificationToken',
80 'Attempting to add a duplicate verification raises the correct exception';
82 'Duplicate verification token 1234567890',
83 'Exception carries the right message'
86 $schema->storage->txn_rollback;
89 subtest 'store( extended_attributes ) tests' => sub {
93 $schema->storage->txn_begin;
95 Koha::Patron::Modifications->search->delete;
98 = $builder->build( { source => 'Borrower' } )->{borrowernumber};
99 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
100 my $valid_json_text = '[{"code":"CODE","value":"VALUE"}]';
101 my $invalid_json_text = '[{"code":"CODE";"value":"VALUE"}]';
103 Koha::Patron::Modification->new(
104 { verification_token => $verification_token,
105 changed_fields => 'borrowernumber,surname,extended_attributes',
106 borrowernumber => $patron,
108 extended_attributes => $valid_json_text
112 my $patron_modification
113 = Koha::Patron::Modifications->search( { borrowernumber => $patron } )
116 is( $patron_modification->surname,
117 'Hall', 'Patron modification correctly stored with valid JSON data' );
118 is( $patron_modification->extended_attributes,
120 'Patron modification correctly stored with valid JSON data' );
122 $verification_token = md5_hex( time().{}.rand().{}.$$ );
124 Koha::Patron::Modification->new(
125 { verification_token => $verification_token,
126 changed_fields => 'borrowernumber,surname,extended_attributes',
127 borrowernumber => $patron,
129 extended_attributes => $invalid_json_text
133 'Koha::Exceptions::Patron::Modification::InvalidData',
134 'Trying to store invalid JSON in extended_attributes field raises exception';
136 is( $@, 'The passed extended_attributes is not valid JSON' );
138 $schema->storage->txn_rollback;
141 subtest 'approve tests' => sub {
145 $schema->storage->txn_begin;
147 Koha::Patron::Modifications->search->delete;
149 my $patron_hashref = $builder->build( { source => 'Borrower' } );
151 { source => 'BorrowerAttributeType', value => { code => 'CODE_1' } }
154 { source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } }
156 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
158 = '[{"code":"CODE_1","value":"VALUE_1"},{"code":"CODE_2","value":0}]';
159 my $patron_modification = Koha::Patron::Modification->new(
160 { verification_token => $verification_token,
161 changed_fields => 'borrowernumber,firstname,extended_attributes',
162 borrowernumber => $patron_hashref->{borrowernumber},
164 extended_attributes => $valid_json_text
168 ok( $patron_modification->approve,
169 'Patron modification correctly approved' );
170 my $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
173 $patron_hashref->{firstname},
174 'Patron modification changed firstname'
176 is( $patron->firstname, 'Kyle',
177 'Patron modification set the right firstname' );
178 my @patron_attributes = GetBorrowerAttributes( $patron->borrowernumber );
179 is( $patron_attributes[0][0]->{code},
180 'CODE_1', 'Patron modification correctly saved attribute code' );
181 is( $patron_attributes[0][0]->{value},
182 'VALUE_1', 'Patron modification correctly saved attribute value' );
183 is( $patron_attributes[0][1]->{code},
184 'CODE_2', 'Patron modification correctly saved attribute code' );
185 is( $patron_attributes[0][1]->{value},
186 0, 'Patron modification correctly saved attribute with value 0, not confused with delete' );
188 # Create a new Koha::Patron::Modification, skip extended_attributes to
190 $patron_modification = Koha::Patron::Modification->new(
191 { verification_token => $verification_token,
192 changed_fields => 'borrowernumber,firstname',
193 borrowernumber => $patron_hashref->{borrowernumber},
198 # Add invalid JSON to extended attributes
199 $patron_modification->extended_attributes(
200 '[{"code":"CODE";"values:VALUES"}]');
201 throws_ok { $patron_modification->approve }
202 'Koha::Exceptions::Patron::Modification::InvalidData',
203 'The right exception is thrown if invalid data is on extended_attributes';
205 $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
206 isnt( $patron->firstname, 'Kylie', 'Patron modification didn\'t apply' );
208 # Try changing only a portion of the attributes
210 = '[{"code":"CODE_2","value":"Tomasito"},{"code":"CODE_2","value":"None"}]';
211 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
213 $patron_modification = Koha::Patron::Modification->new(
214 { verification_token => $verification_token,
215 changed_fields => 'borrowernumber,extended_attributes',
216 borrowernumber => $patron->borrowernumber,
217 extended_attributes => $bigger_json
220 ok( $patron_modification->approve,
221 'Patron modification correctly approved' );
223 = map { $_->unblessed }
224 Koha::Patron::Attributes->search(
225 { borrowernumber => $patron->borrowernumber } );
227 is( $patron_attributes[0]->{code},
228 'CODE_1', 'Untouched attribute type is preserved (code)' );
229 is( $patron_attributes[0]->{attribute},
230 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
232 is( $patron_attributes[1]->{code},
233 'CODE_2', 'Attribute updated correctly (code)' );
234 is( $patron_attributes[1]->{attribute},
235 'Tomasito', 'Attribute updated correctly (attribute)' );
237 is( $patron_attributes[2]->{code},
238 'CODE_2', 'Attribute updated correctly (code)' );
239 is( $patron_attributes[2]->{attribute},
240 'None', 'Attribute updated correctly (attribute)' );
242 my $empty_code_json = '[{"code":"CODE_2","value":""}]';
243 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
245 $patron_modification = Koha::Patron::Modification->new(
246 { verification_token => $verification_token,
247 changed_fields => 'borrowernumber,extended_attributes',
248 borrowernumber => $patron->borrowernumber,
249 extended_attributes => $empty_code_json
252 ok( $patron_modification->approve,
253 'Patron modification correctly approved' );
255 = map { $_->unblessed }
256 Koha::Patron::Attributes->search(
257 { borrowernumber => $patron->borrowernumber } );
259 is( $patron_attributes[0]->{code},
260 'CODE_1', 'Untouched attribute type is preserved (code)' );
261 is( $patron_attributes[0]->{attribute},
262 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
264 my $count = Koha::Patron::Attributes->search({ borrowernumber => $patron->borrowernumber, code => 'CODE_2' })->count;
265 is( $count, 0, 'Attributes deleted when modification contained an empty one');
267 $schema->storage->txn_rollback;
270 subtest 'pending_count() and pending() tests' => sub {
274 $schema->storage->txn_begin;
276 Koha::Patron::Modifications->search->delete;
277 my $library_1 = $builder->build( { source => 'Branch' } )->{branchcode};
278 my $library_2 = $builder->build( { source => 'Branch' } )->{branchcode};
279 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_1' } });
280 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } });
284 { source => 'Borrower', value => { branchcode => $library_1, flags => 1 } } );
287 { source => 'Borrower', value => { branchcode => $library_2 } } );
290 { source => 'Borrower', value => { branchcode => $library_2 } } );
291 $patron_1 = Koha::Patrons->find( $patron_1->{borrowernumber} );
292 $patron_2 = Koha::Patrons->find( $patron_2->{borrowernumber} );
293 $patron_3 = Koha::Patrons->find( $patron_3->{borrowernumber} );
294 my $verification_token_1 = md5_hex( time().{}.rand().{}.$$ );
295 my $verification_token_2 = md5_hex( time().{}.rand().{}.$$ );
296 my $verification_token_3 = md5_hex( time().{}.rand().{}.$$ );
298 Koha::Patron::Attribute->new({ borrowernumber => $patron_1->borrowernumber, code => 'CODE_1', attribute => 'hello' } )->store();
299 Koha::Patron::Attribute->new({ borrowernumber => $patron_2->borrowernumber, code => 'CODE_2', attribute => 'bye' } )->store();
301 my $modification_1 = Koha::Patron::Modification->new(
302 { verification_token => $verification_token_1,
303 changed_fields => 'borrowernumber,surname,firstname,extended_attributes',
304 borrowernumber => $patron_1->borrowernumber,
307 extended_attributes => '[{"code":"CODE_1","value":""}]'
311 is( Koha::Patron::Modifications->pending_count,
312 1, 'pending_count() correctly returns 1' );
314 my $modification_2 = Koha::Patron::Modification->new(
315 { verification_token => $verification_token_2,
316 changed_fields => 'borrowernumber,surname,firstname,extended_attributes',
317 borrowernumber => $patron_2->borrowernumber,
319 firstname => 'Sandy',
320 extended_attributes => '[{"code":"CODE_2","value":"año"},{"code":"CODE_2","value":"ciao"}]'
324 my $modification_3 = Koha::Patron::Modification->new(
325 { verification_token => $verification_token_3,
326 changed_fields => 'borrowernumber,surname,firstname',
327 borrowernumber => $patron_3->borrowernumber,
333 is( Koha::Patron::Modifications->pending_count,
334 3, 'pending_count() correctly returns 3' );
336 my $pending = Koha::Patron::Modifications->pending();
337 is( scalar @{$pending}, 3, 'pending() returns an array with 3 elements' );
339 my @filtered_modifications = grep { $_->{borrowernumber} eq $patron_1->borrowernumber } @{$pending};
340 my $p1_pm = $filtered_modifications[0];
341 my $p1_pm_attribute_1 = $p1_pm->{extended_attributes}->[0];
343 is( scalar @{$p1_pm->{extended_attributes}}, 1, 'patron 1 has modification has one pending attribute modification' );
344 is( ref($p1_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
345 is( $p1_pm_attribute_1->attribute, '', 'patron 1 has an empty value for the attribute' );
347 @filtered_modifications = grep { $_->{borrowernumber} eq $patron_2->borrowernumber } @{$pending};
348 my $p2_pm = $filtered_modifications[0];
350 is( scalar @{$p2_pm->{extended_attributes}}, 2 , 'patron 2 has 2 attribute modifications' );
352 my $p2_pm_attribute_1 = $p2_pm->{extended_attributes}->[0];
353 my $p2_pm_attribute_2 = $p2_pm->{extended_attributes}->[1];
355 is( ref($p2_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
356 is( ref($p2_pm_attribute_2), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
358 is( $p2_pm_attribute_1->attribute, 'año', 'patron modification has the right attribute change' );
359 is( $p2_pm_attribute_2->attribute, 'ciao', 'patron modification has the right attribute change' );
362 t::lib::Mocks::mock_userenv({ patron => $patron_1 });
363 is( Koha::Patron::Modifications->pending_count($library_1),
364 1, 'pending_count() correctly returns 1 if filtered by library' );
366 is( Koha::Patron::Modifications->pending_count($library_2),
367 2, 'pending_count() correctly returns 2 if filtered by library' );
369 $modification_1->approve;
371 is( Koha::Patron::Modifications->pending_count,
372 2, 'pending_count() correctly returns 2' );
374 $modification_2->approve;
376 is( Koha::Patron::Modifications->pending_count,
377 1, 'pending_count() correctly returns 1' );
379 $modification_3->approve;
381 is( Koha::Patron::Modifications->pending_count,
382 0, 'pending_count() correctly returns 0' );
384 $schema->storage->txn_rollback;
387 subtest 'dateofbirth tests' => sub {
390 $schema->storage->txn_begin;
393 my $patron = $builder->build_object( { class => 'Koha::Patrons', value => { dateofbirth => '1980-01-01', surname => 'a_surname' } } );
394 my $patron_modification = Koha::Patron::Modification->new( {
395 changed_fields => 'borrowernumber,dateofbirth',
396 borrowernumber => $patron->borrowernumber,
399 $patron_modification->approve;
401 $patron->discard_changes;
402 is( $patron->dateofbirth, undef, 'dateofbirth must a been set to NULL if required' );
404 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
405 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
407 # Adding a dateofbirth
408 $patron_modification = Koha::Patron::Modification->new( {
409 changed_fields => 'borrowernumber,dateofbirth',
410 borrowernumber => $patron->borrowernumber,
411 dateofbirth => '1980-02-02'
413 $patron_modification->approve;
415 $patron->discard_changes;
416 is( $patron->dateofbirth, '1980-02-02', 'dateofbirth must a been set' );
417 is( $patron->surname, 'a_surname', 'surname must not be updated' );
419 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
420 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
422 # Modifying a dateofbirth
423 $patron_modification = Koha::Patron::Modification->new( {
424 changed_fields => 'borrowernumber,dateofbirth',
425 borrowernumber => $patron->borrowernumber,
426 dateofbirth => '1980-03-03',
429 $patron_modification->approve;
431 $patron->discard_changes;
432 is( $patron->dateofbirth, '1980-03-03', 'dateofbirth must a been updated' );
433 is( $patron->surname, 'a_surname', 'surname must not be updated' );
435 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
436 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
438 # Modifying something else
439 $patron_modification = Koha::Patron::Modification->new( {
440 changed_fields => 'borrowernumber,surname',
441 borrowernumber => $patron->borrowernumber,
442 surname => 'another_surname',
445 $patron_modification->approve;
447 $patron->discard_changes;
448 is( $patron->surname, 'another_surname', 'surname must be updated' );
449 is( $patron->dateofbirth, '1980-03-03', 'dateofbirth should not have been updated if not needed' );
451 $schema->storage->txn_rollback;