diff --git a/VERSIONS_STABLE/18661 b/VERSIONS_STABLE/18661 new file mode 100644 index 0000000000000000000000000000000000000000..754b78113bb791424ebba5001d67f8f172334331 --- /dev/null +++ b/VERSIONS_STABLE/18661 @@ -0,0 +1 @@ + - ticket #18661 : dédoublonnage des mails à l'envoi d'une newsletter \ No newline at end of file diff --git a/library/Class/Batch/SendNewsletters.php b/library/Class/Batch/SendNewsletters.php index bfd6c9cd21ee81d0d61a11d8e8b90155008db75d..21dc9d5aa0c2345ba877a011fbb835f06a7b2994 100644 --- a/library/Class/Batch/SendNewsletters.php +++ b/library/Class/Batch/SendNewsletters.php @@ -22,12 +22,14 @@ class Class_Batch_SendNewsletters extends Class_Batch_Abstract { - protected $_newsletter; + protected + $_newsletter, + $_previous_mail, + $_time_limit; public function __construct($newsletter) { $this->_newsletter = $newsletter; - return $this; } @@ -50,11 +52,79 @@ class Class_Batch_SendNewsletters extends Class_Batch_Abstract { public function run() { - exec("/usr/bin/php -f ".realpath(dirname(__FILE__))."/../../../scripts/sendNewsletter.php " - .$this->getExecParams() - ." > /dev/null &"); + exec('/usr/bin/php -f ' + . realpath(dirname(__FILE__)) . '/../../../scripts/sendNewsletter.php ' + . $this->getExecParams() + . ' > /dev/null &'); + return $this; + } + + + public function sendAllBy($page_size) { + if (!$this->_newsletter) + return; + + $letter = $this->_newsletter; + + Class_NewsletterSubscription::resetSendFlagForNewsletter($letter->getId()); + + $this->_previous_mail = ''; + while (0 < count(array_filter($receivers = $letter->getReceivers($page_size)))) { + $this->_clearMemory() + ->_giveMeMoreTime(30) + ->_sendPage($receivers); + + $letter->setLastDistributionDateWithFormat(); + Class_NewsletterSubscription::updateSendFlagForReceivers($letter->getId(), + $letter->getReceivers($page_size)); + } + + $this->getTimeLimit()->reset(); + } + + + protected function _clearMemory() { + Class_NewsletterSubscription::clearCache(); + Class_Users::clearCache(); + Storm_Model_Loader::resetCache(); + gc_collect_cycles(); + + return $this; + } + + + protected function _giveMeMoreTime($seconds) { + $this->getTimeLimit()->set($seconds); return $this; } -} -?> \ No newline at end of file + + protected function _sendPage($receivers) { + $letter = $this->_newsletter; + + $mail = $letter->newMail(); + $mail->addTo($letter->getExpediteur()); + + foreach($receivers as $receiver) + $this->_addReceiverTo($receiver, $mail); + + $mail->send(); + } + + + protected function _addReceiverTo($receiver, $mail) { + $receiver_mail = $receiver->getMail(); + if ($this->_previous_mail != $receiver_mail) { + $mail->addBcc($receiver_mail); + $this->_previous_mail = $receiver_mail; + } + } + + + public function getTimeLimit() { + if (!$this->_time_limit) + $this->_time_limit = Class_Systeme_TimeLimit::getInstance(); + + return $this->_time_limit; + } +} \ No newline at end of file diff --git a/library/Class/Newsletter.php b/library/Class/Newsletter.php index 60bab4e402ee684d9a588829647aa2b4240399fd..4ed1a48102cb0ed106bae2714f419978657a90c7 100644 --- a/library/Class/Newsletter.php +++ b/library/Class/Newsletter.php @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -55,7 +55,7 @@ class Class_Newsletter extends Storm_Model_Abstract { use Trait_TimeSource, Trait_Translator; - + protected $_table_name = 'newsletters'; protected $_has_many = ['subscriptions' => ['model' => 'Class_NewsletterSubscription', 'role' => 'newsletter', @@ -69,7 +69,7 @@ class Class_Newsletter extends Storm_Model_Abstract { 'limitPage' => [$page, $items_by_page]]); } - + public function send() { return (new Class_Batch_SendNewsletters($this))->run(); } @@ -83,7 +83,7 @@ class Class_Newsletter extends Storm_Model_Abstract { public function sendTo($destinataire) { - $mail = $this->_newMail(); + $mail = $this->newMail(); $mail->addTo($destinataire); $mail->send(); } @@ -97,7 +97,7 @@ class Class_Newsletter extends Storm_Model_Abstract { } - protected function _newMail() { + public function newMail() { $notices = $this->getNotices(); $mail = new ZendAfi_Mail('utf8'); @@ -117,52 +117,33 @@ class Class_Newsletter extends Storm_Model_Abstract { protected function getMailAt($index) { $users = Class_NewsletterSubscription::getAvailableUsersForNewsletter($this->getId(), $index); - - $mail = $this->_newMail(); + + $mail = $this->newMail(); $mail->addTo($this->getExpediteur()); foreach($users as $user) { - ($user_mail = $user->getMail()) + ($user_mail = $user->getMail()) ? $mail->addBcc($user_mail) : ''; } return $mail; } - + public function getNumberOfUsers() { return sprintf('%05d', Class_NewsletterSubscription::countAvailableUserForNewsletter($this->getId())); } - public function generateMails($recipient_size) { - Class_NewsletterSubscription::resetSendFlagForNewsletter($this->getId()); - $time_limit = Class_Systeme_TimeLimit::getInstance(); - - while (0 < count(array_filter( $receivers = $this->getReceivers($recipient_size)))) { - Class_NewsletterSubscription::clearCache(); - Class_Users::clearCache(); - Storm_Model_Loader::resetCache(); - gc_collect_cycles(); - $time_limit->set(30); - - $mail = $this->_newMail(); - $mail->addTo($this->getExpediteur()); - - foreach($receivers as $receiver) - $mail->addBcc($receiver->getMail()); - $mail->send(); - $this->setLastDistributionDateWithFormat(); - Class_NewsletterSubscription::updateSendFlagForReceivers($this->getId(),$this->getReceivers($recipient_size)); - } - $time_limit->reset(); + (new Class_Batch_SendNewsletters($this)) + ->sendAllBy($recipient_size); } - protected function getReceivers($recipient_size) { - return Class_Users::getNewslettersReceivers($this->getId(), $recipient_size); + public function getReceivers($recipient_size) { + return Class_Users::getNewslettersReceivers($this->getId(), $recipient_size); } @@ -253,7 +234,7 @@ class Class_Newsletter extends Storm_Model_Abstract { } return $html; - + } diff --git a/library/Class/NewsletterSubscription.php b/library/Class/NewsletterSubscription.php index 873ec5c8638bc757f710225fdb7116669061fd40..b953d0cccd8a17aa889430de0c9c0cbf90b9b1d9 100644 --- a/library/Class/NewsletterSubscription.php +++ b/library/Class/NewsletterSubscription.php @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ class NewsletterSubscriptionLoader extends Storm_Model_Loader { @@ -25,7 +25,7 @@ class NewsletterSubscriptionLoader extends Storm_Model_Loader { return Class_NewsletterSubscription::countBy($this->availableUserForNewsletter($id_newsletter)); } - + protected function clearInvalid($id_newsletter) { Class_NewsletterSubscription::deleteBy($this->notAvailableUserForNewsletter($id_newsletter)); } @@ -45,29 +45,30 @@ class NewsletterSubscriptionLoader extends Storm_Model_Loader { if(!$subscriptions) return []; - + $users = []; foreach($subscriptions as $subscription) { $user = Class_Users::find($subscription->getUserId()); if(!$user || !$user->getMail()) continue; - + $users[] = $user; } return array_filter($users); } - + public function resetSendFlagForNewsletter($id_newsletter) { $newsletters_not_already_send = Class_NewsletterSubscription::findAllBy(['newsletter_id' => $id_newsletter, 'send' => false]); - if(1 > count(array_filter($newsletters_not_already_send))) + + if(0 == count(array_filter($newsletters_not_already_send))) return sqlExecute("update newsletters_users set send=false where newsletter_id=".$id_newsletter); return ''; } - + public function updateSendFlagForReceivers($id_newsletter,$receivers) { $users_ids_list = []; foreach ($receivers as $user) { @@ -92,11 +93,11 @@ class NewsletterSubscriptionLoader extends Storm_Model_Loader { return ['where' => 'newsletter_id = '.$id_newsletter.' and '.$this->usersWithNoMail()]; } - + protected function usersWithNoMail() { return 'user_id not in ('.$this->selectUser().')'; } - + protected function validUsers() { return 'user_id in ('.$this->selectUser().')'; @@ -115,12 +116,12 @@ class Class_NewsletterSubscription extends Storm_Model_Abstract { protected $_loader_class = 'NewsletterSubscriptionLoader'; protected $_belongs_to = ['user' => ['model' => 'Class_Users'], 'newsletter' => ['model' => 'Class_Newsletter']]; - + public static function newWith($newsletter, $user) { if(!$user || !$newsletter) return new Class_NewsletterSubscription(); - + $subscription = (new Class_NewsletterSubscription()) ->setNewsletter($newsletter) ->setUser($user); diff --git a/library/Class/Users.php b/library/Class/Users.php index 41599f619c4217089a8575a035fe28be230f9d87..a8ad8fdea0e95daf4e68ee22625a79187cbb6599 100644 --- a/library/Class/Users.php +++ b/library/Class/Users.php @@ -20,10 +20,11 @@ */ class UsersLoader extends Storm_Model_Loader { - public function getNewslettersReceivers($id_newsletter,$recipient_size) { - $req = "select bib_admin_users.* from bib_admin_users join newsletters_users on bib_admin_users.id_user = newsletters_users.user_id where newsletter_id = ".$id_newsletter." and newsletters_users.send is false limit ".$recipient_size; - $users = Class_Users::findAll($req); - return $users; + public function getNewslettersReceivers($id_newsletter, $recipient_size) { + // do not use Storm as it must be ordered by mail AND filtered by joined column + $req = 'select bib_admin_users.* from bib_admin_users join newsletters_users on bib_admin_users.id_user = newsletters_users.user_id where newsletter_id = ' . $id_newsletter . ' and newsletters_users.send is false order by bib_admin_users.mail limit ' . $recipient_size; + + return Class_Users::findAll($req); } diff --git a/tests/library/Class/MockMailTransport.php b/tests/library/Class/MockMailTransport.php index 8fd521bf31db1a54f1b9dd2282590239f019ca8f..04d454d6945981512f2e2bbc7585ed415f9c069c 100644 --- a/tests/library/Class/MockMailTransport.php +++ b/tests/library/Class/MockMailTransport.php @@ -16,32 +16,32 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ class MockMailTransport extends Zend_Mail_Transport_Abstract { public $sent_mail = null; protected $_send_block; - protected $_sent_mails = array(); + protected $_sent_mails = []; public function send(Zend_Mail $mail) { $this->sent_mail = $mail; - $this->_sent_mails []= $mail; + $this->_sent_mails[] = $mail; - if (isset($this->_send_block)) { + if (isset($this->_send_block)) call_user_func($this->_send_block); - } } + public function onSendDo($block) { $this->_send_block = $block; } + protected function _sendMail() {} + public function getSentMails() { return $this->_sent_mails; } -} - -?> \ No newline at end of file +} \ No newline at end of file diff --git a/tests/library/Class/ModelTestCase.php b/tests/library/Class/ModelTestCase.php index c90b7e003e9e8f8b70a7e2db071a2dc9bad9612f..ee7985dd2d3678690155c515a6e6871c529ee7f8 100644 --- a/tests/library/Class/ModelTestCase.php +++ b/tests/library/Class/ModelTestCase.php @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE * along with BOKEH; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ abstract class TestFixtures { @@ -39,58 +39,69 @@ abstract class TestFixtures { abstract class ModelTestCase extends PHPUnit_Framework_TestCase { use Storm_Test_THelpers; + protected $_registry_sql; + + + + protected function setUp() { + Storm_Model_Abstract::unsetLoaders(); + Class_SessionFormationInscription::beVolatile(); + $this->_registry_sql = Zend_Registry::get('sql'); + } + + + protected function tearDown() { + Storm_Model_Abstract::unsetLoaders(); + if ($this->_registry_sql) + Zend_Registry::set('sql', $this->_registry_sql); + } + + protected function _buildTableMock($model, $methods) { $table = $this->getMock('Storm_Model_Table'.$model,$methods); - $loader = call_user_func(array($model, 'getLoader')); + $loader = call_user_func([$model, 'getLoader']); $loader->setTable($table); return $table; } + protected function _buildRowset($data) { - return new Zend_Db_Table_Rowset(array('data' => $data)); + return new Zend_Db_Table_Rowset(['data' => $data]); } protected function _setFindExpectation($model, $fixture, $id) { - $mock_results = $this->_buildRowset(array($fixture)); + $mock_results = $this->_buildRowset([$fixture]); - $this->_buildTableMock($model, array('find')) + $this->_buildTableMock($model, ['find']) ->expects($this->once()) ->method('find') ->with($id) ->will($this->returnValue($mock_results)); } - protected function setUp() { - Storm_Model_Abstract::unsetLoaders(); - Class_SessionFormationInscription::beVolatile(); - } - - protected function tearDown() { - Storm_Model_Abstract::unsetLoaders(); - } protected function _setFindAllExpectation($model, $fixtures) { if (!is_array($fixtures)) { - $finst = new $fixtures; + $finst = new $fixtures(); $fixtures = $finst->all(); } + $mock_results = $this->_buildRowset($fixtures); - $tbl_newsletters = $this->_buildTableMock($model, - array('fetchAll')); + $tbl_newsletters = $this->_buildTableMock($model, ['fetchAll']); $tbl_newsletters ->expects($this->once()) ->method('fetchAll') ->will($this->returnValue($mock_results)); + return $tbl_newsletters; } + protected function _generateLoaderFor($model, $methods) { $loader = $this->getMock('Mock'.$model, $methods); Storm_Model_Abstract::setLoaderFor($model, $loader); return $loader; } -} - -?> \ No newline at end of file +} \ No newline at end of file diff --git a/tests/library/Class/NewsletterMailingTest.php b/tests/library/Class/NewsletterMailingTest.php index 3cfdefe6b1ffa52371711f6b93c135dd8be2f62b..bd68b5f6febb8f24effeee82050f6a1e4b478543 100644 --- a/tests/library/Class/NewsletterMailingTest.php +++ b/tests/library/Class/NewsletterMailingTest.php @@ -33,9 +33,9 @@ abstract class NewsletterMailingTestCase extends ModelTestCase { Class_Systeme_TimeLimit::setInstance( $this->mock() - ->whenCalled('set')->with(30)->answers(null) - ->whenCalled('reset')->answers(null) - ->beStrict()); + ->whenCalled('set')->with(30)->answers(null) + ->whenCalled('reset')->answers(null) + ->beStrict()); $profil_portail = $this->fixture('Class_Profil', ['id' => 1, @@ -114,7 +114,7 @@ abstract class NewsletterMailingTestCase extends ModelTestCase { ->beStrict(); - Storm_Test_ObjectWrapper::onLoaderOfModel('Class_NewsletterSubscription') + $this->onLoaderOfModel('Class_NewsletterSubscription') ->whenCalled('clearCache') ->answers(true) @@ -139,7 +139,7 @@ abstract class NewsletterMailingTestCase extends ModelTestCase { ->beStrict(); - Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Notice') + $this->onLoaderOfModel('Class_Notice') ->whenCalled('getNoticesFromPreferences') ->willDo(function() {return $this->notices;}); @@ -153,30 +153,45 @@ abstract class NewsletterMailingTestCase extends ModelTestCase { protected function getReceivers() { - if ($this->user_model->methodCallCount('getNewslettersReceivers')>1) + if ($this->user_model->methodCallCount('getNewslettersReceivers') > 1) return []; - return [$this->rdubois, - $this->mduchamp, - $this->zork, - $this->zorkglub]; + + return [$this->rdubois, $this->mduchamp, $this->zork, $this->zorkglub]; + } + + + protected function assertMIMEPartContains($needle, $text) { + $decoded_text = quoted_printable_decode($text->getContent()); + $this->assertContains($needle, + $decoded_text, + $needle . ' not found in ' . $decoded_text); + } + + + protected function assertBodyHTMLContains($needle) { + $this->assertMIMEPartContains($needle, $this->mail->getBodyHTML()); + } + + + protected function assertBodyTextContains($needle) { + $this->assertMIMEPartContains($needle, $this->mail->getBodyText()); } } -class NewsletterMailingAnimationsTestSendMail extends NewsletterMailingTestCase { +class NewsletterMailingAnimationsSendMailTest extends NewsletterMailingTestCase { public function setUp() { parent::setup(); - $this->animations->generateMails(20); - $this->mail = $this->mock_transport->sent_mail; $this->batch_send = new Class_Batch_SendNewsletters($this->animations); } /** @test */ public function batchSendParamsShouldBeAsExpected() { - $this->assertEquals('"'.$_SERVER['HTTP_HOST'].'" "'.$_SERVER['SERVER_NAME'].'" "'.BASE_URL.'" "1"', $this->batch_send->getExecParams()); + $this->assertEquals('"'.$_SERVER['HTTP_HOST'].'" "'.$_SERVER['SERVER_NAME'].'" "'.BASE_URL.'" "1"', + $this->batch_send->getExecParams()); } @@ -186,131 +201,257 @@ class NewsletterMailingAnimationsTestSendMail extends NewsletterMailingTestCase } - public function testToArrayContainsAdminPortailAsExpediteur() { - $this->assertContains('flo@afi-sa.fr', - $this->mail->getFrom()); + /** @test */ + public function mailShouldBeSendFromPortalAdmin() { + $this->assertContains('flo@afi-sa.fr', $this->mail->getFrom()); } - public function testSubjectIsAnimationsDuMois() { + + /** @test */ + public function subjectShouldBeAnimationsDuMois() { $this->assertEquals('Animations du mois', $this->mail->getSubject()); } - public function testBodyTextIsDecouverteCuisineDuMonde() { + + /** @test */ + public function bodyTextShouldBeDecouverteCuisineDuMonde() { $this->assertContains('Découverte des cuisines du monde', quoted_printable_decode($this->mail->getBodyText()->getContent())); } - public function testBccIncludesRduboisAtFreeDotFr() { - $this->assertContains('rdubois@afi-sa.fr', - implode(',',$this->mail->getRecipients())); - } - public function testBccIncludesMduchampAtHotmailDotCom() { - $this->assertContains('mduchamp@afi-sa.fr', - $this->mail->getRecipients()); + /** @test */ + public function bccShouldContainsRduboisAtFreeDotFr() { + $this->assertContains('rdubois@afi-sa.fr', implode(',',$this->mail->getRecipients())); } - public function testBccShouldNotIncludeZork() { - $this->assertNotContains('zork', - $this->mail->getRecipients()); + /** @test */ + public function bccShouldContainsMduchampAtHotmailDotCom() { + $this->assertContains('mduchamp@afi-sa.fr', $this->mail->getRecipients()); } - public function testToIsAdminPortail() { - $this->assertContains('flo@afi-sa.fr', - $this->mail->getRecipients()); + /** @test */ + public function bccShouldNotContainsZork() { + $this->assertNotContains('zork', $this->mail->getRecipients()); } - public function mailRecipientSizeShouldBe3() { - $this->assertEquals(4, count($this->mail->getRecipients())); + /** @test */ + public function recipientsShouldContainsPortalAdmin() { + $this->assertContains('flo@afi-sa.fr', $this->mail->getRecipients()); } - public function testSenderIsAdminPortail() { - $this->assertEquals('flo@afi-sa.fr', $this->mail->getFrom()); + /** @test */ + public function mailShouldHave3Recipients() { + $this->assertEquals(3, count($this->mail->getRecipients())); } - public function testNewsletterLastDistributionDateIsNow() { - $this->assertEquals('23/05/2014 14:30', DateTime::createFromFormat("Y-m-d H:i:s", ($this->animations->getLastDistributionDate()))->format("d/m/Y H:i")); + /** @test */ + public function newsletterLastDistributionDateShouldBeNow() { + $this->assertEquals('23/05/2014 14:30', + DateTime::createFromFormat('Y-m-d H:i:s', + ($this->animations->getLastDistributionDate())) + ->format("d/m/Y H:i")); } } -class NewsletterMailingConcertsTestPanier extends NewsletterMailingTestCase { - public function setUp() { - parent::setup(); - $this->animations->generateMails(20); - $this->mail = $this->mock_transport->sent_mail; +class NewsletterMailingConcertsPanierTextTest extends NewsletterMailingTestCase { + /** @test */ + public function shouldContainsMillenium() { + $this->assertBodyTextContains("Les hommes qui n'aimaient pas les femmes (Stieg Larsson, 2005)"); } - public function assertMIMEPartContains($needle, $text) { - $decoded_text = quoted_printable_decode($text->getContent()); - parent::assertContains($needle, $decoded_text, - $needle.' not found in '.$decoded_text); - } - - public function assertBodyHTMLContains($needle) { - $this->assertMIMEPartContains($needle, - $this->mail->getBodyHTML()); + /** @test */ + public function shouldContainsResumeMillenium() { + $this->assertBodyTextContains('Polard du nord'); } - public function assertBodyTextContains($needle) { - $this->assertMIMEPartContains($needle, - $this->mail->getBodyText()); - } - public function testBodyTextContainsMillenium() { - $this->assertBodyTextContains("Les hommes qui n'aimaient pas les femmes (Stieg Larsson, 2005)"); - } - - public function testBodyTextContainsResumeMillenium() { - $this->assertBodyTextContains("Polard du nord"); + /** @test */ + public function shouldContainsURLMillenium() { + $this->assertBodyTextContains('http://localhost' . BASE_URL . '/recherche/viewnotice/id/345'); } - public function testBodyTextContainsURLMillenium() { - $this->assertBodyTextContains("http://localhost" . BASE_URL . "/recherche/viewnotice/id/345"); - } - public function testBodyTextContainsPotter() { + /** @test */ + public function shouldContainsPotter() { $this->assertBodyTextContains("Harry Potter à l'école des sorciers (J.K. Rowling, 1998)"); } - public function testBodyTextContainsResumePotter() { + + /** @test */ + public function shouldContainsResumePotter() { $this->assertBodyTextContains("L'histoire d'un sorcier"); } - public function testBodyTextContainsURLPotter() { - $this->assertBodyTextContains("http://localhost" . BASE_URL . "/recherche/viewnotice/id/987"); + + /** @test */ + public function shouldContainsURLPotter() { + $this->assertBodyTextContains('http://localhost' . BASE_URL . "/recherche/viewnotice/id/987"); } +} + + + - public function testVignetteMilleniumInHTML() { +class NewsletterMailingConcertsPanierHtmlTest extends NewsletterMailingTestCase { + /** @test */ + public function shouldContainsVignetteMillenium() { $this->assertBodyHTMLContains('<img src="http://afi-sa.fr/millenium.png"'); } - public function testLinkMillenium() { + + /** @test */ + public function shouldContainsLinkMillenium() { $this->assertBodyHTMLContains('<a href="http://localhost' . BASE_URL . '/recherche/viewnotice?id=345"'); } - public function testBodyHTMLContainsPotter() { + + /** @test */ + public function shouldContainsPotter() { $this->assertBodyHTMLContains("Harry Potter à l'école des sorciers (J.K. Rowling, 1998)"); } - public function testBodyHTMLContainsResumePotter() { + + /** @test */ + public function shouldContainsResumePotter() { $this->assertBodyHTMLContains("L'histoire d'un sorcier..."); } - public function testVignettePotterInHTML() { + + /** @test */ + public function shouldContainsVignettePotter() { $this->assertBodyHTMLContains('<img src="http://afi-sa.fr/potter.gif"'); } - public function testLinkPotter() { + + /** @test */ + public function shouldContainsPotterLink() { $this->assertBodyHTMLContains('<a href="http://localhost' . BASE_URL . '/recherche/viewnotice?id=987"'); } } -?> \ No newline at end of file + + + +/** @see http://forge.afi-sa.fr/issues/18661 */ +class NewsletterMailingDedupTest extends ModelTestCase { + protected + $_fetch_users_calls = 0, + $_letter; + + public function setUp() { + parent::setUp(); + Storm_Model_Loader::defaultToVolatile(); + + $this->mock_transport = new MockMailTransport(); + Zend_Mail::setDefaultTransport($this->mock_transport); + + $time_source = new TimeSourceForTest('2014-05-23 14:30:00'); + Class_Newsletter::setTimeSource($time_source); + + Class_Systeme_TimeLimit::setInstance($this->mock() + ->whenCalled('set')->with(30)->answers(null) + ->whenCalled('reset')->answers(null) + ->beStrict()); + + $this->alcor = $this->fixture('Class_Users', + ['id' => 120, + 'login' => 'alc', + 'password' => 'or', + 'mail' => 'procyon@centre-de-recherche.fr']); + + $this->actarus = $this->fixture('Class_Users', + ['id' => 121, + 'login' => 'acta', + 'password' => 'rus', + 'mail' => 'procyon@centre-de-recherche.fr']); + + $this->_letter = $this->fixture('Class_Newsletter', + ['id' => 23, + 'titre' => 'Alerte vega', + 'id_panier' => 0, + 'id_catalogue' => 0, + 'expediteur' => 'professeur@centre-de-recherche.fr', + 'contenu' => 'Golgoth aperçu azimut 234.53', + 'last_distribution_date' => '', + 'users' => [$this->alcor, $this->actarus]]); + + Zend_Registry::set('sql', $sql = $this->mock()); + $sql->whenCalled('execute') + ->with('update newsletters_users set send=false where newsletter_id=23') + ->answers(null) + + ->beStrict(); + + $this->onLoaderOfModel('Class_Users'); + } + + + public function tearDown() { + Storm_Model_Loader::defaultToDb(); + parent::tearDown(); + } + + + /** @test */ + public function procyonInOnePageShouldNotReceiveTwoMails() { + $this->expectUserFetchAndDo( + function() { + $this->_fetch_users_calls++; + if (1 == $this->_fetch_users_calls) + return [$this->alcor, $this->actarus]; + + return []; + }); + + $this->_letter->generateMails(20); + + $this->assertProcyonNotDuplicatedIn($this->mock_transport->getSentMails()); + } + + + /** @test */ + public function procyonInTwoPagesShouldNotReceiveTwoMails() { + $this->expectUserFetchAndDo( + function() { + $this->_fetch_users_calls++; + if (1 == $this->_fetch_users_calls) + return [$this->alcor]; + + if (2 == $this->_fetch_users_calls) + return [$this->actarus]; + + return []; + }); + + $this->_letter->generateMails(20); + + $this->assertProcyonNotDuplicatedIn($this->mock_transport->getSentMails()); + } + + + protected function assertProcyonNotDuplicatedIn($mails) { + $sent = 0; + foreach ($mails as $mail) + foreach ($mail->getHeaders()['Bcc'] as $recipient) + if ('<procyon@centre-de-recherche.fr>' === $recipient) + $sent++; + + $this->assertEquals(1, $sent); + } + + + protected function expectUserFetchAndDo($closure) { + Class_Users::whenCalled('findAll') + ->with('select bib_admin_users.* from bib_admin_users join newsletters_users on bib_admin_users.id_user = newsletters_users.user_id where newsletter_id = 23 and newsletters_users.send is false order by bib_admin_users.mail limit 20') + ->willDo($closure); + } +} \ No newline at end of file