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