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>.
20 use Test::More tests => 6;
23 use t::lib::TestBuilder;
25 use Digest::MD5 qw( md5_base64 md5_hex );
30 use C4::Members::Attributes qw( GetBorrowerAttributes );
32 use Koha::Patron::Attribute;
35 use_ok('Koha::Patron::Modification');
36 use_ok('Koha::Patron::Modifications');
39 my $schema = Koha::Database->new->schema;
40 my $builder = t::lib::TestBuilder->new;
42 subtest 'new() tests' => sub {
46 $schema->storage->txn_begin;
48 Koha::Patron::Modifications->search->delete;
50 # Create new pending modification
51 Koha::Patron::Modification->new(
52 { verification_token => '1234567890',
58 ## Get the new pending modification
59 my $borrower = Koha::Patron::Modifications->find(
60 { verification_token => '1234567890' } );
62 ## Verify we get the same data
63 is( $borrower->surname, 'Hall',
64 'Found modification has matching surname' );
67 Koha::Patron::Modification->new(
68 { verification_token => '1234567890',
74 'Koha::Exceptions::Patron::Modification::DuplicateVerificationToken',
75 'Attempting to add a duplicate verification raises the correct exception';
77 'Duplicate verification token 1234567890',
78 'Exception carries the right message'
81 $schema->storage->txn_rollback;
84 subtest 'store( extended_attributes ) tests' => sub {
88 $schema->storage->txn_begin;
90 Koha::Patron::Modifications->search->delete;
93 = $builder->build( { source => 'Borrower' } )->{borrowernumber};
94 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
95 my $valid_json_text = '[{"code":"CODE","value":"VALUE"}]';
96 my $invalid_json_text = '[{"code":"CODE";"value":"VALUE"}]';
98 Koha::Patron::Modification->new(
99 { verification_token => $verification_token,
100 borrowernumber => $patron,
102 extended_attributes => $valid_json_text
106 my $patron_modification
107 = Koha::Patron::Modifications->search( { borrowernumber => $patron } )
110 is( $patron_modification->surname,
111 'Hall', 'Patron modification correctly stored with valid JSON data' );
112 is( $patron_modification->extended_attributes,
114 'Patron modification correctly stored with valid JSON data' );
116 $verification_token = md5_hex( time().{}.rand().{}.$$ );
118 Koha::Patron::Modification->new(
119 { verification_token => $verification_token,
120 borrowernumber => $patron,
122 extended_attributes => $invalid_json_text
126 'Koha::Exceptions::Patron::Modification::InvalidData',
127 'Trying to store invalid JSON in extended_attributes field raises exception';
129 is( $@, 'The passed extended_attributes is not valid JSON' );
131 $schema->storage->txn_rollback;
134 subtest 'approve tests' => sub {
138 $schema->storage->txn_begin;
140 Koha::Patron::Modifications->search->delete;
142 my $patron_hashref = $builder->build( { source => 'Borrower' } );
144 { source => 'BorrowerAttributeType', value => { code => 'CODE_1' } }
147 { source => 'BorrowerAttributeType', value => { code => 'CODE_2' } }
149 my $verification_token = md5_hex( time().{}.rand().{}.$$ );
151 = '[{"code":"CODE_1","value":"VALUE_1"},{"code":"CODE_2","value":0}]';
152 my $patron_modification = Koha::Patron::Modification->new(
153 { borrowernumber => $patron_hashref->{borrowernumber},
155 verification_token => $verification_token,
156 extended_attributes => $valid_json_text
160 ok( $patron_modification->approve,
161 'Patron modification correctly approved' );
162 my $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
165 $patron_hashref->{firstname},
166 'Patron modification changed firstname'
168 is( $patron->firstname, 'Kyle',
169 'Patron modification set the right firstname' );
170 my @patron_attributes = GetBorrowerAttributes( $patron->borrowernumber );
171 is( $patron_attributes[0][0]->{code},
172 'CODE_1', 'Patron modification correctly saved attribute code' );
173 is( $patron_attributes[0][0]->{value},
174 'VALUE_1', 'Patron modification correctly saved attribute value' );
175 is( $patron_attributes[0][1]->{code},
176 'CODE_2', 'Patron modification correctly saved attribute code' );
177 is( $patron_attributes[0][1]->{value},
178 0, 'Patron modification correctly saved attribute with value 0, not confused with delete' );
180 # Create a new Koha::Patron::Modification, skip extended_attributes to
182 $patron_modification = Koha::Patron::Modification->new(
183 { borrowernumber => $patron_hashref->{borrowernumber},
184 firstname => 'Kylie',
185 verification_token => $verification_token
189 # Add invalid JSON to extended attributes
190 $patron_modification->extended_attributes(
191 '[{"code":"CODE";"values:VALUES"}]');
192 throws_ok { $patron_modification->approve }
193 'Koha::Exceptions::Patron::Modification::InvalidData',
194 'The right exception is thrown if invalid data is on extended_attributes';
196 $patron = Koha::Patrons->find( $patron_hashref->{borrowernumber} );
197 isnt( $patron->firstname, 'Kylie', 'Patron modification didn\'t apply' );
199 # Try changing only a portion of the attributes
201 = '[{"code":"CODE_2","value":"Tomasito"},{"code":"CODE_2","value":"None"}]';
202 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
204 $patron_modification = Koha::Patron::Modification->new(
205 { borrowernumber => $patron->borrowernumber,
206 extended_attributes => $bigger_json,
207 verification_token => $verification_token
210 ok( $patron_modification->approve,
211 'Patron modification correctly approved' );
213 = map { $_->unblessed }
214 Koha::Patron::Attributes->search(
215 { borrowernumber => $patron->borrowernumber } );
217 is( $patron_attributes[0]->{code},
218 'CODE_1', 'Untouched attribute type is preserved (code)' );
219 is( $patron_attributes[0]->{attribute},
220 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
222 is( $patron_attributes[1]->{code},
223 'CODE_2', 'Attribute updated correctly (code)' );
224 is( $patron_attributes[1]->{attribute},
225 'Tomasito', 'Attribute updated correctly (attribute)' );
227 is( $patron_attributes[2]->{code},
228 'CODE_2', 'Attribute updated correctly (code)' );
229 is( $patron_attributes[2]->{attribute},
230 'None', 'Attribute updated correctly (attribute)' );
232 my $empty_code_json = '[{"code":"CODE_2","value":""}]';
233 $verification_token = md5_hex( time() . {} . rand() . {} . $$ );
235 $patron_modification = Koha::Patron::Modification->new(
236 { borrowernumber => $patron->borrowernumber,
237 extended_attributes => $empty_code_json,
238 verification_token => $verification_token
241 ok( $patron_modification->approve,
242 'Patron modification correctly approved' );
244 = map { $_->unblessed }
245 Koha::Patron::Attributes->search(
246 { borrowernumber => $patron->borrowernumber } );
248 is( $patron_attributes[0]->{code},
249 'CODE_1', 'Untouched attribute type is preserved (code)' );
250 is( $patron_attributes[0]->{attribute},
251 'VALUE_1', 'Untouched attribute type is preserved (attribute)' );
253 my $count = Koha::Patron::Attributes->search({ borrowernumber => $patron->borrowernumber, code => 'CODE_2' })->count;
254 is( $count, 0, 'Attributes deleted when modification contained an empty one');
256 $schema->storage->txn_rollback;
259 subtest 'pending_count() and pending() tests' => sub {
263 $schema->storage->txn_begin;
265 Koha::Patron::Modifications->search->delete;
266 my $library_1 = $builder->build( { source => 'Branch' } )->{branchcode};
267 my $library_2 = $builder->build( { source => 'Branch' } )->{branchcode};
268 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_1' } });
269 $builder->build({ source => 'BorrowerAttributeType', value => { code => 'CODE_2', repeatable => 1 } });
273 { source => 'Borrower', value => { branchcode => $library_1 } } )
277 { source => 'Borrower', value => { branchcode => $library_2 } } )
281 { source => 'Borrower', value => { branchcode => $library_2 } } )
283 my $verification_token_1 = md5_hex( time().{}.rand().{}.$$ );
284 my $verification_token_2 = md5_hex( time().{}.rand().{}.$$ );
285 my $verification_token_3 = md5_hex( time().{}.rand().{}.$$ );
287 Koha::Patron::Attribute->new({ borrowernumber => $patron_1, code => 'CODE_1', attribute => 'hello' } )->store();
288 Koha::Patron::Attribute->new({ borrowernumber => $patron_2, code => 'CODE_2', attribute => 'bye' } )->store();
290 my $modification_1 = Koha::Patron::Modification->new(
291 { borrowernumber => $patron_1,
294 verification_token => $verification_token_1,
295 extended_attributes => '[{"code":"CODE_1","value":""}]'
299 is( Koha::Patron::Modifications->pending_count,
300 1, 'pending_count() correctly returns 1' );
302 my $modification_2 = Koha::Patron::Modification->new(
303 { borrowernumber => $patron_2,
305 firstname => 'Sandy',
306 verification_token => $verification_token_2,
307 extended_attributes => '[{"code":"CODE_2","value":"chau"},{"code":"CODE_2","value":"ciao"}]'
311 my $modification_3 = Koha::Patron::Modification->new(
312 { borrowernumber => $patron_3,
314 firstname => 'Sandy',
315 verification_token => $verification_token_3
319 is( Koha::Patron::Modifications->pending_count,
320 3, 'pending_count() correctly returns 3' );
322 my $pending = Koha::Patron::Modifications->pending();
323 is( scalar @{$pending}, 3, 'pending() returns an array with 3 elements' );
325 my @filtered_modifications = grep { $_->{borrowernumber} eq $patron_1 } @{$pending};
326 my $p1_pm = $filtered_modifications[0];
327 my $p1_pm_attribute_1 = $p1_pm->{extended_attributes}->[0];
329 is( scalar @{$p1_pm->{extended_attributes}}, 1, 'patron 1 has modification has one pending attribute modification' );
330 is( ref($p1_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
331 is( $p1_pm_attribute_1->attribute, '', 'patron 1 has an empty value for the attribute' );
333 @filtered_modifications = grep { $_->{borrowernumber} eq $patron_2 } @{$pending};
334 my $p2_pm = $filtered_modifications[0];
336 is( scalar @{$p2_pm->{extended_attributes}}, 2 , 'patron 2 has 2 attribute modifications' );
338 my $p2_pm_attribute_1 = $p2_pm->{extended_attributes}->[0];
339 my $p2_pm_attribute_2 = $p2_pm->{extended_attributes}->[1];
341 is( ref($p2_pm_attribute_1), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
342 is( ref($p2_pm_attribute_2), 'Koha::Patron::Attribute', 'patron modification has single attribute object' );
344 is( $p2_pm_attribute_1->attribute, 'chau', 'patron modification has the right attribute change' );
345 is( $p2_pm_attribute_2->attribute, 'ciao', 'patron modification has the right attribute change' );
347 is( Koha::Patron::Modifications->pending_count($library_1),
348 1, 'pending_count() correctly returns 1 if filtered by library' );
350 is( Koha::Patron::Modifications->pending_count($library_2),
351 2, 'pending_count() correctly returns 2 if filtered by library' );
353 $modification_1->approve;
355 is( Koha::Patron::Modifications->pending_count,
356 2, 'pending_count() correctly returns 2' );
358 $modification_2->approve;
360 is( Koha::Patron::Modifications->pending_count,
361 1, 'pending_count() correctly returns 1' );
363 $modification_3->approve;
365 is( Koha::Patron::Modifications->pending_count,
366 0, 'pending_count() correctly returns 0' );
368 $schema->storage->txn_rollback;