Bug 33607: Handle default framework
[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 my $number_pattern = $builder->build_object(
553     {
554         class => 'Koha::Subscription::Numberpatterns',
555         value => { numberingmethod => 'No. {X}' }
556     }
557 );
558 my $subscriptionid = NewSubscription(
559      undef,      "",     undef, undef, undef, $biblionumber,
560     '2013-01-01', 1, undef, undef,  undef,
561     undef,      undef,  undef, undef, undef, undef,
562     1,          $notes,undef, '2013-01-01', undef, $number_pattern->id,
563     undef,       undef,  0,    $internalnotes,  0,
564     undef, undef, 0,          undef,         '2013-12-31', 0
565 );
566 $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>>');});
567 my ($serials_count, @serials) = GetSerials($subscriptionid);
568 my $serial = $serials[0];
569
570 my $patron = Koha::Patron->new({
571     firstname    => 'John',
572     surname      => 'Smith',
573     categorycode => $patron_category,
574     branchcode   => $library->{branchcode},
575     dateofbirth  => $date,
576     email        => 'john.smith@test.de',
577 })->store;
578 my $borrowernumber = $patron->borrowernumber;
579 my $subscription = Koha::Subscriptions->find( $subscriptionid );
580 $subscription->add_subscriber( $patron );
581
582 t::lib::Mocks::mock_userenv({ branch => $library->{branchcode} });
583 my $err2;
584 warning_like {
585 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
586     qr|Fake send_or_die|,
587     "SendAlerts is using the mocked send_or_die routine";
588
589 is($err2, 1, "Successfully sent serial notification");
590 is($email_object->email->header('To'), 'john.smith@test.de', "mailto correct in sent serial notification");
591 is($email_object->email->body, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
592
593 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', 'robert.tables@mail.com' );
594
595 my $err3;
596 warning_like {
597 $err3 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
598     qr|Fake send_or_die|,
599     "SendAlerts is using the mocked send_or_die routine";
600 is($email_object->email->header('To'), 'robert.tables@mail.com', "mailto address overwritten by SendAllMailsTo preference");
601
602 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
603 $mocked_koha_email->mock( 'send_or_die', sub {
604     Email::Sender::Failure->throw('something went wrong');
605 });
606
607 warning_like {
608     $err = SendAlerts( 'issue', $serial->{serialid} , 'RLIST' ); }
609     qr{something went wrong},
610     'Warning is printed';
611
612 is($err->{error}, 'something went wrong', "Send exception, error message returned");
613
614 }
615 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
616
617 subtest 'SendAlerts - claimissue' => sub {
618     plan tests => 13;
619
620     use C4::Serials;
621
622     $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>>');});
623
624     my $bookseller = Koha::Acquisition::Bookseller->new(
625         {
626             name => "my vendor",
627             address1 => "bookseller's address",
628             phone => "0123456",
629             active => 1,
630             deliverytime => 5,
631         }
632     )->store;
633     my $booksellerid = $bookseller->id;
634
635     Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
636
637     my $bib = MARC::Record->new();
638     if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
639         $bib->append_fields(
640             MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
641             MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
642         );
643     } else {
644         $bib->append_fields(
645             MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
646             MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
647         );
648     }
649     my ($biblionumber) = AddBiblio($bib, '');
650
651     my $number_pattern = $builder->build_object(
652         {
653             class => 'Koha::Subscription::Numberpatterns',
654             value => { numberingmethod => 'No. {X}' }
655         }
656     );
657
658     my $subscriptionid = NewSubscription(
659          undef, "", $booksellerid, undef, undef, $biblionumber,
660         '2013-01-01', 1, undef, undef,  undef,
661         undef,  undef,  undef, undef, undef, undef,
662         1, 'public',undef, '2013-01-01', undef, $number_pattern->id,
663         undef, undef,  0, 'internal',  0,
664         undef, undef, 0,  undef, '2013-12-31', 0
665     );
666
667     my ($serials_count, @serials) = GetSerials($subscriptionid);
668     my  @serialids = ($serials[0]->{serialid});
669
670     my $err;
671     warning_like {
672         $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
673         qr/^Bookseller .* without emails at/,
674         "Warn on vendor without email address";
675
676     $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
677     $bookseller->contacts->next->email('testemail@mydomain.com')->store;
678
679     # Ensure that the preference 'ClaimsLog' is set to logging
680     t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
681
682     # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
683     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
684
685     t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
686
687     {
688     warning_like {
689         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
690         qr|Fake send_or_die|,
691         "SendAlerts is using the mocked send_or_die routine (claimissues)";
692     is( $err, 1, "Successfully sent claim" );
693     is( $email_object->email->header('To'),
694         'testemail@mydomain.com', "mailto correct in sent claim" );
695     is(
696         $email_object->email->body,
697         "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy",
698         'Serial claim letter for 1 issue constructed successfully'
699     );
700
701     my $mocked_koha_email = Test::MockModule->new('Koha::Email');
702     $mocked_koha_email->mock( 'send_or_die', sub {
703             Email::Sender::Failure->throw('something went wrong');
704     });
705
706     warning_like {
707         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ); }
708         qr{something went wrong},
709         'Warning is printed';
710
711     is($err->{error}, 'something went wrong', "Send exception, error message returned");
712     }
713
714     {
715     my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
716     my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
717     ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
718     ($serials_count, @serials) = GetSerials($subscriptionid);
719     push @serialids, ($serials[1]->{serialid});
720
721     warning_like { $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ); }
722         qr|Fake send_or_die|,
723         "SendAlerts is using the mocked send_or_die routine (claimissues)";
724
725     is(
726         $email_object->email->body,
727         "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy"
728           . $email_object->email->crlf
729           . "$serialids[1]|2013-01-01|Silence in the library|xxxx-yyyy",
730         "Serial claim letter for 2 issues constructed successfully"
731     );
732
733     $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
734     warning_like {
735         $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
736         qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
737         "GetPreparedLetter warns about missing notice template";
738     is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
739     }
740
741 };
742
743 subtest 'GetPreparedLetter' => sub {
744     plan tests => 4;
745
746     Koha::Notice::Template->new(
747         {
748             module                 => 'test',
749             code                   => 'test',
750             branchcode             => '',
751             message_transport_type => 'email'
752         }
753     )->store;
754     my $letter;
755     warning_like {
756         $letter = C4::Letters::GetPreparedLetter(
757             module      => 'test',
758             letter_code => 'test',
759         );
760     }
761     qr{^ERROR: nothing to substitute},
762 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
763     is( $letter, undef,
764 'No letter should be returned by GetPreparedLetter if something went wrong'
765     );
766
767     warning_like {
768         $letter = C4::Letters::GetPreparedLetter(
769             module      => 'test',
770             letter_code => 'test',
771             substitute  => {}
772         );
773     }
774     qr{^ERROR: nothing to substitute},
775 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
776     is( $letter, undef,
777 'No letter should be returned by GetPreparedLetter if something went wrong'
778     );
779
780 };
781
782
783
784 subtest 'TranslateNotices' => sub {
785     plan tests => 4;
786
787     t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
788
789     $dbh->do(
790         q|
791         INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
792         ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
793         ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
794     | );
795     my $substitute = {};
796     my $letter = C4::Letters::GetPreparedLetter(
797             module                 => 'test',
798             tables                 => $tables,
799             letter_code            => 'code',
800             message_transport_type => 'email',
801             substitute             => $substitute,
802     );
803     is(
804         $letter->{title},
805         'a test',
806         'GetPreparedLetter should return the default one if the lang parameter is not provided'
807     );
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                   => 'es-ES',
816     );
817     is( $letter->{title}, 'una prueba',
818         'GetPreparedLetter should return the required notice if it exists' );
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                   => 'fr-FR',
827     );
828     is(
829         $letter->{title},
830         'a test',
831         'GetPreparedLetter should return the default notice if the one required does not exist'
832     );
833
834     t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
835
836     $letter = C4::Letters::GetPreparedLetter(
837             module                 => 'test',
838             tables                 => $tables,
839             letter_code            => 'code',
840             message_transport_type => 'email',
841             substitute             => $substitute,
842             lang                   => 'es-ES',
843     );
844     is( $letter->{title}, 'a test',
845         'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
846
847 };
848
849 subtest 'Test SMS handling in SendQueuedMessages' => sub {
850
851     plan tests => 14;
852
853     t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
854     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
855
856     my $patron = Koha::Patrons->find($borrowernumber);
857     $dbh->do(q|
858         INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
859         VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
860         |, undef, $borrowernumber
861     );
862     eval { C4::Letters::SendQueuedMessages(); };
863     is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
864
865     my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
866     $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
867     $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
868
869     warning_like { C4::Letters::SendQueuedMessages(); }
870         qr|Fake send_or_die|,
871         "SendAlerts is using the mocked send_or_die routine (claimissues)";
872
873     my $message = $schema->resultset('MessageQueue')->search({
874         borrowernumber => $borrowernumber,
875         status => 'sent'
876     })->next();
877
878     is( $message->letter_id, $messages->[0]->{id}, "Message letter_id is set correctly" );
879     is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
880     is(
881         $message->from_address(),
882         'from@example.com',
883         'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
884     );
885
886     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
887
888     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
889
890     $message_id = C4::Letters::EnqueueLetter($my_message);
891     warning_like { C4::Letters::SendQueuedMessages(); }
892         qr|Fake send_or_die|,
893         "SendAlerts is using the mocked send_or_die routine (claimissues)";
894
895     $message = $schema->resultset('MessageQueue')->search({
896         borrowernumber => $borrowernumber,
897         status => 'sent'
898     })->next();
899
900     is(
901         $message->from_address(),
902         'override@example.com',
903         'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
904     );
905
906     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
907     $my_message->{to_address} = 'fixme@kidclamp.iswrong';
908     $message_id = C4::Letters::EnqueueLetter($my_message);
909
910     my $number_attempted = C4::Letters::SendQueuedMessages({
911         borrowernumber => -1, # -1 still triggers the borrowernumber condition
912         letter_code    => 'PASSWORD_RESET',
913     });
914     is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
915
916     warning_like { C4::Letters::SendQueuedMessages(); }
917         qr|Fake send_or_die|,
918         "SendAlerts is using the mocked send_or_die routine (claimissues)";
919
920     my $sms_message_address = $schema->resultset('MessageQueue')->search({
921         borrowernumber => $borrowernumber,
922         status => 'sent'
923     })->next()->to_address();
924     is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
925
926     # Test using SMS::Send::Test driver that's bundled with SMS::Send
927     t::lib::Mocks::mock_preference('SMSSendDriver', "AU::Test");
928
929     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
930     C4::Letters::EnqueueLetter($my_message);
931     C4::Letters::SendQueuedMessages();
932
933     $sms_message_address = $schema->resultset('MessageQueue')->search({
934         borrowernumber => $borrowernumber,
935         status => 'sent'
936     })->next()->to_address();
937     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' );
938
939 };
940
941 subtest 'get_item_content' => sub {
942     plan tests => 2;
943
944     t::lib::Mocks::mock_preference('dateformat', 'metric');
945     t::lib::Mocks::mock_preference('timeformat', '24hr');
946     my @items = (
947         {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
948         {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
949     );
950     my @item_content_fields = qw( date_due title barcode author itemnumber );
951
952     my $items_content;
953     for my $item ( @items ) {
954         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
955     }
956
957     my $expected_items_content = <<EOF;
958 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
959 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
960 EOF
961     is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
962
963
964     $items_content = q||;
965     for my $item ( @items ) {
966         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
967     }
968
969     $expected_items_content = <<EOF;
970 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
971 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
972 EOF
973     is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
974 };
975
976 subtest 'Test where parameter for SendQueuedMessages' => sub {
977     plan tests => 3;
978
979     my $dbh = C4::Context->dbh;
980
981     my $borrowernumber = Koha::Patron->new({
982         firstname    => 'Jane',
983         surname      => 'Smith',
984         categorycode => $patron_category,
985         branchcode   => $library->{branchcode},
986         dateofbirth  => $date,
987     })->store->borrowernumber;
988
989     $dbh->do(q|DELETE FROM message_queue|);
990     $my_message = {
991         'letter' => {
992             'content'      => 'a message',
993             'metadata'     => 'metadata',
994             'code'         => 'TEST_MESSAGE',
995             'content_type' => 'text/plain',
996             'title'        => 'message title'
997         },
998         'borrowernumber'         => $borrowernumber,
999         'to_address'             => undef,
1000         'message_transport_type' => 'sms',
1001         'from_address'           => 'from@example.com'
1002     };
1003     my $my_message2 = {
1004         'letter' => {
1005             'content'      => 'another message',
1006             'metadata'     => 'metadata',
1007             'code'         => 'TEST_MESSAGE',
1008             'content_type' => 'text/plain',
1009             'title'        => 'message title'
1010         },
1011         'borrowernumber'         => $borrowernumber,
1012         'to_address'             => undef,
1013         'message_transport_type' => 'sms',
1014         'from_address'           => 'from@example.com'
1015     };
1016     my $my_message3 = {
1017         'letter' => {
1018             'content'      => 'a skipped message',
1019             'metadata'     => 'metadata',
1020             'code'         => 'TEST_MESSAGE',
1021             'content_type' => 'text/plain',
1022             'title'        => 'message title'
1023         },
1024         'borrowernumber'         => $borrowernumber,
1025         'to_address'             => undef,
1026         'message_transport_type' => 'sms',
1027         'from_address'           => 'from@example.com'
1028     };
1029     my @id = ( C4::Letters::EnqueueLetter($my_message),
1030         C4::Letters::EnqueueLetter($my_message2),
1031         C4::Letters::EnqueueLetter($my_message3),
1032     );
1033     C4::Letters::SendQueuedMessages({
1034         # Test scalar/arrayref in parameter too
1035         letter_code => [ 'TEST_MESSAGE', 'SOMETHING_NOT_THERE' ],
1036         type => 'sms',
1037         where => q{content NOT LIKE '%skipped%'},
1038     });
1039     is( Koha::Notice::Messages->find( $id[0] )->status, 'failed', 'Processed but failed' );
1040     is( Koha::Notice::Messages->find( $id[1] )->status, 'failed', 'Processed but failed' );
1041     is( Koha::Notice::Messages->find( $id[2] )->status, 'pending', 'Skipped, still pending' );
1042 };
1043
1044 subtest 'Test limit parameter for SendQueuedMessages' => sub {
1045     plan tests => 18;
1046
1047     my $dbh = C4::Context->dbh;
1048
1049     my $borrowernumber = Koha::Patron->new({
1050         firstname    => 'Jane',
1051         surname      => 'Smith',
1052         categorycode => $patron_category,
1053         branchcode   => $library->{branchcode},
1054         dateofbirth  => $date,
1055         email          => 'shouldnotwork@wrong.net',
1056     })->store->borrowernumber;
1057
1058     $dbh->do(q|DELETE FROM message_queue|);
1059     $my_message = {
1060         'letter' => {
1061             'content'      => 'a message',
1062             'metadata'     => 'metadata',
1063             'code'         => 'TEST_MESSAGE',
1064             'content_type' => 'text/plain',
1065             'title'        => 'message title'
1066         },
1067         'borrowernumber'         => $borrowernumber,
1068         'to_address'             => undef,
1069         'message_transport_type' => 'email',
1070         'from_address'           => 'from@example.com'
1071     };
1072     C4::Letters::EnqueueLetter($my_message) for 1..5;
1073
1074     $send_or_die_count = 0; # reset
1075     my $messages_sent;
1076     my $regex = qr|Fake send_or_die|;
1077     warning_like {
1078         $messages_sent = C4::Letters::SendQueuedMessages( { limit => 1 } ) }
1079         $regex,
1080         "SendQueuedMessages with limit 1";
1081     is( $messages_sent, 1,
1082         'Sent 1 message with limit of 1 and 5 unprocessed messages' );
1083
1084     warnings_like {
1085         $messages_sent = C4::Letters::SendQueuedMessages( { limit => 2 } ) }
1086         [ map { $regex } 1..2 ],
1087         "SendQueuedMessages with limit 2";
1088     is( $messages_sent, 2,
1089         'Sent 2 messages with limit of 2 and 4 unprocessed messages' );
1090
1091     warnings_like {
1092         $messages_sent = C4::Letters::SendQueuedMessages( { limit => 3 } ) }
1093         [ map { $regex } 1..2 ],
1094         "SendQueuedMessages with limit 3";
1095     is( $messages_sent, 2,
1096         'Sent 2 messages with limit of 3 and 2 unprocessed messages' );
1097
1098     is( $send_or_die_count, 5, '5 messages sent' );
1099     # Mimic correct status in queue for next tests
1100     Koha::Notice::Messages->search({ to_address => { 'LIKE', '%wrong.net' }})->update({ status => 'sent' });
1101
1102     # Now add a few domain limits too, sending 2 more mails to wrongnet, 2 to fake1, 2 to fake2
1103     # Since we already sent 5 to wrong.net, we expect one deferral when limiting to 6
1104     # Similarly we arrange for 1 deferral for fake1, and 2 for fake2
1105     # We set therefore limit to 3 sent messages: we expect 2 sent, 4 deferred (so 6 processed)
1106     t::lib::Mocks::mock_config( 'message_domain_limits', { domain => [
1107         { name => 'wrong.net',    limit => 6, unit => '1m' },
1108         { name => 'fake1.domain', limit => 1, unit => '1m' },
1109         { name => 'fake2.domain', limit => 0, unit => '1m' },
1110     ]});
1111     C4::Letters::EnqueueLetter($my_message) for 1..2;
1112     $my_message->{to_address} = 'someone@fake1.domain';
1113     C4::Letters::EnqueueLetter($my_message) for 1..2;
1114     $my_message->{to_address} = 'another@fake2.domain';
1115     C4::Letters::EnqueueLetter($my_message) for 1..2;
1116     my $mocked_util = Test::MockModule->new('Koha::Notice::Util');
1117     my $count_exceeds_calls = 0;
1118     $mocked_util->mock( 'exceeds_limit', sub {
1119         $count_exceeds_calls++;
1120         $mocked_util->original('exceeds_limit')->(@_);
1121     });
1122     warnings_like {
1123         $messages_sent = C4::Letters::SendQueuedMessages({ limit => 3 }) }
1124         [ qr/wrong.net reached limit/, $regex, qr/fake1.domain reached limit/, $regex ],
1125         "SendQueuedMessages with limit 2 and domain limits";
1126     is( $messages_sent, 2, 'Only expecting 2 sent messages' );
1127     is(  Koha::Notice::Messages->search({ status => 'pending' })->count, 4, 'Still 4 pending' );
1128     is( $count_exceeds_calls, 6, 'We saw 6 messages while checking domain limits: so we deferred 4' );
1129 };
1130
1131 subtest 'Test message_id parameter for SendQueuedMessages' => sub {
1132
1133     plan tests => 8;
1134
1135     my $dbh = C4::Context->dbh;
1136
1137     my $borrowernumber = Koha::Patron->new({
1138         firstname    => 'Jane',
1139         surname      => 'Smith',
1140         categorycode => $patron_category,
1141         branchcode   => $library->{branchcode},
1142         dateofbirth  => $date,
1143         smsalertnumber => undef,
1144     })->store->borrowernumber;
1145
1146     $dbh->do(q|DELETE FROM message_queue|);
1147     $my_message = {
1148         'letter' => {
1149             'content'      => 'a message',
1150             'metadata'     => 'metadata',
1151             'code'         => 'TEST_MESSAGE',
1152             'content_type' => 'text/plain',
1153             'title'        => 'message title'
1154         },
1155         'borrowernumber'         => $borrowernumber,
1156         'to_address'             => 'to@example.org',
1157         'message_transport_type' => 'email',
1158         'from_address'           => '@example.com' # invalid from_address
1159     };
1160     my $message_id = C4::Letters::EnqueueLetter($my_message);
1161     $send_or_die_count = 0; # reset
1162     my $processed = C4::Letters::SendQueuedMessages( { message_id => $message_id } );
1163     is( $send_or_die_count, 0, 'Nothing sent when one message_id passed' );
1164     my $message_1 = C4::Letters::GetMessage($message_id);
1165     is( $message_1->{status}, 'failed', 'Invalid from_address => status failed' );
1166     is( $message_1->{failure_code}, 'INVALID_EMAIL:from', 'Failure code set correctly for invalid email parameter');
1167
1168     $my_message->{from_address} = 'root@example.org'; # valid from_address
1169     $message_id = C4::Letters::EnqueueLetter($my_message);
1170     warning_like { C4::Letters::SendQueuedMessages( { message_id => $message_id } ); }
1171         qr|Fake send_or_die|,
1172         "SendQueuedMessages is using the mocked send_or_die routine";
1173     is( $send_or_die_count, 1, 'One message passed through' );
1174     $message_1 = C4::Letters::GetMessage($message_1->{message_id});
1175     my $message_2 = C4::Letters::GetMessage($message_id);
1176     is( $message_1->{status}, 'failed', 'Message 1 status is unchanged' );
1177     is( $message_2->{status}, 'sent', 'Valid from_address => status sent' );
1178 };