3 # This file is part of Koha.
5 # Copyright (C) 2013 Equinox Software, Inc.
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.
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.
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>.
21 use Test::More tests => 94;
26 use Email::Sender::Failure;
32 my ( $email_object, $sendmail_params );
33 our $send_or_die_count = 0;
35 my $email_sender_module = Test::MockModule->new('Email::Stuffer');
36 $email_sender_module->mock(
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";
43 warn "Fake send_or_die";
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 ));
53 use t::lib::TestBuilder;
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;
60 use Koha::Notice::Messages;
61 use Koha::Notice::Templates;
62 use Koha::Notice::Util;
64 use Koha::Subscriptions;
66 my $schema = Koha::Database->schema;
67 $schema->storage->txn_begin();
69 my $builder = t::lib::TestBuilder->new;
70 my $dbh = C4::Context->dbh;
72 $dbh->do(q|DELETE FROM letter|);
73 $dbh->do(q|DELETE FROM message_queue|);
74 $dbh->do(q|DELETE FROM message_transport_types|);
76 my $library = $builder->build({
79 branchemail => 'branchemail@address.com',
80 branchreplyto => 'branchreplyto@address.com',
81 branchreturnpath => 'branchreturnpath@address.com',
84 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
85 my $date = dt_from_string;
86 my $borrowernumber = Koha::Patron->new({
89 categorycode => $patron_category,
90 branchcode => $library->{branchcode},
92 smsalertnumber => undef,
93 })->store->borrowernumber;
95 my $marc_record = MARC::Record->new;
96 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
100 # GetMessageTransportTypes
101 my $mtts = C4::Letters::GetMessageTransportTypes();
102 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
105 INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
107 $mtts = C4::Letters::GetMessageTransportTypes();
108 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
112 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
115 borrowernumber => $borrowernumber,
116 message_transport_type => 'sms',
118 from_address => 'from@example.com',
120 my $message_id = C4::Letters::EnqueueLetter($my_message);
121 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
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',
132 $message_id = C4::Letters::EnqueueLetter($my_message);
133 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
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');
141 my $messages = C4::Letters::GetQueuedMessages();
142 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
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');
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;
163 C4::Letters::SendQueuedMessages( { message_id => undef } );
165 'Koha::Exceptions::BadParameter', 'Undef message_id throws an exception';
168 C4::Letters::SendQueuedMessages( { message_id => 0 } );
170 'Koha::Exceptions::BadParameter', 'message_id of 0 throws an exception';
173 C4::Letters::SendQueuedMessages( { message_id => q{} } );
175 'Koha::Exceptions::BadParameter', 'Empty string message_id throws an exception';
177 my $messages_processed = C4::Letters::SendQueuedMessages( { type => 'email' });
178 is($messages_processed, 0, 'No queued messages processed if type limit passed with unused type');
179 $messages_processed = C4::Letters::SendQueuedMessages( { type => 'sms' });
180 is($messages_processed, 0, 'All queued messages processed, nothing sent');
181 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
183 $messages->[0]->{status},
185 'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
188 $messages->[0]->{failure_code},
190 'Correct failure code set for borrower with no smsalertnumber set'
192 isnt($messages->[0]->{updated_on}, $messages->[0]->{time_queued}, 'Time status changed differs from time queued when status changes' );
193 is(dt_from_string($messages->[0]->{time_queued}), $yesterday, 'Time queued remaines inmutable' );
196 my $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
197 my $message = C4::Letters::GetMessage( $messages->[0]->{message_id});
198 is( $resent, 1, 'The message should have been resent' );
199 is($message->{status},'pending', 'ResendMessage sets status to pending correctly (bug 12426)');
200 $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
201 is( $resent, 0, 'The message should not have been resent again' );
202 $resent = C4::Letters::ResendMessage();
203 is( $resent, undef, 'ResendMessage should return undef if not message_id given' );
206 is( $messages->[0]->{failure_code}, 'MISSING_SMS', 'Failure code set correctly for no smsalertnumber correctly set' );
209 my $letters = C4::Letters::GetLetters();
210 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
212 my $title = q|<<branches.branchname>> - <<status>>|;
213 my $content = q{Dear <<borrowers.firstname>> <<borrowers.surname>>,
214 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.
216 <<branches.branchname>>
217 <<branches.branchaddress1>>
220 The following item(s) is/are currently <<status>>:
222 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
224 Thank-you for your prompt attention to this matter.
225 Don't forget your date of birth: <<borrowers.dateofbirth>>.
226 Look at this wonderful biblio timestamp: <<biblio.timestamp>>.
229 $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 );
230 $letters = C4::Letters::GetLetters();
231 is( @$letters, 1, 'GetLetters returns the correct number of letters' );
232 is( $letters->[0]->{module}, 'my module', 'GetLetters gets the module correctly' );
233 is( $letters->[0]->{code}, 'my code', 'GetLetters gets the code correctly' );
234 is( $letters->[0]->{name}, 'my name', 'GetLetters gets the name correctly' );
236 my $data_1 = { module => 'blah', code => 'ISBN', name => 'book' };
237 my $data_2 = { module => 'blah', code => 'ISSN' };
238 $builder->build_object( { class => 'Koha::Notice::Templates', value => $data_1 } );
239 $builder->build_object( { class => 'Koha::Notice::Templates', value => $data_2 } );
241 $letters = GetLetters( { module => 'blah' } );
242 is( scalar(@$letters), 2, 'GetLetters returns the 2 inserted letters' );
244 my ($ISBN_letter) = grep { $_->{code} eq 'ISBN' } @$letters;
245 is( $ISBN_letter->{name}, 'book', 'letter name for "ISBN" letter is book' );
248 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
249 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
251 my $sms_content = 'This is a SMS for an <<status>>';
252 $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 );
255 borrowers => $borrowernumber,
256 branches => $library->{branchcode},
257 biblio => $biblionumber,
264 itemcallnumber => 'my callnumber1',
268 itemcallnumber => 'my callnumber2',
272 my $prepared_letter = GetPreparedLetter((
273 module => 'my module',
274 branchcode => $library->{branchcode},
275 letter_code => 'my code',
277 substitute => $substitute,
280 my $retrieved_library = Koha::Libraries->find($library->{branchcode});
281 my $my_title_letter = $retrieved_library->branchname . qq| - $substitute->{status}|;
282 my $biblio_timestamp = dt_from_string( GetBiblioData($biblionumber)->{timestamp} );
283 my $my_content_letter = qq|Dear Jane Smith,
284 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.
286 |.$retrieved_library->branchname.qq|
287 |.$retrieved_library->branchaddress1.qq|
288 URL: http://thisisatest.com
290 The following item(s) is/are currently $substitute->{status}:
292 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
293 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
295 Thank-you for your prompt attention to this matter.
296 Don't forget your date of birth: | . output_pref({ dt => $date, dateonly => 1 }) . q|.
297 Look at this wonderful biblio timestamp: | . output_pref({ dt => $biblio_timestamp }) . ".\n";
299 is( $prepared_letter->{title}, $my_title_letter, 'GetPreparedLetter returns the title correctly' );
300 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
302 $prepared_letter = GetPreparedLetter((
303 module => 'my module',
304 branchcode => $library->{branchcode},
305 letter_code => 'my code',
307 substitute => $substitute,
309 message_transport_type => 'sms',
311 $my_content_letter = qq|This is a SMS for an $substitute->{status}|;
312 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
315 $prepared_letter = GetPreparedLetter((
316 module => 'my module',
317 branchcode => $library->{branchcode},
318 letter_code => 'my code',
320 substitute => { status => undef },
322 message_transport_type => 'sms',
325 undef, "No warning if GetPreparedLetter called with substitute containing undefined value";
326 is( $prepared_letter->{content}, q|This is a SMS for an |, 'GetPreparedLetter returns the content correctly when substitute contains undefined value' );
328 $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>>.');});
329 $prepared_letter = GetPreparedLetter((
330 module => 'test_date',
332 letter_code => 'test_date',
334 substitute => $substitute,
337 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
339 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp | dateonly>>.' WHERE code = 'test_date';});
340 $prepared_letter = GetPreparedLetter((
341 module => 'test_date',
343 letter_code => 'test_date',
345 substitute => $substitute,
348 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
350 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp|dateonly >>.' WHERE code = 'test_date';});
351 $prepared_letter = GetPreparedLetter((
352 module => 'test_date',
354 letter_code => 'test_date',
356 substitute => $substitute,
359 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
361 t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
362 my $yesterday_night = $date->clone->add( days => -1 )->set_hour(22);
363 $dbh->do(q|UPDATE biblio SET timestamp = ? WHERE biblionumber = ?|, undef, $yesterday_night, $biblionumber );
364 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp>>.' WHERE code = 'test_date';});
365 $prepared_letter = GetPreparedLetter((
366 module => 'test_date',
368 letter_code => 'test_date',
370 substitute => $substitute,
373 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
375 $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>');});
376 $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 %]');});
378 my $testacqorder2_content = <<EOF;
380 [% bookseller.name %]
381 [% FOREACH order IN orders %]
382 Ordernumber [% order.ordernumber %] [% order.quantity %] [% order.listprice | \$Price %]
386 $dbh->do("INSERT INTO letter (module, code, name, title, content) VALUES ('orderacquisition','TESTACQORDER2','Acquisition Order','Order','$testacqorder2_content');");
388 my $popito = $builder->build({
389 source => 'Aqbookseller',
390 value => { name => 'Popito' }
393 my $order_1 = $builder->build({
401 my $order_2 = $builder->build({
409 $prepared_letter = GetPreparedLetter((
410 module => 'orderacquisition',
412 letter_code => 'TESTACQORDER2',
413 tables => { 'aqbooksellers' => $popito->{id} },
415 aqorders => [ $order_1->{ordernumber}, $order_2->{ordernumber} ]
419 my $testacqorder2_expected = qq|Popito
421 Ordernumber | . $order_1->{ordernumber} . qq| 2 12.00
423 Ordernumber | . $order_2->{ordernumber} . qq| 1 23.50
427 is($prepared_letter->{content}, $testacqorder2_expected);
429 # Test that _parseletter doesn't modify its parameters bug 15429
431 my $values = { dateexpiry => '2015-12-13', };
432 C4::Letters::_parseletter($prepared_letter, 'borrowers', $values);
433 is( $values->{dateexpiry}, '2015-12-13', "_parseletter doesn't modify its parameters" );
436 # Correctly format dateexpiry
438 my $values = { dateexpiry => '2015-12-13', };
440 t::lib::Mocks::mock_preference('dateformat', 'metric');
441 t::lib::Mocks::mock_preference('timeformat', '24hr');
442 my $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
443 is( $letter->{content}, 'expiry on 13/12/2015' );
445 t::lib::Mocks::mock_preference('dateformat', 'metric');
446 t::lib::Mocks::mock_preference('timeformat', '12hr');
447 $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
448 is( $letter->{content}, 'expiry on 13/12/2015' );
451 my $bookseller = Koha::Acquisition::Bookseller->new(
454 address1 => "bookseller's address",
460 my $booksellerid = $bookseller->id;
462 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith', phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
463 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
464 my $basketno = NewBasket($booksellerid, 1, 'The basket name');
466 my $budgetid = C4::Budgets::AddBudget({
467 budget_code => "budget_code_test_letters",
468 budget_name => "budget_name_test_letters",
471 my $bib = MARC::Record->new();
472 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
474 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
478 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
482 my $logged_in_user = $builder->build_object(
484 class => 'Koha::Patrons',
486 branchcode => $library->{branchcode},
487 email => 'some@email.com'
492 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
494 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
495 my $order = Koha::Acquisition::Order->new(
497 basketno => $basketno,
499 biblionumber => $biblionumber,
500 budget_id => $budgetid,
503 my $ordernumber = $order->ordernumber;
505 Koha::Acquisition::Baskets->find( $basketno )->close;
508 $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
509 qr/^Bookseller .* without emails at/,
510 "SendAlerts prints a warning";
511 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
513 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
514 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
516 # Ensure that the preference 'ClaimsLog' is set to logging
517 t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
519 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
520 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
524 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
525 qr|Fake send_or_die|,
526 "SendAlerts is using the mocked send_or_die routine (orderacquisition)";
527 is($err, 1, "Successfully sent order.");
528 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent order");
529 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');
531 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
532 $mocked_koha_email->mock( 'send_or_die', sub {
533 Email::Sender::Failure->throw('something went wrong');
537 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ); }
538 qr{something went wrong},
539 'Warning is printed';
541 is($err->{error}, 'something went wrong', "Send exception, error message returned");
543 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
545 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
546 qr/No orderacquisition TESTACQORDER letter transported by email/,
547 "GetPreparedLetter warns about missing notice template";
548 is($err->{'error'}, 'no_letter', "No TESTACQORDER letter was defined.");
553 $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
554 qr|Fake send_or_die|,
555 "SendAlerts is using the mocked send_or_die routine";
557 is($err, 1, "Successfully sent claim");
558 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent claim");
559 is($email_object->email->body, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
561 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
562 $mocked_koha_email->mock( 'send_or_die', sub {
563 Email::Sender::Failure->throw('something went wrong');
567 $err = SendAlerts( 'claimacquisition', [ $ordernumber ] , 'TESTACQCLAIM' ); }
568 qr{something went wrong},
569 'Warning is printed';
571 is($err->{error}, 'something went wrong', "Send exception, error message returned");
575 use C4::Serials qw( NewSubscription GetSerials findSerialsByStatus ModSerialStatus );
578 my $internalnotes = 'intnotes';
579 my $number_pattern = $builder->build_object(
581 class => 'Koha::Subscription::Numberpatterns',
582 value => { numberingmethod => 'No. {X}' }
585 my $subscriptionid = NewSubscription(
586 undef, "", undef, undef, undef, $biblionumber,
587 '2013-01-01', 1, undef, undef, undef,
588 undef, undef, undef, undef, undef, undef,
589 1, $notes,undef, '2013-01-01', undef, $number_pattern->id,
590 undef, undef, 0, $internalnotes, 0,
591 undef, undef, 0, undef, '2013-12-31', 0
593 $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>>');});
594 my ($serials_count, @serials) = GetSerials($subscriptionid);
595 my $serial = $serials[0];
597 my $patron = Koha::Patron->new({
600 categorycode => $patron_category,
601 branchcode => $library->{branchcode},
602 dateofbirth => $date,
603 email => 'john.smith@test.de',
605 my $borrowernumber = $patron->borrowernumber;
606 my $subscription = Koha::Subscriptions->find( $subscriptionid );
607 $subscription->add_subscriber( $patron );
609 t::lib::Mocks::mock_userenv({ branch => $library->{branchcode} });
612 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
613 qr|Fake send_or_die|,
614 "SendAlerts is using the mocked send_or_die routine";
616 is($err2, 1, "Successfully sent serial notification");
617 is($email_object->email->header('To'), 'john.smith@test.de', "mailto correct in sent serial notification");
618 is($email_object->email->body, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
620 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', 'robert.tables@mail.com' );
624 $err3 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
625 qr|Fake send_or_die|,
626 "SendAlerts is using the mocked send_or_die routine";
627 is($email_object->email->header('To'), 'robert.tables@mail.com', "mailto address overwritten by SendAllMailsTo preference");
629 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
630 $mocked_koha_email->mock( 'send_or_die', sub {
631 Email::Sender::Failure->throw('something went wrong');
635 $err = SendAlerts( 'issue', $serial->{serialid} , 'RLIST' ); }
636 qr{something went wrong},
637 'Warning is printed';
639 is($err->{error}, 'something went wrong', "Send exception, error message returned");
642 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
644 subtest '_parseletter' => sub {
647 # Regression test for bug 10843
648 # $dt->add takes a scalar, not undef
650 t::lib::Mocks::mock_preference( 'ReservesMaxPickUpDelay', undef );
651 $letter = C4::Letters::_parseletter( undef, 'reserves', { waitingdate => "2013-01-01" } );
652 is( ref($letter), 'HASH' );
653 t::lib::Mocks::mock_preference( 'ReservesMaxPickUpDelay', 1 );
654 $letter = C4::Letters::_parseletter( undef, 'reserves', { waitingdate => "2013-01-01" } );
655 is( ref($letter), 'HASH' );
658 subtest 'SendAlerts - claimissue' => sub {
663 $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>>');});
665 my $bookseller = Koha::Acquisition::Bookseller->new(
668 address1 => "bookseller's address",
674 my $booksellerid = $bookseller->id;
676 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
678 my $bib = MARC::Record->new();
679 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
681 MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
682 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
686 MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
687 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
690 my ($biblionumber) = AddBiblio($bib, '');
692 my $number_pattern = $builder->build_object(
694 class => 'Koha::Subscription::Numberpatterns',
695 value => { numberingmethod => 'No. {X}' }
699 my $subscriptionid = NewSubscription(
700 undef, "", $booksellerid, undef, undef, $biblionumber,
701 '2013-01-01', 1, undef, undef, undef,
702 undef, undef, undef, undef, undef, undef,
703 1, 'public',undef, '2013-01-01', undef, $number_pattern->id,
704 undef, undef, 0, 'internal', 0,
705 undef, undef, 0, undef, '2013-12-31', 0
708 my ($serials_count, @serials) = GetSerials($subscriptionid);
709 my @serialids = ($serials[0]->{serialid});
713 $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
714 qr/^Bookseller .* without emails at/,
715 "Warn on vendor without email address";
717 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
718 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
720 # Ensure that the preference 'ClaimsLog' is set to logging
721 t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
723 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
724 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
726 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
730 $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
731 qr|Fake send_or_die|,
732 "SendAlerts is using the mocked send_or_die routine (claimissues)";
733 is( $err, 1, "Successfully sent claim" );
734 is( $email_object->email->header('To'),
735 'testemail@mydomain.com', "mailto correct in sent claim" );
737 $email_object->email->body,
738 "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy",
739 'Serial claim letter for 1 issue constructed successfully'
742 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
743 $mocked_koha_email->mock( 'send_or_die', sub {
744 Email::Sender::Failure->throw('something went wrong');
748 $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ); }
749 qr{something went wrong},
750 'Warning is printed';
752 is($err->{error}, 'something went wrong', "Send exception, error message returned");
756 my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
757 my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
758 ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
759 ($serials_count, @serials) = GetSerials($subscriptionid);
760 push @serialids, ($serials[1]->{serialid});
762 warning_like { $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ); }
763 qr|Fake send_or_die|,
764 "SendAlerts is using the mocked send_or_die routine (claimissues)";
767 $email_object->email->body,
768 "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy"
769 . $email_object->email->crlf
770 . "$serialids[1]|2013-01-01|Silence in the library|xxxx-yyyy",
771 "Serial claim letter for 2 issues constructed successfully"
774 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
776 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
777 qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
778 "GetPreparedLetter warns about missing notice template";
779 is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
784 subtest 'GetPreparedLetter' => sub {
787 Koha::Notice::Template->new(
792 message_transport_type => 'email'
797 $letter = C4::Letters::GetPreparedLetter(
799 letter_code => 'test',
802 qr{^ERROR: nothing to substitute},
803 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
805 'No letter should be returned by GetPreparedLetter if something went wrong'
809 $letter = C4::Letters::GetPreparedLetter(
811 letter_code => 'test',
815 qr{^ERROR: nothing to substitute},
816 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
818 'No letter should be returned by GetPreparedLetter if something went wrong'
825 subtest 'TranslateNotices' => sub {
828 t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
832 INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
833 ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
834 ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
837 my $letter = C4::Letters::GetPreparedLetter(
840 letter_code => 'code',
841 message_transport_type => 'email',
842 substitute => $substitute,
847 'GetPreparedLetter should return the default one if the lang parameter is not provided'
850 $letter = C4::Letters::GetPreparedLetter(
853 letter_code => 'code',
854 message_transport_type => 'email',
855 substitute => $substitute,
858 is( $letter->{title}, 'una prueba',
859 'GetPreparedLetter should return the required notice if it exists' );
861 $letter = C4::Letters::GetPreparedLetter(
864 letter_code => 'code',
865 message_transport_type => 'email',
866 substitute => $substitute,
872 'GetPreparedLetter should return the default notice if the one required does not exist'
875 t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
877 $letter = C4::Letters::GetPreparedLetter(
880 letter_code => 'code',
881 message_transport_type => 'email',
882 substitute => $substitute,
885 is( $letter->{title}, 'a test',
886 'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
890 subtest 'Test SMS handling in SendQueuedMessages' => sub {
894 t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
895 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
897 my $patron = Koha::Patrons->find($borrowernumber);
899 INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
900 VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
901 |, undef, $borrowernumber
903 eval { C4::Letters::SendQueuedMessages(); };
904 is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
906 my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
907 $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
908 $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
910 warning_like { C4::Letters::SendQueuedMessages(); }
911 qr|Fake send_or_die|,
912 "SendAlerts is using the mocked send_or_die routine (claimissues)";
914 my $message = $schema->resultset('MessageQueue')->search({
915 borrowernumber => $borrowernumber,
919 is( $message->letter_id, $messages->[0]->{id}, "Message letter_id is set correctly" );
920 is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
922 $message->from_address(),
924 'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
927 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
929 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
931 $message_id = C4::Letters::EnqueueLetter($my_message);
932 warning_like { C4::Letters::SendQueuedMessages(); }
933 qr|Fake send_or_die|,
934 "SendAlerts is using the mocked send_or_die routine (claimissues)";
936 $message = $schema->resultset('MessageQueue')->search({
937 borrowernumber => $borrowernumber,
942 $message->from_address(),
943 'override@example.com',
944 'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
947 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
948 $my_message->{to_address} = 'fixme@kidclamp.iswrong';
949 $message_id = C4::Letters::EnqueueLetter($my_message);
951 my $number_attempted = C4::Letters::SendQueuedMessages({
952 borrowernumber => -1, # -1 still triggers the borrowernumber condition
953 letter_code => 'PASSWORD_RESET',
955 is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
957 warning_like { C4::Letters::SendQueuedMessages(); }
958 qr|Fake send_or_die|,
959 "SendAlerts is using the mocked send_or_die routine (claimissues)";
961 my $sms_message_address = $schema->resultset('MessageQueue')->search({
962 borrowernumber => $borrowernumber,
964 })->next()->to_address();
965 is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
967 # Test using SMS::Send::Test driver that's bundled with SMS::Send
968 t::lib::Mocks::mock_preference('SMSSendDriver', "AU::Test");
970 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
971 C4::Letters::EnqueueLetter($my_message);
972 C4::Letters::SendQueuedMessages();
974 $sms_message_address = $schema->resultset('MessageQueue')->search({
975 borrowernumber => $borrowernumber,
977 })->next()->to_address();
978 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' );
980 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
983 subtest 'Test guarantor handling in SendQueuedMessages' => sub {
987 t::lib::Mocks::mock_preference( 'borrowerRelationship', 'test' );
989 my $patron = Koha::Patrons->find($borrowernumber);
990 my $guarantor1 = $builder->build_object( { class => 'Koha::Patrons', value => { email => 'g1@email.com' } } );
991 my $guarantor2 = $builder->build_object( { class => 'Koha::Patrons', value => { email => 'g2@email.com' } } );
992 $patron->add_guarantor( { guarantor_id => $guarantor1->borrowernumber, relationship => 'test' } );
993 $patron->add_guarantor( { guarantor_id => $guarantor2->borrowernumber, relationship => 'test' } );
997 'content' => 'a message',
998 'metadata' => 'metadata',
999 'code' => 'TEST_MESSAGE',
1000 'content_type' => 'text/plain',
1001 'title' => 'message title'
1003 'borrowernumber' => $borrowernumber,
1004 'to_address' => undef,
1005 'message_transport_type' => 'email',
1006 'from_address' => 'from@example.com'
1008 $message_id = C4::Letters::EnqueueLetter($my_message);
1011 t::lib::Mocks::mock_preference( 'RedirectGuaranteeEmail', '0' );
1013 warning_like { C4::Letters::SendQueuedMessages(); }
1014 qr|No 'to_address', email address or guarantors email address for borrowernumber|,
1015 "SendQueuedMessages fails when no to_address, patron notice email and RedirectGuaranteeEmail is not set";
1018 t::lib::Mocks::mock_preference( 'RedirectGuaranteeEmail', '1' );
1020 # reset message - testing without to or borrower valid email
1021 Koha::Notice::Messages->find($message_id)->delete;
1022 $message_id = C4::Letters::EnqueueLetter($my_message);
1024 warning_like { C4::Letters::SendQueuedMessages(); }
1025 qr|Fake send_or_die|,
1026 "SendQueuedMessages is using the mocked send_or_die routine";
1028 $message = $schema->resultset('MessageQueue')->search(
1030 borrowernumber => $borrowernumber,
1036 $message->to_address(),
1038 'SendQueuedMessages uses first guarantor email for "to" when patron has no email'
1042 $message->cc_address(),
1044 'SendQueuedMessages sets cc address to second guarantor email when "to" takes first guarantor email'
1047 is( $email_object->email->header('To'), $guarantor1->email, "mailto correctly uses first guarantor" );
1048 is( $email_object->email->header('Cc'), $guarantor2->email, "cc correctly uses second guarantor" );
1050 # reset message - testing borrower with valid email
1051 Koha::Notice::Messages->find($message_id)->delete;
1052 $message_id = C4::Letters::EnqueueLetter($my_message);
1054 $patron->email('patron@example.com')->store();
1056 warning_like { C4::Letters::SendQueuedMessages(); }
1057 qr|Fake send_or_die|,
1058 "SendQueuedMessages is using the mocked send_or_die routine";
1060 $message = $schema->resultset('MessageQueue')->search(
1062 borrowernumber => $borrowernumber,
1068 $message->to_address(),
1070 'SendQueuedMessages uses patron email when defined'
1074 $message->cc_address(),
1075 $guarantor1->email.",".$guarantor2->email,
1076 'SendQueuedMessages sets cc address to both guarantor emails when patron has email defined'
1079 is( $email_object->email->header('To'), $patron->email, "mailto correctly uses patrons email address" );
1080 is( $email_object->email->header('Cc'), $guarantor1->email.", ".$guarantor2->email, "cc correctly uses both guarantors" );
1083 # reset message - testing explicit to passed to enqueue
1084 Koha::Notice::Messages->find($message_id)->delete;
1085 $my_message->{'to_address'} = 'to@example.com';
1086 $message_id = C4::Letters::EnqueueLetter($my_message);
1088 warning_like { C4::Letters::SendQueuedMessages(); }
1089 qr|Fake send_or_die|,
1090 "SendQueuedMessages is using the mocked send_or_die routine";
1092 $message = $schema->resultset('MessageQueue')->search(
1094 borrowernumber => $borrowernumber,
1100 $message->to_address(),
1102 'SendQueuedMessages uses to_address if it was specified at enqueue time'
1106 $message->cc_address(),
1107 $guarantor1->email.",".$guarantor2->email,
1108 'SendQueuedMessages sets cc address to both guarantor emails when "to" is already specified'
1111 is( $email_object->email->header('To'), 'to@example.com', "mailto correctly uses passed email" );
1112 is( $email_object->email->header('Cc'), $guarantor1->email.", ".$guarantor2->email, "cc correctly uses both guarantors" );
1114 # clear borrower queue
1115 Koha::Notice::Messages->find($message_id)->delete;
1118 subtest 'get_item_content' => sub {
1121 t::lib::Mocks::mock_preference('dateformat', 'metric');
1122 t::lib::Mocks::mock_preference('timeformat', '24hr');
1124 {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
1125 {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
1127 my @item_content_fields = qw( date_due title barcode author itemnumber );
1130 for my $item ( @items ) {
1131 $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
1134 my $expected_items_content = <<EOF;
1135 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
1136 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
1138 is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
1141 $items_content = q||;
1142 for my $item ( @items ) {
1143 $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
1146 $expected_items_content = <<EOF;
1147 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
1148 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
1150 is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
1153 subtest 'Test where parameter for SendQueuedMessages' => sub {
1156 my $dbh = C4::Context->dbh;
1158 my $borrowernumber = Koha::Patron->new({
1159 firstname => 'Jane',
1161 categorycode => $patron_category,
1162 branchcode => $library->{branchcode},
1163 dateofbirth => $date,
1164 })->store->borrowernumber;
1166 $dbh->do(q|DELETE FROM message_queue|);
1169 'content' => 'a message',
1170 'metadata' => 'metadata',
1171 'code' => 'TEST_MESSAGE',
1172 'content_type' => 'text/plain',
1173 'title' => 'message title'
1175 'borrowernumber' => $borrowernumber,
1176 'to_address' => undef,
1177 'message_transport_type' => 'sms',
1178 'from_address' => 'from@example.com'
1182 'content' => 'another message',
1183 'metadata' => 'metadata',
1184 'code' => 'TEST_MESSAGE',
1185 'content_type' => 'text/plain',
1186 'title' => 'message title'
1188 'borrowernumber' => $borrowernumber,
1189 'to_address' => undef,
1190 'message_transport_type' => 'sms',
1191 'from_address' => 'from@example.com'
1195 'content' => 'a skipped message',
1196 'metadata' => 'metadata',
1197 'code' => 'TEST_MESSAGE',
1198 'content_type' => 'text/plain',
1199 'title' => 'message title'
1201 'borrowernumber' => $borrowernumber,
1202 'to_address' => undef,
1203 'message_transport_type' => 'sms',
1204 'from_address' => 'from@example.com'
1206 my @id = ( C4::Letters::EnqueueLetter($my_message),
1207 C4::Letters::EnqueueLetter($my_message2),
1208 C4::Letters::EnqueueLetter($my_message3),
1210 C4::Letters::SendQueuedMessages({
1211 # Test scalar/arrayref in parameter too
1212 letter_code => [ 'TEST_MESSAGE', 'SOMETHING_NOT_THERE' ],
1214 where => q{content NOT LIKE '%skipped%'},
1216 is( Koha::Notice::Messages->find( $id[0] )->status, 'failed', 'Processed but failed' );
1217 is( Koha::Notice::Messages->find( $id[1] )->status, 'failed', 'Processed but failed' );
1218 is( Koha::Notice::Messages->find( $id[2] )->status, 'pending', 'Skipped, still pending' );
1221 subtest 'Test limit parameter for SendQueuedMessages' => sub {
1224 my $dbh = C4::Context->dbh;
1226 my $borrowernumber = Koha::Patron->new({
1227 firstname => 'Jane',
1229 categorycode => $patron_category,
1230 branchcode => $library->{branchcode},
1231 dateofbirth => $date,
1232 email => 'shouldnotwork@wrong.net',
1233 })->store->borrowernumber;
1235 $dbh->do(q|DELETE FROM message_queue|);
1238 'content' => 'a message',
1239 'metadata' => 'metadata',
1240 'code' => 'TEST_MESSAGE',
1241 'content_type' => 'text/plain',
1242 'title' => 'message title'
1244 'borrowernumber' => $borrowernumber,
1245 'to_address' => undef,
1246 'message_transport_type' => 'email',
1247 'from_address' => 'from@example.com'
1249 C4::Letters::EnqueueLetter($my_message) for 1..5;
1251 $send_or_die_count = 0; # reset
1253 my $regex = qr|Fake send_or_die|;
1255 $messages_sent = C4::Letters::SendQueuedMessages( { limit => 1 } ) }
1257 "SendQueuedMessages with limit 1";
1258 is( $messages_sent, 1,
1259 'Sent 1 message with limit of 1 and 5 unprocessed messages' );
1262 $messages_sent = C4::Letters::SendQueuedMessages( { limit => 2 } ) }
1263 [ map { $regex } 1..2 ],
1264 "SendQueuedMessages with limit 2";
1265 is( $messages_sent, 2,
1266 'Sent 2 messages with limit of 2 and 4 unprocessed messages' );
1269 $messages_sent = C4::Letters::SendQueuedMessages( { limit => 3 } ) }
1270 [ map { $regex } 1..2 ],
1271 "SendQueuedMessages with limit 3";
1272 is( $messages_sent, 2,
1273 'Sent 2 messages with limit of 3 and 2 unprocessed messages' );
1275 is( $send_or_die_count, 5, '5 messages sent' );
1276 # Mimic correct status in queue for next tests
1277 Koha::Notice::Messages->search({ to_address => { 'LIKE', '%wrong.net' }})->update({ status => 'sent' });
1279 # Now add a few domain limits too, sending 2 more mails to wrongnet, 2 to fake1, 2 to fake2
1280 # Since we already sent 5 to wrong.net, we expect one deferral when limiting to 6
1281 # Similarly we arrange for 1 deferral for fake1, and 2 for fake2
1282 # We set therefore limit to 3 sent messages: we expect 2 sent, 4 deferred (so 6 processed)
1283 t::lib::Mocks::mock_config( 'message_domain_limits', { domain => [
1284 { name => 'wrong.net', limit => 6, unit => '1m' },
1285 { name => 'fake1.domain', limit => 1, unit => '1m' },
1286 { name => 'fake2.domain', limit => 0, unit => '1m' },
1288 C4::Letters::EnqueueLetter($my_message) for 1..2;
1289 $my_message->{to_address} = 'someone@fake1.domain';
1290 C4::Letters::EnqueueLetter($my_message) for 1..2;
1291 $my_message->{to_address} = 'another@fake2.domain';
1292 C4::Letters::EnqueueLetter($my_message) for 1..2;
1293 my $mocked_util = Test::MockModule->new('Koha::Notice::Util');
1294 my $count_exceeds_calls = 0;
1295 $mocked_util->mock( 'exceeds_limit', sub {
1296 $count_exceeds_calls++;
1297 $mocked_util->original('exceeds_limit')->(@_);
1300 $messages_sent = C4::Letters::SendQueuedMessages({ limit => 3 }) }
1301 [ qr/wrong.net reached limit/, $regex, qr/fake1.domain reached limit/, $regex ],
1302 "SendQueuedMessages with limit 2 and domain limits";
1303 is( $messages_sent, 2, 'Only expecting 2 sent messages' );
1304 is( Koha::Notice::Messages->search({ status => 'pending' })->count, 4, 'Still 4 pending' );
1305 is( $count_exceeds_calls, 6, 'We saw 6 messages while checking domain limits: so we deferred 4' );
1308 subtest 'Test message_id parameter for SendQueuedMessages' => sub {
1312 my $dbh = C4::Context->dbh;
1314 my $borrowernumber = Koha::Patron->new({
1315 firstname => 'Jane',
1317 categorycode => $patron_category,
1318 branchcode => $library->{branchcode},
1319 dateofbirth => $date,
1320 smsalertnumber => undef,
1321 })->store->borrowernumber;
1323 $dbh->do(q|DELETE FROM message_queue|);
1326 'content' => 'a message',
1327 'metadata' => 'metadata',
1328 'code' => 'TEST_MESSAGE',
1329 'content_type' => 'text/plain',
1330 'title' => 'message title'
1332 'borrowernumber' => $borrowernumber,
1333 'to_address' => 'to@example.org',
1334 'message_transport_type' => 'email',
1335 'from_address' => '@example.com' # invalid from_address
1337 my $message_id = C4::Letters::EnqueueLetter($my_message);
1338 $send_or_die_count = 0; # reset
1339 my $processed = C4::Letters::SendQueuedMessages( { message_id => $message_id } );
1340 is( $send_or_die_count, 0, 'Nothing sent when one message_id passed' );
1341 my $message_1 = C4::Letters::GetMessage($message_id);
1342 is( $message_1->{status}, 'failed', 'Invalid from_address => status failed' );
1343 is( $message_1->{failure_code}, 'INVALID_EMAIL:from', 'Failure code set correctly for invalid email parameter');
1345 $my_message->{from_address} = 'root@example.org'; # valid from_address
1346 $message_id = C4::Letters::EnqueueLetter($my_message);
1347 warning_like { C4::Letters::SendQueuedMessages( { message_id => $message_id } ); }
1348 qr|Fake send_or_die|,
1349 "SendQueuedMessages is using the mocked send_or_die routine";
1350 is( $send_or_die_count, 1, 'One message passed through' );
1351 $message_1 = C4::Letters::GetMessage($message_1->{message_id});
1352 my $message_2 = C4::Letters::GetMessage($message_id);
1353 is( $message_1->{status}, 'failed', 'Message 1 status is unchanged' );
1354 is( $message_2->{status}, 'sent', 'Valid from_address => status sent' );