Bug 23151: Add tests
[koha.git] / t / db_dependent / Koha / Patron / Modifications.t
blob9bdf28973f9e628bfc9971becb4656f689cc5a19
1 #!/usr/bin/perl
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>.
18 use Modern::Perl;
20 use utf8;
22 use Test::More tests => 7;
23 use Test::Exception;
25 use t::lib::TestBuilder;
26 use t::lib::Mocks;
28 use Digest::MD5 qw( md5_base64 md5_hex );
29 use Try::Tiny;
31 use C4::Context;
32 use C4::Members;
33 use C4::Members::Attributes qw( GetBorrowerAttributes );
34 use Koha::Patrons;
35 use Koha::Patron::Attribute;
37 BEGIN {
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 {
47 plan tests => 3;
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 surname => 'Hall',
57 firstname => 'Kyle'
59 )->store();
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' );
69 throws_ok {
70 Koha::Patron::Modification->new(
71 { verification_token => '1234567890',
72 surname => 'Hall',
73 firstname => 'Daria'
75 )->store();
77 'Koha::Exceptions::Patron::Modification::DuplicateVerificationToken',
78 'Attempting to add a duplicate verification raises the correct exception';
79 is( $@,
80 'Duplicate verification token 1234567890',
81 'Exception carries the right message'
84 $schema->storage->txn_rollback;
87 subtest 'store( extended_attributes ) tests' => sub {
89 plan tests => 4;
91 $schema->storage->txn_begin;
93 Koha::Patron::Modifications->search->delete;
95 my $patron
96 = $builder->build( { source => 'Borrower' } )->{borrowernumber};
97 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
98 my $valid_json_text = '[{"code":"CODE","value":"VALUE"}]';
99 my $invalid_json_text = '[{"code":"CODE";"value":"VALUE"}]';
101 Koha::Patron::Modification->new(
102 { verification_token => $verification_token,
103 borrowernumber => $patron,
104 surname => 'Hall',
105 extended_attributes => $valid_json_text
107 )->store();
109 my $patron_modification
110 = Koha::Patron::Modifications->search( { borrowernumber => $patron } )
111 ->next;
113 is( $patron_modification->surname,
114 'Hall', 'Patron modification correctly stored with valid JSON data' );
115 is( $patron_modification->extended_attributes,
116 $valid_json_text,
117 'Patron modification correctly stored with valid JSON data' );
119 $verification_token = md5_hex( time().{}.rand().{}.$$ );
120 throws_ok {
121 Koha::Patron::Modification->new(
122 { verification_token => $verification_token,
123 borrowernumber => $patron,
124 surname => 'Hall',
125 extended_attributes => $invalid_json_text
127 )->store();
129 'Koha::Exceptions::Patron::Modification::InvalidData',
130 'Trying to store invalid JSON in extended_attributes field raises exception';
132 is( $@, 'The passed extended_attributes is not valid JSON' );
134 $schema->storage->txn_rollback;
137 subtest 'approve tests' => sub {
139 plan tests => 20;
141 $schema->storage->txn_begin;
143 Koha::Patron::Modifications->search->delete;
145 my $patron_hashref = $builder->build( { source => 'Borrower' } );
146 $builder->build(
147 { source => 'BorrowerAttributeType', value => { code => 'CODE_1' } }
149 $builder->build(
150 { source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } }
152 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
153 my $valid_json_text
154 = '[{"code":"CODE_1","value":"VALUE_1"},{"code":"CODE_2","value":0}]';
155 my $patron_modification = Koha::Patron::Modification->new(
156 { borrowernumber => $patron_hashref->{borrowernumber},
157 firstname => 'Kyle',
158 verification_token => $verification_token,
159 extended_attributes => $valid_json_text
161 )->store();
163 ok( $patron_modification->approve,
164 'Patron modification correctly approved' );
165 my $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
166 isnt(
167 $patron->firstname,
168 $patron_hashref->{firstname},
169 'Patron modification changed firstname'
171 is( $patron->firstname, 'Kyle',
172 'Patron modification set the right firstname' );
173 my @patron_attributes = GetBorrowerAttributes( $patron->borrowernumber );
174 is( $patron_attributes[0][0]->{code},
175 'CODE_1', 'Patron modification correctly saved attribute code' );
176 is( $patron_attributes[0][0]->{value},
177 'VALUE_1', 'Patron modification correctly saved attribute value' );
178 is( $patron_attributes[0][1]->{code},
179 'CODE_2', 'Patron modification correctly saved attribute code' );
180 is( $patron_attributes[0][1]->{value},
181 0, 'Patron modification correctly saved attribute with value 0, not confused with delete' );
183 # Create a new Koha::Patron::Modification, skip extended_attributes to
184 # bypass checks
185 $patron_modification = Koha::Patron::Modification->new(
186 { borrowernumber => $patron_hashref->{borrowernumber},
187 firstname => 'Kylie',
188 verification_token => $verification_token
190 )->store();
192 # Add invalid JSON to extended attributes
193 $patron_modification->extended_attributes(
194 '[{"code":"CODE";"values:VALUES"}]');
195 throws_ok { $patron_modification->approve }
196 'Koha::Exceptions::Patron::Modification::InvalidData',
197 'The right exception is thrown if invalid data is on extended_attributes';
199 $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
200 isnt( $patron->firstname, 'Kylie', 'Patron modification didn\'t apply' );
202 # Try changing only a portion of the attributes
203 my $bigger_json
204 = '[{"code":"CODE_2","value":"Tomasito"},{"code":"CODE_2","value":"None"}]';
205 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
207 $patron_modification = Koha::Patron::Modification->new(
208 { borrowernumber => $patron->borrowernumber,
209 extended_attributes => $bigger_json,
210 verification_token => $verification_token
212 )->store();
213 ok( $patron_modification->approve,
214 'Patron modification correctly approved' );
215 @patron_attributes
216 = map { $_->unblessed }
217 Koha::Patron::Attributes->search(
218 { borrowernumber => $patron->borrowernumber } );
220 is( $patron_attributes[0]->{code},
221 'CODE_1', 'Untouched attribute type is preserved (code)' );
222 is( $patron_attributes[0]->{attribute},
223 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
225 is( $patron_attributes[1]->{code},
226 'CODE_2', 'Attribute updated correctly (code)' );
227 is( $patron_attributes[1]->{attribute},
228 'Tomasito', 'Attribute updated correctly (attribute)' );
230 is( $patron_attributes[2]->{code},
231 'CODE_2', 'Attribute updated correctly (code)' );
232 is( $patron_attributes[2]->{attribute},
233 'None', 'Attribute updated correctly (attribute)' );
235 my $empty_code_json = '[{"code":"CODE_2","value":""}]';
236 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
238 $patron_modification = Koha::Patron::Modification->new(
239 { borrowernumber => $patron->borrowernumber,
240 extended_attributes => $empty_code_json,
241 verification_token => $verification_token
243 )->store();
244 ok( $patron_modification->approve,
245 'Patron modification correctly approved' );
246 @patron_attributes
247 = map { $_->unblessed }
248 Koha::Patron::Attributes->search(
249 { borrowernumber => $patron->borrowernumber } );
251 is( $patron_attributes[0]->{code},
252 'CODE_1', 'Untouched attribute type is preserved (code)' );
253 is( $patron_attributes[0]->{attribute},
254 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
256 my $count = Koha::Patron::Attributes->search({ borrowernumber => $patron->borrowernumber, code => 'CODE_2' })->count;
257 is( $count, 0, 'Attributes deleted when modification contained an empty one');
259 $schema->storage->txn_rollback;
262 subtest 'pending_count() and pending() tests' => sub {
264 plan tests => 16;
266 $schema->storage->txn_begin;
268 Koha::Patron::Modifications->search->delete;
269 my $library_1 = $builder->build( { source => 'Branch' } )->{branchcode};
270 my $library_2 = $builder->build( { source => 'Branch' } )->{branchcode};
271 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_1' } });
272 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } });
274 my $patron_1
275 = $builder->build(
276 { source => 'Borrower', value => { branchcode => $library_1, flags => 1 } } );
277 my $patron_2
278 = $builder->build(
279 { source => 'Borrower', value => { branchcode => $library_2 } } );
280 my $patron_3
281 = $builder->build(
282 { source => 'Borrower', value => { branchcode => $library_2 } } );
283 $patron_1 = Koha::Patrons->find( $patron_1->{borrowernumber} );
284 $patron_2 = Koha::Patrons->find( $patron_2->{borrowernumber} );
285 $patron_3 = Koha::Patrons->find( $patron_3->{borrowernumber} );
286 my $verification_token_1 = md5_hex( time().{}.rand().{}.$$ );
287 my $verification_token_2 = md5_hex( time().{}.rand().{}.$$ );
288 my $verification_token_3 = md5_hex( time().{}.rand().{}.$$ );
290 Koha::Patron::Attribute->new({ borrowernumber => $patron_1->borrowernumber, code => 'CODE_1', attribute => 'hello' } )->store();
291 Koha::Patron::Attribute->new({ borrowernumber => $patron_2->borrowernumber, code => 'CODE_2', attribute => 'bye' } )->store();
293 my $modification_1 = Koha::Patron::Modification->new(
294 { borrowernumber => $patron_1->borrowernumber,
295 surname => 'Hall',
296 firstname => 'Kyle',
297 verification_token => $verification_token_1,
298 extended_attributes => '[{"code":"CODE_1","value":""}]'
300 )->store();
302 is( Koha::Patron::Modifications->pending_count,
303 1, 'pending_count() correctly returns 1' );
305 my $modification_2 = Koha::Patron::Modification->new(
306 { borrowernumber => $patron_2->borrowernumber,
307 surname => 'Smith',
308 firstname => 'Sandy',
309 verification_token => $verification_token_2,
310 extended_attributes => '[{"code":"CODE_2","value":"año"},{"code":"CODE_2","value":"ciao"}]'
312 )->store();
314 my $modification_3 = Koha::Patron::Modification->new(
315 { borrowernumber => $patron_3->borrowernumber,
316 surname => 'Smithy',
317 firstname => 'Sandy',
318 verification_token => $verification_token_3
320 )->store();
322 is( Koha::Patron::Modifications->pending_count,
323 3, 'pending_count() correctly returns 3' );
325 my $pending = Koha::Patron::Modifications->pending();
326 is( scalar @{$pending}, 3, 'pending() returns an array with 3 elements' );
328 my @filtered_modifications = grep { $_->{borrowernumber} eq $patron_1->borrowernumber } @{$pending};
329 my $p1_pm = $filtered_modifications[0];
330 my $p1_pm_attribute_1 = $p1_pm->{extended_attributes}->[0];
332 is( scalar @{$p1_pm->{extended_attributes}}, 1, 'patron 1 has modification has one pending attribute modification' );
333 is( ref($p1_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
334 is( $p1_pm_attribute_1->attribute, '', 'patron 1 has an empty value for the attribute' );
336 @filtered_modifications = grep { $_->{borrowernumber} eq $patron_2->borrowernumber } @{$pending};
337 my $p2_pm = $filtered_modifications[0];
339 is( scalar @{$p2_pm->{extended_attributes}}, 2 , 'patron 2 has 2 attribute modifications' );
341 my $p2_pm_attribute_1 = $p2_pm->{extended_attributes}->[0];
342 my $p2_pm_attribute_2 = $p2_pm->{extended_attributes}->[1];
344 is( ref($p2_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
345 is( ref($p2_pm_attribute_2), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
347 is( $p2_pm_attribute_1->attribute, 'año', 'patron modification has the right attribute change' );
348 is( $p2_pm_attribute_2->attribute, 'ciao', 'patron modification has the right attribute change' );
351 t::lib::Mocks::mock_userenv({ patron => $patron_1 });
352 is( Koha::Patron::Modifications->pending_count($library_1),
353 1, 'pending_count() correctly returns 1 if filtered by library' );
355 is( Koha::Patron::Modifications->pending_count($library_2),
356 2, 'pending_count() correctly returns 2 if filtered by library' );
358 $modification_1->approve;
360 is( Koha::Patron::Modifications->pending_count,
361 2, 'pending_count() correctly returns 2' );
363 $modification_2->approve;
365 is( Koha::Patron::Modifications->pending_count,
366 1, 'pending_count() correctly returns 1' );
368 $modification_3->approve;
370 is( Koha::Patron::Modifications->pending_count,
371 0, 'pending_count() correctly returns 0' );
373 $schema->storage->txn_rollback;
376 subtest 'dateofbirth tests' => sub {
377 plan tests => 7;
379 $schema->storage->txn_begin;
381 # Cleaning the field
382 my $patron = $builder->build_object( { class => 'Koha::Patrons', value => { dateofbirth => '1980-01-01', surname => 'a_surname' } } );
383 my $patron_modification = Koha::Patron::Modification->new( { borrowernumber => $patron->borrowernumber, dateofbirth => undef }
384 )->store;
385 $patron_modification->approve;
387 $patron->discard_changes;
388 is( $patron->dateofbirth, undef, 'dateofbirth must a been set to NULL if required' );
390 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
391 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
393 # Adding a dateofbirth
394 $patron_modification = Koha::Patron::Modification->new( { borrowernumber => $patron->borrowernumber, dateofbirth => '1980-02-02' }
395 )->store;
396 $patron_modification->approve;
398 $patron->discard_changes;
399 is( $patron->dateofbirth, '1980-02-02', 'dateofbirth must a been set' );
400 is( $patron->surname, 'a_surname', 'surname must not be updated' );
402 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
403 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
405 # Modifying a dateofbirth
406 $patron_modification = Koha::Patron::Modification->new( { borrowernumber => $patron->borrowernumber, dateofbirth => '1980-03-03', surname => undef }
407 )->store;
408 $patron_modification->approve;
410 $patron->discard_changes;
411 is( $patron->dateofbirth, '1980-03-03', 'dateofbirth must a been updated' );
412 is( $patron->surname, 'a_surname', 'surname must not be updated' );
414 # FIXME ->approve must have been removed it, but it did not. There may be an hidden bug here.
415 Koha::Patron::Modifications->search({ borrowernumber => $patron->borrowernumber })->delete;
417 # Modifying something else
418 $patron_modification = Koha::Patron::Modification->new( { borrowernumber => $patron->borrowernumber, surname => 'another_surname', dateofbirth => undef }
419 )->store;
420 $patron_modification->approve;
422 $patron->discard_changes;
423 is( $patron->surname, 'another_surname', 'surname must be updated' );
424 is( $patron->dateofbirth, '1980-03-03', 'dateofbirth should not have been updated if not needed' );
426 $schema->storage->txn_rollback;