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 => 6;
25 use t::lib::TestBuilder;
27 use Digest::MD5 qw( md5_base64 md5_hex );
32 use C4::Members::Attributes qw( GetBorrowerAttributes );
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',
60 ## Get the new pending modification
61 my $borrower = Koha::Patron::Modifications->find(
62 { verification_token => '1234567890' } );
64 ## Verify we get the same data
65 is( $borrower->surname, 'Hall',
66 'Found modification has matching surname' );
69 Koha::Patron::Modification->new(
70 { verification_token => '1234567890',
76 'Koha::Exceptions::Patron::Modification::DuplicateVerificationToken',
77 'Attempting to add a duplicate verification raises the correct exception';
79 'Duplicate verification token 1234567890',
80 'Exception carries the right message'
83 $schema->storage->txn_rollback;
86 subtest 'store( extended_attributes ) tests' => sub {
90 $schema->storage->txn_begin;
92 Koha::Patron::Modifications->search->delete;
95 = $builder->build( { source => 'Borrower' } )->{borrowernumber};
96 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
97 my $valid_json_text = '[{"code":"CODE","value":"VALUE"}]';
98 my $invalid_json_text = '[{"code":"CODE";"value":"VALUE"}]';
100 Koha::Patron::Modification->new(
101 { verification_token => $verification_token,
102 borrowernumber => $patron,
104 extended_attributes => $valid_json_text
108 my $patron_modification
109 = Koha::Patron::Modifications->search( { borrowernumber => $patron } )
112 is( $patron_modification->surname,
113 'Hall', 'Patron modification correctly stored with valid JSON data' );
114 is( $patron_modification->extended_attributes,
116 'Patron modification correctly stored with valid JSON data' );
118 $verification_token = md5_hex( time().{}.rand().{}.$$ );
120 Koha::Patron::Modification->new(
121 { verification_token => $verification_token,
122 borrowernumber => $patron,
124 extended_attributes => $invalid_json_text
128 'Koha::Exceptions::Patron::Modification::InvalidData',
129 'Trying to store invalid JSON in extended_attributes field raises exception';
131 is( $@, 'The passed extended_attributes is not valid JSON' );
133 $schema->storage->txn_rollback;
136 subtest 'approve tests' => sub {
140 $schema->storage->txn_begin;
142 Koha::Patron::Modifications->search->delete;
144 my $patron_hashref = $builder->build( { source => 'Borrower' } );
146 { source => 'BorrowerAttributeType', value => { code => 'CODE_1' } }
149 { source => 'BorrowerAttributeType', value => { code => 'CODE_2' } }
151 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
153 = '[{"code":"CODE_1","value":"VALUE_1"},{"code":"CODE_2","value":0}]';
154 my $patron_modification = Koha::Patron::Modification->new(
155 { borrowernumber => $patron_hashref->{borrowernumber},
157 verification_token => $verification_token,
158 extended_attributes => $valid_json_text
162 ok( $patron_modification->approve,
163 'Patron modification correctly approved' );
164 my $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
167 $patron_hashref->{firstname},
168 'Patron modification changed firstname'
170 is( $patron->firstname, 'Kyle',
171 'Patron modification set the right firstname' );
172 my @patron_attributes = GetBorrowerAttributes( $patron->borrowernumber );
173 is( $patron_attributes[0][0]->{code},
174 'CODE_1', 'Patron modification correctly saved attribute code' );
175 is( $patron_attributes[0][0]->{value},
176 'VALUE_1', 'Patron modification correctly saved attribute value' );
177 is( $patron_attributes[0][1]->{code},
178 'CODE_2', 'Patron modification correctly saved attribute code' );
179 is( $patron_attributes[0][1]->{value},
180 0, 'Patron modification correctly saved attribute with value 0, not confused with delete' );
182 # Create a new Koha::Patron::Modification, skip extended_attributes to
184 $patron_modification = Koha::Patron::Modification->new(
185 { borrowernumber => $patron_hashref->{borrowernumber},
186 firstname => 'Kylie',
187 verification_token => $verification_token
191 # Add invalid JSON to extended attributes
192 $patron_modification->extended_attributes(
193 '[{"code":"CODE";"values:VALUES"}]');
194 throws_ok { $patron_modification->approve }
195 'Koha::Exceptions::Patron::Modification::InvalidData',
196 'The right exception is thrown if invalid data is on extended_attributes';
198 $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
199 isnt( $patron->firstname, 'Kylie', 'Patron modification didn\'t apply' );
201 # Try changing only a portion of the attributes
203 = '[{"code":"CODE_2","value":"Tomasito"},{"code":"CODE_2","value":"None"}]';
204 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
206 $patron_modification = Koha::Patron::Modification->new(
207 { borrowernumber => $patron->borrowernumber,
208 extended_attributes => $bigger_json,
209 verification_token => $verification_token
212 ok( $patron_modification->approve,
213 'Patron modification correctly approved' );
215 = map { $_->unblessed }
216 Koha::Patron::Attributes->search(
217 { borrowernumber => $patron->borrowernumber } );
219 is( $patron_attributes[0]->{code},
220 'CODE_1', 'Untouched attribute type is preserved (code)' );
221 is( $patron_attributes[0]->{attribute},
222 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
224 is( $patron_attributes[1]->{code},
225 'CODE_2', 'Attribute updated correctly (code)' );
226 is( $patron_attributes[1]->{attribute},
227 'Tomasito', 'Attribute updated correctly (attribute)' );
229 is( $patron_attributes[2]->{code},
230 'CODE_2', 'Attribute updated correctly (code)' );
231 is( $patron_attributes[2]->{attribute},
232 'None', 'Attribute updated correctly (attribute)' );
234 my $empty_code_json = '[{"code":"CODE_2","value":""}]';
235 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
237 $patron_modification = Koha::Patron::Modification->new(
238 { borrowernumber => $patron->borrowernumber,
239 extended_attributes => $empty_code_json,
240 verification_token => $verification_token
243 ok( $patron_modification->approve,
244 'Patron modification correctly approved' );
246 = map { $_->unblessed }
247 Koha::Patron::Attributes->search(
248 { borrowernumber => $patron->borrowernumber } );
250 is( $patron_attributes[0]->{code},
251 'CODE_1', 'Untouched attribute type is preserved (code)' );
252 is( $patron_attributes[0]->{attribute},
253 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
255 my $count = Koha::Patron::Attributes->search({ borrowernumber => $patron->borrowernumber, code => 'CODE_2' })->count;
256 is( $count, 0, 'Attributes deleted when modification contained an empty one');
258 $schema->storage->txn_rollback;
261 subtest 'pending_count() and pending() tests' => sub {
265 $schema->storage->txn_begin;
267 Koha::Patron::Modifications->search->delete;
268 my $library_1 = $builder->build( { source => 'Branch' } )->{branchcode};
269 my $library_2 = $builder->build( { source => 'Branch' } )->{branchcode};
270 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_1' } });
271 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } });
275 { source => 'Borrower', value => { branchcode => $library_1 } } );
278 { source => 'Borrower', value => { branchcode => $library_2 } } );
281 { source => 'Borrower', value => { branchcode => $library_2 } } );
282 $patron_1 = Koha::Patrons->find( $patron_1->{borrowernumber} );
283 $patron_2 = Koha::Patrons->find( $patron_2->{borrowernumber} );
284 $patron_3 = Koha::Patrons->find( $patron_3->{borrowernumber} );
285 my $verification_token_1 = md5_hex( time().{}.rand().{}.$$ );
286 my $verification_token_2 = md5_hex( time().{}.rand().{}.$$ );
287 my $verification_token_3 = md5_hex( time().{}.rand().{}.$$ );
289 Koha::Patron::Attribute->new({ borrowernumber => $patron_1->borrowernumber, code => 'CODE_1', attribute => 'hello' } )->store();
290 Koha::Patron::Attribute->new({ borrowernumber => $patron_2->borrowernumber, code => 'CODE_2', attribute => 'bye' } )->store();
292 my $modification_1 = Koha::Patron::Modification->new(
293 { borrowernumber => $patron_1->borrowernumber,
296 verification_token => $verification_token_1,
297 extended_attributes => '[{"code":"CODE_1","value":""}]'
301 is( Koha::Patron::Modifications->pending_count,
302 1, 'pending_count() correctly returns 1' );
304 my $modification_2 = Koha::Patron::Modification->new(
305 { borrowernumber => $patron_2->borrowernumber,
307 firstname => 'Sandy',
308 verification_token => $verification_token_2,
309 extended_attributes => '[{"code":"CODE_2","value":"año"},{"code":"CODE_2","value":"ciao"}]'
313 my $modification_3 = Koha::Patron::Modification->new(
314 { borrowernumber => $patron_3->borrowernumber,
316 firstname => 'Sandy',
317 verification_token => $verification_token_3
321 is( Koha::Patron::Modifications->pending_count,
322 3, 'pending_count() correctly returns 3' );
324 my $pending = Koha::Patron::Modifications->pending();
325 is( scalar @{$pending}, 3, 'pending() returns an array with 3 elements' );
327 my @filtered_modifications = grep { $_->{borrowernumber} eq $patron_1->borrowernumber } @{$pending};
328 my $p1_pm = $filtered_modifications[0];
329 my $p1_pm_attribute_1 = $p1_pm->{extended_attributes}->[0];
331 is( scalar @{$p1_pm->{extended_attributes}}, 1, 'patron 1 has modification has one pending attribute modification' );
332 is( ref($p1_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
333 is( $p1_pm_attribute_1->attribute, '', 'patron 1 has an empty value for the attribute' );
335 @filtered_modifications = grep { $_->{borrowernumber} eq $patron_2->borrowernumber } @{$pending};
336 my $p2_pm = $filtered_modifications[0];
338 is( scalar @{$p2_pm->{extended_attributes}}, 2 , 'patron 2 has 2 attribute modifications' );
340 my $p2_pm_attribute_1 = $p2_pm->{extended_attributes}->[0];
341 my $p2_pm_attribute_2 = $p2_pm->{extended_attributes}->[1];
343 is( ref($p2_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
344 is( ref($p2_pm_attribute_2), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
346 is( $p2_pm_attribute_1->attribute, 'año', 'patron modification has the right attribute change' );
347 is( $p2_pm_attribute_2->attribute, 'ciao', 'patron modification has the right attribute change' );
350 C4::Context->_new_userenv('xxx');
351 set_logged_in_user( $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 sub set_logged_in_user {
378 C4::Context->set_userenv(
379 $patron->borrowernumber, $patron->userid,
380 $patron->cardnumber, 'firstname',
381 'surname', $patron->library->branchcode,
382 'Midway Public Library', $patron->flags,