Bug 33329: Regression tests
[koha.git] / t / db_dependent / Letters.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Copyright (C) 2013 Equinox Software, Inc.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21 use Test::More tests => 87;
22 use Test::MockModule;
23 use Test::Warn;
24 use Test::Exception;
25
26 use Email::Sender::Failure;
27
28 use MARC::Record;
29
30 use utf8;
31
32 my ( $email_object, $sendmail_params );
33 our $send_or_die_count = 0;
34
35 my $email_sender_module = Test::MockModule->new('Email::Stuffer');
36 $email_sender_module->mock(
37     'send_or_die',
38     sub {
39         ( $email_object, $sendmail_params ) = @_;
40         my $str = $email_object->email->as_string;
41         unlike $str, qr/I =C3=A2=C2=99=C2=A5 Koha=/, "Content is not double encoded";
42         $send_or_die_count++;
43         warn "Fake send_or_die";
44     }
45 );
46
47 use_ok('C4::Context');
48 use_ok('C4::Members');
49 use_ok('C4::Acquisition', qw( NewBasket ));
50 use_ok('C4::Biblio', qw( AddBiblio GetBiblioData ));
51 use_ok('C4::Letters', qw( GetMessageTransportTypes GetMessage EnqueueLetter GetQueuedMessages SendQueuedMessages ResendMessage GetLetters GetPreparedLetter SendAlerts ));
52 use t::lib::Mocks;
53 use t::lib::TestBuilder;
54 use Koha::Database;
55 use Koha::DateUtils qw( dt_from_string output_pref );
56 use Koha::Acquisition::Booksellers;
57 use Koha::Acquisition::Bookseller::Contacts;
58 use Koha::Acquisition::Orders;
59 use Koha::Libraries;
60 use Koha::Notice::Messages;
61 use Koha::Notice::Templates;
62 use Koha::Notice::Util;
63 use Koha::Patrons;
64 use Koha::Subscriptions;
65
66 my $schema = Koha::Database->schema;
67 $schema->storage->txn_begin();
68
69 my $builder = t::lib::TestBuilder->new;
70 my $dbh = C4::Context->dbh;
71
72 $dbh->do(q|DELETE FROM letter|);
73 $dbh->do(q|DELETE FROM message_queue|);
74 $dbh->do(q|DELETE FROM message_transport_types|);
75
76 my $library = $builder->build({
77     source => 'Branch',
78     value  => {
79         branchemail      => 'branchemail@address.com',
80         branchreplyto    => 'branchreplyto@address.com',
81         branchreturnpath => 'branchreturnpath@address.com',
82     }
83 });
84 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
85 my $date = dt_from_string;
86 my $borrowernumber = Koha::Patron->new({
87     firstname    => 'Jane',
88     surname      => 'Smith',
89     categorycode => $patron_category,
90     branchcode   => $library->{branchcode},
91     dateofbirth  => $date,
92     smsalertnumber => undef,
93 })->store->borrowernumber;
94
95 my $marc_record = MARC::Record->new;
96 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
97
98
99
100 # GetMessageTransportTypes
101 my $mtts = C4::Letters::GetMessageTransportTypes();
102 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
103
104 $dbh->do(q|
105     INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
106 |);
107 $mtts = C4::Letters::GetMessageTransportTypes();
108 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
109
110
111 # EnqueueLetter
112 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
113
114 my $my_message = {
115     borrowernumber         => $borrowernumber,
116     message_transport_type => 'sms',
117     to_address             => undef,
118     from_address           => 'from@example.com',
119 };
120 my $message_id = C4::Letters::EnqueueLetter($my_message);
121 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
122
123 delete $my_message->{message_transport_type};
124 $my_message->{letter} = {
125     content      => 'I ♥ Koha',
126     title        => '啤酒 is great',
127     metadata     => 'metadata',
128     code         => 'TEST_MESSAGE',
129     content_type => 'text/plain',
130 };
131
132 $message_id = C4::Letters::EnqueueLetter($my_message);
133 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
134
135 $my_message->{message_transport_type} = 'sms';
136 $message_id = C4::Letters::EnqueueLetter($my_message);
137 ok(defined $message_id && $message_id > 0, 'new message successfully queued');
138
139
140 # GetQueuedMessages
141 my $messages = C4::Letters::GetQueuedMessages();
142 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
143
144 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
145 is( @$messages, 1, 'one message stored for the borrower' );
146 is( $messages->[0]->{message_id}, $message_id, 'EnqueueLetter returns the message id correctly' );
147 is( $messages->[0]->{borrowernumber}, $borrowernumber, 'EnqueueLetter stores the borrower number correctly' );
148 is( $messages->[0]->{subject}, $my_message->{letter}->{title}, 'EnqueueLetter stores the subject correctly' );
149 is( $messages->[0]->{content}, $my_message->{letter}->{content}, 'EnqueueLetter stores the content correctly' );
150 is( $messages->[0]->{message_transport_type}, $my_message->{message_transport_type}, 'EnqueueLetter stores the message type correctly' );
151 is( $messages->[0]->{status}, 'pending', 'EnqueueLetter stores the status pending correctly' );
152 isnt( $messages->[0]->{time_queued}, undef, 'Time queued inserted by default in message_queue table' );
153 is( $messages->[0]->{updated_on}, $messages->[0]->{time_queued}, 'Time status changed equals time queued when created in message_queue table' );
154 is( $messages->[0]->{failure_code}, '', 'Failure code for successful message correctly empty');
155
156 # Setting time_queued to something else than now
157 my $yesterday = dt_from_string->subtract( days => 1 );
158 Koha::Notice::Messages->find($messages->[0]->{message_id})->time_queued($yesterday)->store;
159
160 # SendQueuedMessages
161 my $messages_processed = C4::Letters::SendQueuedMessages( { type => 'email' });
162 is($messages_processed, 0, 'No queued messages processed if type limit passed with unused type');
163 $messages_processed = C4::Letters::SendQueuedMessages( { type => 'sms' });
164 is($messages_processed, 0, 'All queued messages processed, nothing sent');
165 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
166 is(
167     $messages->[0]->{status},
168     'failed',
169     'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
170 );
171 is(
172     $messages->[0]->{failure_code},
173     'MISSING_SMS',
174     'Correct failure code set for borrower with no smsalertnumber set'
175 );
176 isnt($messages->[0]->{updated_on}, $messages->[0]->{time_queued}, 'Time status changed differs from time queued when status changes' );
177 is(dt_from_string($messages->[0]->{time_queued}), $yesterday, 'Time queued remaines inmutable' );
178
179 # ResendMessage
180 my $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
181 my $message = C4::Letters::GetMessage( $messages->[0]->{message_id});
182 is( $resent, 1, 'The message should have been resent' );
183 is($message->{status},'pending', 'ResendMessage sets status to pending correctly (bug 12426)');
184 $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
185 is( $resent, 0, 'The message should not have been resent again' );
186 $resent = C4::Letters::ResendMessage();
187 is( $resent, undef, 'ResendMessage should return undef if not message_id given' );
188
189 # Delivery notes
190 is( $messages->[0]->{failure_code}, 'MISSING_SMS', 'Failure code set correctly for no smsalertnumber correctly set' );
191
192 # GetLetters
193 my $letters = C4::Letters::GetLetters();
194 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
195
196 my $title = q|<<branches.branchname>> - <<status>>|;
197 my $content = q{Dear <<borrowers.firstname>> <<borrowers.surname>>,
198 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
199
200 <<branches.branchname>>
201 <<branches.branchaddress1>>
202 URL: <<OPACBaseURL>>
203
204 The following item(s) is/are currently <<status>>:
205
206 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
207
208 Thank-you for your prompt attention to this matter.
209 Don't forget your date of birth: <<borrowers.dateofbirth>>.
210 Look at this wonderful biblio timestamp: <<biblio.timestamp>>.
211 };
212
213 $dbh->do( q|INSERT INTO letter(branchcode,module,code,name,is_html,title,content,message_transport_type) VALUES (?,'my module','my code','my name',1,?,?,'email')|, undef, $library->{branchcode}, $title, $content );
214 $letters = C4::Letters::GetLetters();
215 is( @$letters, 1, 'GetLetters returns the correct number of letters' );
216 is( $letters->[0]->{module}, 'my module', 'GetLetters gets the module correctly' );
217 is( $letters->[0]->{code}, 'my code', 'GetLetters gets the code correctly' );
218 is( $letters->[0]->{name}, 'my name', 'GetLetters gets the name correctly' );
219
220 # GetPreparedLetter
221 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
222 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
223
224 my $sms_content = 'This is a SMS for an <<status>>';
225 $dbh->do( q|INSERT INTO letter(branchcode,module,code,name,is_html,title,content,message_transport_type) VALUES (?,'my module','my code','my name',1,'my title',?,'sms')|, undef, $library->{branchcode}, $sms_content );
226
227 my $tables = {
228     borrowers => $borrowernumber,
229     branches => $library->{branchcode},
230     biblio => $biblionumber,
231 };
232 my $substitute = {
233     status => 'overdue',
234 };
235 my $repeat = [
236     {
237         itemcallnumber => 'my callnumber1',
238         barcode        => '1234',
239     },
240     {
241         itemcallnumber => 'my callnumber2',
242         barcode        => '5678',
243     },
244 ];
245 my $prepared_letter = GetPreparedLetter((
246     module      => 'my module',
247     branchcode  => $library->{branchcode},
248     letter_code => 'my code',
249     tables      => $tables,
250     substitute  => $substitute,
251     repeat      => $repeat,
252 ));
253 my $retrieved_library = Koha::Libraries->find($library->{branchcode});
254 my $my_title_letter = $retrieved_library->branchname . qq| - $substitute->{status}|;
255 my $biblio_timestamp = dt_from_string( GetBiblioData($biblionumber)->{timestamp} );
256 my $my_content_letter = qq|Dear Jane Smith,
257 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
258
259 |.$retrieved_library->branchname.qq|
260 |.$retrieved_library->branchaddress1.qq|
261 URL: http://thisisatest.com
262
263 The following item(s) is/are currently $substitute->{status}:
264
265 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
266 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
267
268 Thank-you for your prompt attention to this matter.
269 Don't forget your date of birth: | . output_pref({ dt => $date, dateonly => 1 }) . q|.
270 Look at this wonderful biblio timestamp: | . output_pref({ dt => $biblio_timestamp })  . ".\n";
271
272 is( $prepared_letter->{title}, $my_title_letter, 'GetPreparedLetter returns the title correctly' );
273 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
274
275 $prepared_letter = GetPreparedLetter((
276     module                 => 'my module',
277     branchcode             => $library->{branchcode},
278     letter_code            => 'my code',
279     tables                 => $tables,
280     substitute             => $substitute,
281     repeat                 => $repeat,
282     message_transport_type => 'sms',
283 ));
284 $my_content_letter = qq|This is a SMS for an $substitute->{status}|;
285 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
286
287 warning_is {
288     $prepared_letter = GetPreparedLetter((
289         module                 => 'my module',
290         branchcode             => $library->{branchcode},
291         letter_code            => 'my code',
292         tables                 => $tables,
293         substitute             => { status => undef },
294         repeat                 => $repeat,
295         message_transport_type => 'sms',
296     ));
297 }
298 undef, "No warning if GetPreparedLetter called with substitute containing undefined value";
299 is( $prepared_letter->{content}, q|This is a SMS for an |, 'GetPreparedLetter returns the content correctly when substitute contains undefined value' );
300
301 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test_date','TEST_DATE','Test dates','A title with a timestamp: <<biblio.timestamp>>','This one only contains the date: <<biblio.timestamp | dateonly>>.');});
302 $prepared_letter = GetPreparedLetter((
303     module                 => 'test_date',
304     branchcode             => '',
305     letter_code            => 'test_date',
306     tables                 => $tables,
307     substitute             => $substitute,
308     repeat                 => $repeat,
309 ));
310 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
311
312 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp | dateonly>>.' WHERE code = 'test_date';});
313 $prepared_letter = GetPreparedLetter((
314     module                 => 'test_date',
315     branchcode             => '',
316     letter_code            => 'test_date',
317     tables                 => $tables,
318     substitute             => $substitute,
319     repeat                 => $repeat,
320 ));
321 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
322
323 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp|dateonly >>.' WHERE code = 'test_date';});
324 $prepared_letter = GetPreparedLetter((
325     module                 => 'test_date',
326     branchcode             => '',
327     letter_code            => 'test_date',
328     tables                 => $tables,
329     substitute             => $substitute,
330     repeat                 => $repeat,
331 ));
332 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
333
334 t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
335 my $yesterday_night = $date->clone->add( days => -1 )->set_hour(22);
336 $dbh->do(q|UPDATE biblio SET timestamp = ? WHERE biblionumber = ?|, undef, $yesterday_night, $biblionumber );
337 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp>>.' WHERE code = 'test_date';});
338 $prepared_letter = GetPreparedLetter((
339     module                 => 'test_date',
340     branchcode             => '',
341     letter_code            => 'test_date',
342     tables                 => $tables,
343     substitute             => $substitute,
344     repeat                 => $repeat,
345 ));
346 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
347
348 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('claimacquisition','TESTACQCLAIM','Acquisition Claim','Item Not Received','<<aqbooksellers.name>>|<<aqcontacts.name>>|<order>Ordernumber <<aqorders.ordernumber>> (<<biblio.title>>) (<<aqorders.quantity>> ordered)</order>');});
349 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('orderacquisition','TESTACQORDER','Acquisition Order','Order','<<aqbooksellers.name>>|<<aqcontacts.name>>|<order>Ordernumber <<aqorders.ordernumber>> (<<biblio.title>>) (<<aqorders.quantity>> ordered)</order> Basket name: [% basket.basketname %]');});
350
351 my $testacqorder2_content = <<EOF;
352 [%- USE Price -%]
353 [% bookseller.name %]
354 [% FOREACH order IN orders %]
355 Ordernumber [% order.ordernumber %] [% order.quantity %] [% order.listprice | \$Price %]
356 [% END %]
357 EOF
358
359 $dbh->do("INSERT INTO letter (module, code, name, title, content) VALUES ('orderacquisition','TESTACQORDER2','Acquisition Order','Order','$testacqorder2_content');");
360
361 my $popito = $builder->build({
362     source => 'Aqbookseller',
363     value  => { name => 'Popito' }
364 });
365
366 my $order_1 = $builder->build({
367     source => 'Aqorder',
368     value  => {
369        quantity => 2,
370        listprice => '12.00'
371     }
372 });
373
374 my $order_2 = $builder->build({
375     source => 'Aqorder',
376     value  => {
377        quantity => 1,
378        listprice => '23.50'
379     }
380 });
381
382 $prepared_letter = GetPreparedLetter((
383     module                 => 'orderacquisition',
384     branchcode             => '',
385     letter_code            => 'TESTACQORDER2',
386     tables                 => { 'aqbooksellers' => $popito->{id} },
387     loops                  => {
388         aqorders => [ $order_1->{ordernumber}, $order_2->{ordernumber} ]
389     }
390 ));
391
392 my $testacqorder2_expected = qq|Popito
393
394 Ordernumber | . $order_1->{ordernumber} . qq| 2 12.00
395
396 Ordernumber | . $order_2->{ordernumber} . qq| 1 23.50
397
398 |;
399
400 is($prepared_letter->{content}, $testacqorder2_expected);
401
402 # Test that _parseletter doesn't modify its parameters bug 15429
403 {
404     my $values = { dateexpiry => '2015-12-13', };
405     C4::Letters::_parseletter($prepared_letter, 'borrowers', $values);
406     is( $values->{dateexpiry}, '2015-12-13', "_parseletter doesn't modify its parameters" );
407 }
408
409 # Correctly format dateexpiry
410 {
411     my $values = { dateexpiry => '2015-12-13', };
412
413     t::lib::Mocks::mock_preference('dateformat', 'metric');
414     t::lib::Mocks::mock_preference('timeformat', '24hr');
415     my $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
416     is( $letter->{content}, 'expiry on 13/12/2015' );
417
418     t::lib::Mocks::mock_preference('dateformat', 'metric');
419     t::lib::Mocks::mock_preference('timeformat', '12hr');
420     $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
421     is( $letter->{content}, 'expiry on 13/12/2015' );
422 }
423
424 my $bookseller = Koha::Acquisition::Bookseller->new(
425     {
426         name => "my vendor",
427         address1 => "bookseller's address",
428         phone => "0123456",
429         active => 1,
430         deliverytime => 5,
431     }
432 )->store;
433 my $booksellerid = $bookseller->id;
434
435 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith',  phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
436 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues      => 1, booksellerid => $booksellerid } )->store;
437 my $basketno = NewBasket($booksellerid, 1, 'The basket name');
438
439 my $budgetid = C4::Budgets::AddBudget({
440     budget_code => "budget_code_test_letters",
441     budget_name => "budget_name_test_letters",
442 });
443
444 my $bib = MARC::Record->new();
445 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
446     $bib->append_fields(
447         MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
448     );
449 } else {
450     $bib->append_fields(
451         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
452     );
453 }
454
455 my $logged_in_user = $builder->build_object(
456     {
457         class => 'Koha::Patrons',
458         value => {
459             branchcode => $library->{branchcode},
460             email      => 'some@email.com'
461         }
462     }
463 );
464
465 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
466
467 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
468 my $order = Koha::Acquisition::Order->new(
469     {
470         basketno => $basketno,
471         quantity => 1,
472         biblionumber => $biblionumber,
473         budget_id => $budgetid,
474     }
475 )->store;
476 my $ordernumber = $order->ordernumber;
477
478 Koha::Acquisition::Baskets->find( $basketno )->close;
479 my $err;
480 warning_like {
481     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
482     qr/^Bookseller .* without emails at/,
483     "SendAlerts prints a warning";
484 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
485
486 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
487 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
488
489 # Ensure that the preference 'ClaimsLog' is set to logging
490 t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
491
492 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
493 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
494
495 {
496 warning_like {
497     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
498     qr|Fake send_or_die|,
499     "SendAlerts is using the mocked send_or_die routine (orderacquisition)";
500 is($err, 1, "Successfully sent order.");
501 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent order");
502 is($email_object->email->body, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered) Basket name: The basket name', 'Order notice text constructed successfully');
503
504 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
505 $mocked_koha_email->mock( 'send_or_die', sub {
506     Email::Sender::Failure->throw('something went wrong');
507 });
508
509 warning_like {
510     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ); }
511     qr{something went wrong},
512     'Warning is printed';
513
514 is($err->{error}, 'something went wrong', "Send exception, error message returned");
515
516 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
517 warning_like {
518     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
519     qr/No orderacquisition TESTACQORDER letter transported by email/,
520     "GetPreparedLetter warns about missing notice template";
521 is($err->{'error'}, 'no_letter', "No TESTACQORDER letter was defined.");
522 }
523
524 {
525 warning_like {
526     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
527     qr|Fake send_or_die|,
528     "SendAlerts is using the mocked send_or_die routine";
529
530 is($err, 1, "Successfully sent claim");
531 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent claim");
532 is($email_object->email->body, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
533
534 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
535 $mocked_koha_email->mock( 'send_or_die', sub {
536     Email::Sender::Failure->throw('something went wrong');
537 });
538
539 warning_like {
540     $err = SendAlerts( 'claimacquisition', [ $ordernumber ] , 'TESTACQCLAIM' ); }
541     qr{something went wrong},
542     'Warning is printed';
543
544 is($err->{error}, 'something went wrong', "Send exception, error message returned");
545 }
546
547 {
548 use C4::Serials qw( NewSubscription GetSerials findSerialsByStatus ModSerialStatus );
549
550 my $notes = 'notes';
551 my $internalnotes = 'intnotes';
552 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
553 my $subscriptionid = NewSubscription(
554      undef,      "",     undef, undef, undef, $biblionumber,
555     '2013-01-01', 1, undef, undef,  undef,
556     undef,      undef,  undef, undef, undef, undef,
557     1,          $notes,undef, '2013-01-01', undef, 1,
558     undef,       undef,  0,    $internalnotes,  0,
559     undef, undef, 0,          undef,         '2013-12-31', 0
560 );
561 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('serial','RLIST','Serial issue notification','Serial issue notification','<<biblio.title>>,<<subscription.subscriptionid>>,<<serial.serialseq>>');});
562 my ($serials_count, @serials) = GetSerials($subscriptionid);
563 my $serial = $serials[0];
564
565 my $patron = Koha::Patron->new({
566     firstname    => 'John',
567     surname      => 'Smith',
568     categorycode => $patron_category,
569     branchcode   => $library->{branchcode},
570     dateofbirth  => $date,
571     email        => 'john.smith@test.de',
572 })->store;
573 my $borrowernumber = $patron->borrowernumber;
574 my $subscription = Koha::Subscriptions->find( $subscriptionid );
575 $subscription->add_subscriber( $patron );
576
577 t::lib::Mocks::mock_userenv({ branch => $library->{branchcode} });
578 my $err2;
579 warning_like {
580 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
581     qr|Fake send_or_die|,
582     "SendAlerts is using the mocked send_or_die routine";
583
584 is($err2, 1, "Successfully sent serial notification");
585 is($email_object->email->header('To'), 'john.smith@test.de', "mailto correct in sent serial notification");
586 is($email_object->email->body, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
587
588 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', 'robert.tables@mail.com' );
589
590 my $err3;
591 warning_like {
592 $err3 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
593     qr|Fake send_or_die|,
594     "SendAlerts is using the mocked send_or_die routine";
595 is($email_object->email->header('To'), 'robert.tables@mail.com', "mailto address overwritten by SendAllMailsTo preference");
596
597 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
598 $mocked_koha_email->mock( 'send_or_die', sub {
599     Email::Sender::Failure->throw('something went wrong');
600 });
601
602 warning_like {
603     $err = SendAlerts( 'issue', $serial->{serialid} , 'RLIST' ); }
604     qr{something went wrong},
605     'Warning is printed';
606
607 is($err->{error}, 'something went wrong', "Send exception, error message returned");
608
609 }
610 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
611
612 subtest 'SendAlerts - claimissue' => sub {
613     plan tests => 13;
614
615     use C4::Serials;
616
617     $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('claimissues','TESTSERIALCLAIM','Serial claim test','Serial claim test','<<serial.serialid>>|<<subscription.startdate>>|<<biblio.title>>|<<biblioitems.issn>>');});
618
619     my $bookseller = Koha::Acquisition::Bookseller->new(
620         {
621             name => "my vendor",
622             address1 => "bookseller's address",
623             phone => "0123456",
624             active => 1,
625             deliverytime => 5,
626         }
627     )->store;
628     my $booksellerid = $bookseller->id;
629
630     Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
631
632     my $bib = MARC::Record->new();
633     if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
634         $bib->append_fields(
635             MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
636             MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
637         );
638     } else {
639         $bib->append_fields(
640             MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
641             MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
642         );
643     }
644     my ($biblionumber) = AddBiblio($bib, '');
645
646     $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
647     my $subscriptionid = NewSubscription(
648          undef, "", $booksellerid, undef, undef, $biblionumber,
649         '2013-01-01', 1, undef, undef,  undef,
650         undef,  undef,  undef, undef, undef, undef,
651         1, 'public',undef, '2013-01-01', undef, 1,
652         undef, undef,  0, 'internal',  0,
653         undef, undef, 0,  undef, '2013-12-31', 0
654     );
655
656     my ($serials_count, @serials) = GetSerials($subscriptionid);
657     my  @serialids = ($serials[0]->{serialid});
658
659     my $err;
660     warning_like {
661         $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
662         qr/^Bookseller .* without emails at/,
663         "Warn on vendor without email address";
664
665     $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
666     $bookseller->contacts->next->email('testemail@mydomain.com')->store;
667
668     # Ensure that the preference 'ClaimsLog' is set to logging
669     t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
670
671     # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
672     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
673
674     t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
675
676     {
677     warning_like {
678         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
679         qr|Fake send_or_die|,
680         "SendAlerts is using the mocked send_or_die routine (claimissues)";
681     is( $err, 1, "Successfully sent claim" );
682     is( $email_object->email->header('To'),
683         'testemail@mydomain.com', "mailto correct in sent claim" );
684     is(
685         $email_object->email->body,
686         "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy",
687         'Serial claim letter for 1 issue constructed successfully'
688     );
689
690     my $mocked_koha_email = Test::MockModule->new('Koha::Email');
691     $mocked_koha_email->mock( 'send_or_die', sub {
692             Email::Sender::Failure->throw('something went wrong');
693     });
694
695     warning_like {
696         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ); }
697         qr{something went wrong},
698         'Warning is printed';
699
700     is($err->{error}, 'something went wrong', "Send exception, error message returned");
701     }
702
703     {
704     my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
705     my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
706     ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
707     ($serials_count, @serials) = GetSerials($subscriptionid);
708     push @serialids, ($serials[1]->{serialid});
709
710     warning_like { $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ); }
711         qr|Fake send_or_die|,
712         "SendAlerts is using the mocked send_or_die routine (claimissues)";
713
714     is(
715         $email_object->email->body,
716         "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy"
717           . $email_object->email->crlf
718           . "$serialids[1]|2013-01-01|Silence in the library|xxxx-yyyy",
719         "Serial claim letter for 2 issues constructed successfully"
720     );
721
722     $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
723     warning_like {
724         $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
725         qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
726         "GetPreparedLetter warns about missing notice template";
727     is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
728     }
729
730 };
731
732 subtest 'GetPreparedLetter' => sub {
733     plan tests => 4;
734
735     Koha::Notice::Template->new(
736         {
737             module                 => 'test',
738             code                   => 'test',
739             branchcode             => '',
740             message_transport_type => 'email'
741         }
742     )->store;
743     my $letter;
744     warning_like {
745         $letter = C4::Letters::GetPreparedLetter(
746             module      => 'test',
747             letter_code => 'test',
748         );
749     }
750     qr{^ERROR: nothing to substitute},
751 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
752     is( $letter, undef,
753 'No letter should be returned by GetPreparedLetter if something went wrong'
754     );
755
756     warning_like {
757         $letter = C4::Letters::GetPreparedLetter(
758             module      => 'test',
759             letter_code => 'test',
760             substitute  => {}
761         );
762     }
763     qr{^ERROR: nothing to substitute},
764 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
765     is( $letter, undef,
766 'No letter should be returned by GetPreparedLetter if something went wrong'
767     );
768
769 };
770
771
772
773 subtest 'TranslateNotices' => sub {
774     plan tests => 4;
775
776     t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
777
778     $dbh->do(
779         q|
780         INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
781         ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
782         ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
783     | );
784     my $substitute = {};
785     my $letter = C4::Letters::GetPreparedLetter(
786             module                 => 'test',
787             tables                 => $tables,
788             letter_code            => 'code',
789             message_transport_type => 'email',
790             substitute             => $substitute,
791     );
792     is(
793         $letter->{title},
794         'a test',
795         'GetPreparedLetter should return the default one if the lang parameter is not provided'
796     );
797
798     $letter = C4::Letters::GetPreparedLetter(
799             module                 => 'test',
800             tables                 => $tables,
801             letter_code            => 'code',
802             message_transport_type => 'email',
803             substitute             => $substitute,
804             lang                   => 'es-ES',
805     );
806     is( $letter->{title}, 'una prueba',
807         'GetPreparedLetter should return the required notice if it exists' );
808
809     $letter = C4::Letters::GetPreparedLetter(
810             module                 => 'test',
811             tables                 => $tables,
812             letter_code            => 'code',
813             message_transport_type => 'email',
814             substitute             => $substitute,
815             lang                   => 'fr-FR',
816     );
817     is(
818         $letter->{title},
819         'a test',
820         'GetPreparedLetter should return the default notice if the one required does not exist'
821     );
822
823     t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
824
825     $letter = C4::Letters::GetPreparedLetter(
826             module                 => 'test',
827             tables                 => $tables,
828             letter_code            => 'code',
829             message_transport_type => 'email',
830             substitute             => $substitute,
831             lang                   => 'es-ES',
832     );
833     is( $letter->{title}, 'a test',
834         'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
835
836 };
837
838 subtest 'Test SMS handling in SendQueuedMessages' => sub {
839
840     plan tests => 14;
841
842     t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
843     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
844
845     my $patron = Koha::Patrons->find($borrowernumber);
846     $dbh->do(q|
847         INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
848         VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
849         |, undef, $borrowernumber
850     );
851     eval { C4::Letters::SendQueuedMessages(); };
852     is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
853
854     my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
855     $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
856     $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
857
858     warning_like { C4::Letters::SendQueuedMessages(); }
859         qr|Fake send_or_die|,
860         "SendAlerts is using the mocked send_or_die routine (claimissues)";
861
862     my $message = $schema->resultset('MessageQueue')->search({
863         borrowernumber => $borrowernumber,
864         status => 'sent'
865     })->next();
866
867     is( $message->letter_id, $messages->[0]->{id}, "Message letter_id is set correctly" );
868     is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
869     is(
870         $message->from_address(),
871         'from@example.com',
872         'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
873     );
874
875     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
876
877     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
878
879     $message_id = C4::Letters::EnqueueLetter($my_message);
880     warning_like { C4::Letters::SendQueuedMessages(); }
881         qr|Fake send_or_die|,
882         "SendAlerts is using the mocked send_or_die routine (claimissues)";
883
884     $message = $schema->resultset('MessageQueue')->search({
885         borrowernumber => $borrowernumber,
886         status => 'sent'
887     })->next();
888
889     is(
890         $message->from_address(),
891         'override@example.com',
892         'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
893     );
894
895     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
896     $my_message->{to_address} = 'fixme@kidclamp.iswrong';
897     $message_id = C4::Letters::EnqueueLetter($my_message);
898
899     my $number_attempted = C4::Letters::SendQueuedMessages({
900         borrowernumber => -1, # -1 still triggers the borrowernumber condition
901         letter_code    => 'PASSWORD_RESET',
902     });
903     is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
904
905     warning_like { C4::Letters::SendQueuedMessages(); }
906         qr|Fake send_or_die|,
907         "SendAlerts is using the mocked send_or_die routine (claimissues)";
908
909     my $sms_message_address = $schema->resultset('MessageQueue')->search({
910         borrowernumber => $borrowernumber,
911         status => 'sent'
912     })->next()->to_address();
913     is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
914
915     # Test using SMS::Send::Test driver that's bundled with SMS::Send
916     t::lib::Mocks::mock_preference('SMSSendDriver', "AU::Test");
917
918     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
919     C4::Letters::EnqueueLetter($my_message);
920     C4::Letters::SendQueuedMessages();
921
922     $sms_message_address = $schema->resultset('MessageQueue')->search({
923         borrowernumber => $borrowernumber,
924         status => 'sent'
925     })->next()->to_address();
926     is( $sms_message_address, '5555555555', 'SendQueuedMessages populates the to address correctly for SMS by SMS::Send driver to smsalertnumber when to_address is set incorrectly' );
927
928 };
929
930 subtest 'get_item_content' => sub {
931     plan tests => 2;
932
933     t::lib::Mocks::mock_preference('dateformat', 'metric');
934     t::lib::Mocks::mock_preference('timeformat', '24hr');
935     my @items = (
936         {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
937         {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
938     );
939     my @item_content_fields = qw( date_due title barcode author itemnumber );
940
941     my $items_content;
942     for my $item ( @items ) {
943         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
944     }
945
946     my $expected_items_content = <<EOF;
947 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
948 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
949 EOF
950     is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
951
952
953     $items_content = q||;
954     for my $item ( @items ) {
955         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
956     }
957
958     $expected_items_content = <<EOF;
959 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
960 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
961 EOF
962     is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
963 };
964
965 subtest 'Test where parameter for SendQueuedMessages' => sub {
966     plan tests => 3;
967
968     my $dbh = C4::Context->dbh;
969
970     my $borrowernumber = Koha::Patron->new({
971         firstname    => 'Jane',
972         surname      => 'Smith',
973         categorycode => $patron_category,
974         branchcode   => $library->{branchcode},
975         dateofbirth  => $date,
976     })->store->borrowernumber;
977
978     $dbh->do(q|DELETE FROM message_queue|);
979     $my_message = {
980         'letter' => {
981             'content'      => 'a message',
982             'metadata'     => 'metadata',
983             'code'         => 'TEST_MESSAGE',
984             'content_type' => 'text/plain',
985             'title'        => 'message title'
986         },
987         'borrowernumber'         => $borrowernumber,
988         'to_address'             => undef,
989         'message_transport_type' => 'sms',
990         'from_address'           => 'from@example.com'
991     };
992     my $my_message2 = {
993         'letter' => {
994             'content'      => 'another message',
995             'metadata'     => 'metadata',
996             'code'         => 'TEST_MESSAGE',
997             'content_type' => 'text/plain',
998             'title'        => 'message title'
999         },
1000         'borrowernumber'         => $borrowernumber,
1001         'to_address'             => undef,
1002         'message_transport_type' => 'sms',
1003         'from_address'           => 'from@example.com'
1004     };
1005     my $my_message3 = {
1006         'letter' => {
1007             'content'      => 'a skipped message',
1008             'metadata'     => 'metadata',
1009             'code'         => 'TEST_MESSAGE',
1010             'content_type' => 'text/plain',
1011             'title'        => 'message title'
1012         },
1013         'borrowernumber'         => $borrowernumber,
1014         'to_address'             => undef,
1015         'message_transport_type' => 'sms',
1016         'from_address'           => 'from@example.com'
1017     };
1018     my @id = ( C4::Letters::EnqueueLetter($my_message),
1019         C4::Letters::EnqueueLetter($my_message2),
1020         C4::Letters::EnqueueLetter($my_message3),
1021     );
1022     C4::Letters::SendQueuedMessages({
1023         # Test scalar/arrayref in parameter too
1024         letter_code => [ 'TEST_MESSAGE', 'SOMETHING_NOT_THERE' ],
1025         type => 'sms',
1026         where => q{content NOT LIKE '%skipped%'},
1027     });
1028     is( Koha::Notice::Messages->find( $id[0] )->status, 'failed', 'Processed but failed' );
1029     is( Koha::Notice::Messages->find( $id[1] )->status, 'failed', 'Processed but failed' );
1030     is( Koha::Notice::Messages->find( $id[2] )->status, 'pending', 'Skipped, still pending' );
1031 };
1032
1033 subtest 'Test limit parameter for SendQueuedMessages' => sub {
1034     plan tests => 18;
1035
1036     my $dbh = C4::Context->dbh;
1037
1038     my $borrowernumber = Koha::Patron->new({
1039         firstname    => 'Jane',
1040         surname      => 'Smith',
1041         categorycode => $patron_category,
1042         branchcode   => $library->{branchcode},
1043         dateofbirth  => $date,
1044         email          => 'shouldnotwork@wrong.net',
1045     })->store->borrowernumber;
1046
1047     $dbh->do(q|DELETE FROM message_queue|);
1048     $my_message = {
1049         'letter' => {
1050             'content'      => 'a message',
1051             'metadata'     => 'metadata',
1052             'code'         => 'TEST_MESSAGE',
1053             'content_type' => 'text/plain',
1054             'title'        => 'message title'
1055         },
1056         'borrowernumber'         => $borrowernumber,
1057         'to_address'             => undef,
1058         'message_transport_type' => 'email',
1059         'from_address'           => 'from@example.com'
1060     };
1061     C4::Letters::EnqueueLetter($my_message) for 1..5;
1062
1063     $send_or_die_count = 0; # reset
1064     my $messages_sent;
1065     my $regex = qr|Fake send_or_die|;
1066     warning_like {
1067         $messages_sent = C4::Letters::SendQueuedMessages( { limit => 1 } ) }
1068         $regex,
1069         "SendQueuedMessages with limit 1";
1070     is( $messages_sent, 1,
1071         'Sent 1 message with limit of 1 and 5 unprocessed messages' );
1072
1073     warnings_like {
1074         $messages_sent = C4::Letters::SendQueuedMessages( { limit => 2 } ) }
1075         [ map { $regex } 1..2 ],
1076         "SendQueuedMessages with limit 2";
1077     is( $messages_sent, 2,
1078         'Sent 2 messages with limit of 2 and 4 unprocessed messages' );
1079
1080     warnings_like {
1081         $messages_sent = C4::Letters::SendQueuedMessages( { limit => 3 } ) }
1082         [ map { $regex } 1..2 ],
1083         "SendQueuedMessages with limit 3";
1084     is( $messages_sent, 2,
1085         'Sent 2 messages with limit of 3 and 2 unprocessed messages' );
1086
1087     is( $send_or_die_count, 5, '5 messages sent' );
1088     # Mimic correct status in queue for next tests
1089     Koha::Notice::Messages->search({ to_address => { 'LIKE', '%wrong.net' }})->update({ status => 'sent' });
1090
1091     # Now add a few domain limits too, sending 2 more mails to wrongnet, 2 to fake1, 2 to fake2
1092     # Since we already sent 5 to wrong.net, we expect one deferral when limiting to 6
1093     # Similarly we arrange for 1 deferral for fake1, and 2 for fake2
1094     # We set therefore limit to 3 sent messages: we expect 2 sent, 4 deferred (so 6 processed)
1095     t::lib::Mocks::mock_config( 'message_domain_limits', { domain => [
1096         { name => 'wrong.net',    limit => 6, unit => '1m' },
1097         { name => 'fake1.domain', limit => 1, unit => '1m' },
1098         { name => 'fake2.domain', limit => 0, unit => '1m' },
1099     ]});
1100     C4::Letters::EnqueueLetter($my_message) for 1..2;
1101     $my_message->{to_address} = 'someone@fake1.domain';
1102     C4::Letters::EnqueueLetter($my_message) for 1..2;
1103     $my_message->{to_address} = 'another@fake2.domain';
1104     C4::Letters::EnqueueLetter($my_message) for 1..2;
1105     my $mocked_util = Test::MockModule->new('Koha::Notice::Util');
1106     my $count_exceeds_calls = 0;
1107     $mocked_util->mock( 'exceeds_limit', sub {
1108         $count_exceeds_calls++;
1109         $mocked_util->original('exceeds_limit')->(@_);
1110     });
1111     warnings_like {
1112         $messages_sent = C4::Letters::SendQueuedMessages({ limit => 3 }) }
1113         [ qr/wrong.net reached limit/, $regex, qr/fake1.domain reached limit/, $regex ],
1114         "SendQueuedMessages with limit 2 and domain limits";
1115     is( $messages_sent, 2, 'Only expecting 2 sent messages' );
1116     is(  Koha::Notice::Messages->search({ status => 'pending' })->count, 4, 'Still 4 pending' );
1117     is( $count_exceeds_calls, 6, 'We saw 6 messages while checking domain limits: so we deferred 4' );
1118 };
1119
1120 subtest 'Test message_id parameter for SendQueuedMessages' => sub {
1121
1122     plan tests => 8;
1123
1124     my $dbh = C4::Context->dbh;
1125
1126     my $borrowernumber = Koha::Patron->new({
1127         firstname    => 'Jane',
1128         surname      => 'Smith',
1129         categorycode => $patron_category,
1130         branchcode   => $library->{branchcode},
1131         dateofbirth  => $date,
1132         smsalertnumber => undef,
1133     })->store->borrowernumber;
1134
1135     $dbh->do(q|DELETE FROM message_queue|);
1136     $my_message = {
1137         'letter' => {
1138             'content'      => 'a message',
1139             'metadata'     => 'metadata',
1140             'code'         => 'TEST_MESSAGE',
1141             'content_type' => 'text/plain',
1142             'title'        => 'message title'
1143         },
1144         'borrowernumber'         => $borrowernumber,
1145         'to_address'             => 'to@example.org',
1146         'message_transport_type' => 'email',
1147         'from_address'           => '@example.com' # invalid from_address
1148     };
1149     my $message_id = C4::Letters::EnqueueLetter($my_message);
1150     $send_or_die_count = 0; # reset
1151     my $processed = C4::Letters::SendQueuedMessages( { message_id => $message_id } );
1152     is( $send_or_die_count, 0, 'Nothing sent when one message_id passed' );
1153     my $message_1 = C4::Letters::GetMessage($message_id);
1154     is( $message_1->{status}, 'failed', 'Invalid from_address => status failed' );
1155     is( $message_1->{failure_code}, 'INVALID_EMAIL:from', 'Failure code set correctly for invalid email parameter');
1156
1157     $my_message->{from_address} = 'root@example.org'; # valid from_address
1158     $message_id = C4::Letters::EnqueueLetter($my_message);
1159     warning_like { C4::Letters::SendQueuedMessages( { message_id => $message_id } ); }
1160         qr|Fake send_or_die|,
1161         "SendQueuedMessages is using the mocked send_or_die routine";
1162     is( $send_or_die_count, 1, 'One message passed through' );
1163     $message_1 = C4::Letters::GetMessage($message_1->{message_id});
1164     my $message_2 = C4::Letters::GetMessage($message_id);
1165     is( $message_1->{status}, 'failed', 'Message 1 status is unchanged' );
1166     is( $message_2->{status}, 'sent', 'Valid from_address => status sent' );
1167 };