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 );
34 use Koha::Patron::Attribute;
37 use_ok('Koha::Patron::Modification');
38 use_ok('Koha::Patron::Modifications');
41 my $schema = Koha::Database->new->schema;
42 my $builder = t::lib::TestBuilder->new;
44 subtest 'new() tests' => sub {
48 $schema->storage->txn_begin;
50 Koha::Patron::Modifications->search->delete;
52 # Create new pending modification
53 Koha::Patron::Modification->new(
54 { verification_token => '1234567890',
55 changed_fields => 'surname,firstname',
61 ## Get the new pending modification
62 my $borrower = Koha::Patron::Modifications->find(
63 { verification_token => '1234567890' } );
65 ## Verify we get the same data
66 is( $borrower->surname, 'Hall',
67 'Found modification has matching surname' );
70 Koha::Patron::Modification->new(
71 { verification_token => '1234567890',
72 changed_fields => 'surname,firstname',
78 'Koha::Exceptions::Patron::Modification::DuplicateVerificationToken',
79 'Attempting to add a duplicate verification raises the correct exception';
81 'Duplicate verification token 1234567890',
82 'Exception carries the right message'
85 $schema->storage->txn_rollback;
88 subtest 'store( extended_attributes ) tests' => sub {
92 $schema->storage->txn_begin;
94 Koha::Patron::Modifications->search->delete;
97 = $builder->build( { source => 'Borrower' } )->{borrowernumber};
98 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
99 my $valid_json_text = '[{"code":"CODE","value":"VALUE"}]';
100 my $invalid_json_text = '[{"code":"CODE";"value":"VALUE"}]';
102 Koha::Patron::Modification->new(
103 { verification_token => $verification_token,
104 changed_fields => 'borrowernumber,surname,extended_attributes',
105 borrowernumber => $patron,
107 extended_attributes => $valid_json_text
111 my $patron_modification
112 = Koha::Patron::Modifications->search( { borrowernumber => $patron } )
115 is( $patron_modification->surname,
116 'Hall', 'Patron modification correctly stored with valid JSON data' );
117 is( $patron_modification->extended_attributes,
119 'Patron modification correctly stored with valid JSON data' );
121 $verification_token = md5_hex( time().{}.rand().{}.$$ );
123 Koha::Patron::Modification->new(
124 { verification_token => $verification_token,
125 changed_fields => 'borrowernumber,surname,extended_attributes',
126 borrowernumber => $patron,
128 extended_attributes => $invalid_json_text
132 'Koha::Exceptions::Patron::Modification::InvalidData',
133 'Trying to store invalid JSON in extended_attributes field raises exception';
135 is( $@, 'The passed extended_attributes is not valid JSON' );
137 $schema->storage->txn_rollback;
140 subtest 'approve tests' => sub {
144 $schema->storage->txn_begin;
146 Koha::Patron::Modifications->search->delete;
148 my $patron_hashref = $builder->build( { source => 'Borrower' } );
150 { source => 'BorrowerAttributeType', value => { code => 'CODE_1' } }
153 { source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } }
155 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
157 = '[{"code":"CODE_1","value":"VALUE_1"},{"code":"CODE_2","value":0}]';
158 my $patron_modification = Koha::Patron::Modification->new(
159 { verification_token => $verification_token,
160 changed_fields => 'borrowernumber,firstname,extended_attributes',
161 borrowernumber => $patron_hashref->{borrowernumber},
163 extended_attributes => $valid_json_text
167 ok( $patron_modification->approve,
168 'Patron modification correctly approved' );
169 my $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
172 $patron_hashref->{firstname},
173 'Patron modification changed firstname'
175 is( $patron->firstname, 'Kyle',
176 'Patron modification set the right firstname' );
177 my $patron_attributes = $patron->extended_attributes;
178 my $attribute_1 = $patron_attributes->next;
179 is( $attribute_1->code,
180 'CODE_1', 'Patron modification correctly saved attribute code' );
181 is( $attribute_1->attribute,
182 'VALUE_1', 'Patron modification correctly saved attribute value' );
183 my $attribute_2 = $patron_attributes->next;
184 is( $attribute_2->code,
185 'CODE_2', 'Patron modification correctly saved attribute code' );
186 is( $attribute_2->attribute,
187 0, 'Patron modification correctly saved attribute with value 0, not confused with delete' );
189 # Create a new Koha::Patron::Modification, skip extended_attributes to
191 $patron_modification = Koha::Patron::Modification->new(
192 { verification_token => $verification_token,
193 changed_fields => 'borrowernumber,firstname',
194 borrowernumber => $patron_hashref->{borrowernumber},
199 # Add invalid JSON to extended attributes
200 $patron_modification->extended_attributes(
201 '[{"code":"CODE";"values:VALUES"}]');
202 throws_ok { $patron_modification->approve }
203 'Koha::Exceptions::Patron::Modification::InvalidData',
204 'The right exception is thrown if invalid data is on extended_attributes';
206 $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
207 isnt( $patron->firstname, 'Kylie', 'Patron modification didn\'t apply' );
209 # Try changing only a portion of the attributes
211 = '[{"code":"CODE_2","value":"Tomasito"},{"code":"CODE_2","value":"None"}]';
212 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
214 $patron_modification = Koha::Patron::Modification->new(
215 { verification_token => $verification_token,
216 changed_fields => 'borrowernumber,extended_attributes',
217 borrowernumber => $patron->borrowernumber,
218 extended_attributes => $bigger_json
221 ok( $patron_modification->approve,
222 'Patron modification correctly approved' );
223 my @patron_attributes
224 = map { $_->unblessed }
225 Koha::Patron::Attributes->search(
226 { borrowernumber => $patron->borrowernumber } );
228 is( $patron_attributes[0]->{code},
229 'CODE_1', 'Untouched attribute type is preserved (code)' );
230 is( $patron_attributes[0]->{attribute},
231 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
233 is( $patron_attributes[1]->{code},
234 'CODE_2', 'Attribute updated correctly (code)' );
235 is( $patron_attributes[1]->{attribute},
236 'None', 'Attribute updated correctly (attribute)' );
238 is( $patron_attributes[2]->{code},
239 'CODE_2', 'Attribute updated correctly (code)' );
240 is( $patron_attributes[2]->{attribute},
241 'Tomasito', 'Attribute updated correctly (attribute)' );
243 my $empty_code_json = '[{"code":"CODE_2","value":""}]';
244 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
246 $patron_modification = Koha::Patron::Modification->new(
247 { verification_token => $verification_token,
248 changed_fields => 'borrowernumber,extended_attributes',
249 borrowernumber => $patron->borrowernumber,
250 extended_attributes => $empty_code_json
253 ok( $patron_modification->approve,
254 'Patron modification correctly approved' );
256 = map { $_->unblessed }
257 Koha::Patron::Attributes->search(
258 { borrowernumber => $patron->borrowernumber } );
260 is( $patron_attributes[0]->{code},
261 'CODE_1', 'Untouched attribute type is preserved (code)' );
262 is( $patron_attributes[0]->{attribute},
263 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
265 my $count = Koha::Patron::Attributes->search({ borrowernumber => $patron->borrowernumber, code => 'CODE_2' })->count;
266 is( $count, 0, 'Attributes deleted when modification contained an empty one');
268 $schema->storage->txn_rollback;
271 subtest 'pending_count() and pending() tests' => sub {
275 $schema->storage->txn_begin;
277 Koha::Patron::Modifications->search->delete;
278 my $library_1 = $builder->build( { source => 'Branch' } )->{branchcode};
279 my $library_2 = $builder->build( { source => 'Branch' } )->{branchcode};
280 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_1' } });
281 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } });
285 { source => 'Borrower', value => { branchcode => $library_1, flags => 1 } } );
288 { source => 'Borrower', value => { branchcode => $library_2 } } );
291 { source => 'Borrower', value => { branchcode => $library_2 } } );
292 $patron_1 = Koha::Patrons->find( $patron_1->{borrowernumber} );
293 $patron_2 = Koha::Patrons->find( $patron_2->{borrowernumber} );
294 $patron_3 = Koha::Patrons->find( $patron_3->{borrowernumber} );
295 my $verification_token_1 = md5_hex( time().{}.rand().{}.$$ );
296 my $verification_token_2 = md5_hex( time().{}.rand().{}.$$ );
297 my $verification_token_3 = md5_hex( time().{}.rand().{}.$$ );
299 Koha::Patron::Attribute->new({ borrowernumber => $patron_1->borrowernumber, code => 'CODE_1', attribute => 'hello' } )->store();
300 Koha::Patron::Attribute->new({ borrowernumber => $patron_2->borrowernumber, code => 'CODE_2', attribute => 'bye' } )->store();
302 my $modification_1 = Koha::Patron::Modification->new(
303 { verification_token => $verification_token_1,
304 changed_fields => 'borrowernumber,surname,firstname,extended_attributes',
305 borrowernumber => $patron_1->borrowernumber,
308 extended_attributes => '[{"code":"CODE_1","value":""}]'
312 is( Koha::Patron::Modifications->pending_count,
313 1, 'pending_count() correctly returns 1' );
315 my $modification_2 = Koha::Patron::Modification->new(
316 { verification_token => $verification_token_2,
317 changed_fields => 'borrowernumber,surname,firstname,extended_attributes',
318 borrowernumber => $patron_2->borrowernumber,
320 firstname => 'Sandy',
321 extended_attributes => '[{"code":"CODE_2","value":"año"},{"code":"CODE_2","value":"ciao"}]'
325 my $modification_3 = Koha::Patron::Modification->new(
326 { verification_token => $verification_token_3,
327 changed_fields => 'borrowernumber,surname,firstname',
328 borrowernumber => $patron_3->borrowernumber,
334 is( Koha::Patron::Modifications->pending_count,
335 3, 'pending_count() correctly returns 3' );
337 my $pending = Koha::Patron::Modifications->pending();
338 is( scalar @{$pending}, 3, 'pending() returns an array with 3 elements' );
340 my @filtered_modifications = grep { $_->{borrowernumber} eq $patron_1->borrowernumber } @{$pending};
341 my $p1_pm = $filtered_modifications[0];
342 my $p1_pm_attribute_1 = $p1_pm->{extended_attributes}->[0];
344 is( scalar @{$p1_pm->{extended_attributes}}, 1, 'patron 1 has modification has one pending attribute modification' );
345 is( ref($p1_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
346 is( $p1_pm_attribute_1->attribute, '', 'patron 1 has an empty value for the attribute' );
348 @filtered_modifications = grep { $_->{borrowernumber} eq $patron_2->borrowernumber } @{$pending};
349 my $p2_pm = $filtered_modifications[0];
351 is( scalar @{$p2_pm->{extended_attributes}}, 2 , 'patron 2 has 2 attribute modifications' );
353 my $p2_pm_attribute_1 = $p2_pm->{extended_attributes}->[0];
354 my $p2_pm_attribute_2 = $p2_pm->{extended_attributes}->[1];
356 is( ref($p2_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
357 is( ref($p2_pm_attribute_2), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
359 is( $p2_pm_attribute_1->attribute, 'año', 'patron modification has the right attribute change' );
360 is( $p2_pm_attribute_2->attribute, 'ciao', 'patron modification has the right attribute change' );
363 t::lib::Mocks::mock_userenv({ patron => $patron_1 });
364 is( Koha::Patron::Modifications->pending_count($library_1),
365 1, 'pending_count() correctly returns 1 if filtered by library' );
367 is( Koha::Patron::Modifications->pending_count($library_2),
368 2, 'pending_count() correctly returns 2 if filtered by library' );
370 $modification_1->approve;
372 is( Koha::Patron::Modifications->pending_count,
373 2, 'pending_count() correctly returns 2' );
375 $modification_2->approve;
377 is( Koha::Patron::Modifications->pending_count,
378 1, 'pending_count() correctly returns 1' );
380 $modification_3->approve;
382 is( Koha::Patron::Modifications->pending_count,
383 0, 'pending_count() correctly returns 0' );
385 $schema->storage->txn_rollback;
388 subtest 'dateofbirth tests' => sub {
391 $schema->storage->txn_begin;
394 my $patron = $builder->build_object( { class => 'Koha::Patrons', value => { dateofbirth => '1980-01-01', surname => 'a_surname' } } );
395 my $patron_modification = Koha::Patron::Modification->new( {
396 changed_fields => 'borrowernumber,dateofbirth',
397 borrowernumber => $patron->borrowernumber,
400 $patron_modification->approve;
402 $patron->discard_changes;
403 is( $patron->dateofbirth, undef, 'dateofbirth must a been set to NULL if required' );
405 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
406 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
408 # Adding a dateofbirth
409 $patron_modification = Koha::Patron::Modification->new( {
410 changed_fields => 'borrowernumber,dateofbirth',
411 borrowernumber => $patron->borrowernumber,
412 dateofbirth => '1980-02-02'
414 $patron_modification->approve;
416 $patron->discard_changes;
417 is( $patron->dateofbirth, '1980-02-02', 'dateofbirth must a been set' );
418 is( $patron->surname, 'a_surname', 'surname must not be updated' );
420 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
421 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
423 # Modifying a dateofbirth
424 $patron_modification = Koha::Patron::Modification->new( {
425 changed_fields => 'borrowernumber,dateofbirth',
426 borrowernumber => $patron->borrowernumber,
427 dateofbirth => '1980-03-03',
430 $patron_modification->approve;
432 $patron->discard_changes;
433 is( $patron->dateofbirth, '1980-03-03', 'dateofbirth must a been updated' );
434 is( $patron->surname, 'a_surname', 'surname must not be updated' );
436 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
437 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
439 # Modifying something else
440 $patron_modification = Koha::Patron::Modification->new( {
441 changed_fields => 'borrowernumber,surname',
442 borrowernumber => $patron->borrowernumber,
443 surname => 'another_surname',
446 $patron_modification->approve;
448 $patron->discard_changes;
449 is( $patron->surname, 'another_surname', 'surname must be updated' );
450 is( $patron->dateofbirth, '1980-03-03', 'dateofbirth should not have been updated if not needed' );
452 $schema->storage->txn_rollback;