def test_sending_many_emails(self, exacttarget_mock): """Test that we can send emails to a lot of users in the same run. """ # First add a lot of emails. now = utc_now() - datetime.timedelta(minutes=30) config_manager = self._setup_storage_config() with config_manager.context() as config: storage = ElasticSearchCrashStorage(config) for i in range(21): storage.save_processed({ 'uuid': 'fake-%s' % i, 'email': '*****@*****.**' % i, 'product': 'WaterWolf', 'version': '20.0', 'release_channel': 'Release', 'date_processed': now, }) storage.es.refresh() config_manager = self._setup_simple_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') job.run(utc_now()) et_mock = exacttarget_mock.return_value # Verify that we have the default 4 results + the 21 we added. eq_(et_mock.trigger_send.call_count, 25)
def test_correct_email(self): domains = ('gmail.com', 'yahoo.com') config_manager = self._setup_simple_config(domains=domains) with config_manager.context() as config: app = automatic_emails.AutomaticEmailsCronApp(config, '') # easy corrections self.assertEqual( app.correct_email('*****@*****.**', typo_correct=True), '*****@*****.**' ) self.assertEqual( app.correct_email('*****@*****.**', typo_correct=True), '*****@*****.**' ) self.assertEqual( app.correct_email('*****@*****.**', typo_correct=True), '*****@*****.**' ) self.assertEqual( # case insensitive app.correct_email('*****@*****.**', typo_correct=True), '*****@*****.**' ) self.assertEqual( app.correct_email('*****@*****.**', typo_correct=True), '*****@*****.**' ) self.assertEqual( app.correct_email('*****@*****.**', typo_correct=True), '*****@*****.**' ) # dots here and there self.assertEqual( app.correct_email('[email protected].'), '*****@*****.**' ) self.assertEqual( app.correct_email('*****@*****.**'), '*****@*****.**' ) self.assertEqual( app.correct_email('[email protected].'), '*****@*****.**' ) # dots and typos self.assertEqual( # case insensitive app.correct_email('[email protected].', typo_correct=True), '*****@*****.**' ) # What doesn't work are edit distances greater than 1 self.assertFalse(app.correct_email('*****@*****.**')) self.assertFalse(app.correct_email('*****@*****.**')) # and don't mess with @ signs self.assertFalse( app.correct_email('[email protected]@gamil.com') )
def test_error_in_send_email_with_easy_correction(self, exacttarget_mock): attempted_emails = [] def mocked_trigger_send(email_template, fields): attempted_emails.append(fields['EMAIL_ADDRESS_']) return True exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config( common_email_domains=['example.com', 'gmail.com']) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') email = '*****@*****.**' job.send_email({ 'processed_crash.email': email, 'email_template': '', }) eq_(config.logger.error.call_count, 0) # note that this means only one attempt was made eq_(attempted_emails, ['*****@*****.**'])
def test_update_user(self): config_manager = self._setup_simple_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') now = utc_now().isoformat() es = SuperS().es( urls=config.elasticsearch.elasticsearch_urls, timeout=config.elasticsearch.elasticsearch_timeout, ) search = es.indexes( config.elasticsearch.elasticsearch_emails_index) search = search.doctypes('emails') connection = es.get_es() job.update_user('*****@*****.**', now, connection) connection.refresh() s = search.filter(_id='*****@*****.**') res = list(s.values_list('last_sending')) eq_(len(res), 1) eq_(res[0][0], now) # Test with a non-existing user job.update_user('*****@*****.**', now, connection) connection.refresh() s = search.filter(_id='*****@*****.**') res = list(s.values_list('last_sending')) eq_(len(res), 1) eq_(res[0][0], now)
def test_error_in_send_email_recovery_failing(self, exacttarget_mock): attempted_emails = [] def mocked_trigger_send(email_template, fields): # raise an error no matter what attempted_emails.append(fields['EMAIL_ADDRESS_']) raise exacttarget.NewsletterException('error') exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config( common_email_domains=['example.com', 'gmail.com']) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') email = '*****@*****.**' job.send_email({ 'processed_crash.email': email, 'email_template': '', }) eq_(config.logger.error.call_count, 1) config.logger.error.assert_called_with( 'Unable to send a corrected email to %s, error is: %s', '*****@*****.**', 'error', exc_info=True) eq_(attempted_emails, ['*****@*****.**', '*****@*****.**'])
def test_send_email_test_mode(self, exacttarget_mock): list_service_mock = exacttarget_mock.return_value.list.return_value list_service_mock.get_subscriber.return_value = { 'token': '*****@*****.**' } config_manager = self._setup_test_mode_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') email = '*****@*****.**' job.send_email({ 'processed_crash.email': email, 'email_template': 'socorro_dev_test', }) fields = { 'EMAIL_ADDRESS_': config.test_email_address, 'EMAIL_FORMAT_': 'H', 'TOKEN': '*****@*****.**' } exacttarget_mock.return_value.trigger_send.assert_called_with( 'socorro_dev_test', fields )
def test_send_email_test_mode(self, exacttarget_mock): list_service_mock = exacttarget_mock.return_value.list.return_value list_service_mock.get_subscriber.return_value = { 'token': '*****@*****.**' } config_manager = self._setup_test_mode_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') report = { 'email': '*****@*****.**', 'product': 'WaterWolf', 'version': '20.0', 'release_channel': 'Release', } job.send_email(report) fields = { 'EMAIL_ADDRESS_': config.test_email_address, 'EMAIL_FORMAT_': 'H', 'TOKEN': '*****@*****.**' } exacttarget_mock.return_value.trigger_send.assert_called_with( 'socorro_dev_test', fields)
def test_error_in_send_email_recovery_failing(self, exacttarget_mock): attempted_emails = [] def mocked_trigger_send(email_template, fields): # raise an error no matter what attempted_emails.append(fields['EMAIL_ADDRESS_']) raise exacttarget.NewsletterException('error') exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config( common_email_domains=['example.com', 'gmail.com']) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') report = { 'email': '*****@*****.**', 'product': 'WaterWolf', 'version': '20.0', 'release_channel': 'Release', } job.send_email(report) self.assertEqual(config.logger.error.call_count, 1) config.logger.error.assert_called_with( 'Unable to send a corrected email to %s, error is: %s', '*****@*****.**', 'error', exc_info=True) self.assertEqual(attempted_emails, ['*****@*****.**', '*****@*****.**'])
def test_update_user(self): config_manager = self._setup_simple_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') now = utc_now() report = {'email': '*****@*****.**'} job.update_user(report, now, self.conn) cursor = self.conn.cursor() cursor.execute( """ SELECT last_sending FROM emails WHERE email=%(email)s """, report) self.assertEqual(cursor.rowcount, 1) row = cursor.fetchone() self.assertEqual(row[0], now) # Test with a non-existing user report = {'email': '*****@*****.**'} job.update_user(report, now, self.conn) cursor = self.conn.cursor() cursor.execute( """ SELECT last_sending FROM emails WHERE email=%(email)s """, report) self.assertEqual(cursor.rowcount, 1) row = cursor.fetchone() self.assertEqual(row[0], now)
def test_error_in_send_email_with_clever_recovery(self, exacttarget_mock): attempted_emails = [] def mocked_trigger_send(email_template, fields): attempted_emails.append(fields['EMAIL_ADDRESS_']) if fields['EMAIL_ADDRESS_'].endswith('exampl.com'): raise exacttarget.NewsletterException('error') else: return True exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config( common_email_domains=['example.com', 'gmail.com']) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') report = { 'email': '*****@*****.**', 'product': 'WaterWolf', 'version': '20.0', 'release_channel': 'Release', } job.send_email(report) self.assertEqual(config.logger.error.call_count, 0) self.assertEqual(attempted_emails, ['*****@*****.**', '*****@*****.**'])
def test_error_in_send_email_with_easy_correction(self, exacttarget_mock): attempted_emails = [] def mocked_trigger_send(email_template, fields): attempted_emails.append(fields['EMAIL_ADDRESS_']) return True exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config( common_email_domains=['example.com', 'gmail.com']) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') report = { 'email': '[email protected].', 'product': 'WaterWolf', 'version': '20.0', 'release_channel': 'Release', } job.send_email(report) self.assertEqual(config.logger.error.call_count, 0) # note that this means only one attempt was made self.assertEqual(attempted_emails, ['*****@*****.**'])
def test_error_in_send_email_with_clever_recovery(self, exacttarget_mock): attempted_emails = [] def mocked_trigger_send(email_template, fields): attempted_emails.append(fields['EMAIL_ADDRESS_']) if fields['EMAIL_ADDRESS_'].endswith('exampl.com'): raise exacttarget.NewsletterException('error') else: return True exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config( common_email_domains=['example.com', 'gmail.com']) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') email = '*****@*****.**' job.send_email({ 'processed_crash.email': email, 'email_template': '', }) eq_(config.logger.error.call_count, 0) eq_(attempted_emails, ['*****@*****.**', '*****@*****.**'])
def test_run(self, exacttarget_mock): config_manager = self._setup_simple_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') job.run(utc_now()) et_mock = exacttarget_mock.return_value eq_(et_mock.trigger_send.call_count, 4)
def test_correct_ambiguous_email(self): domains = ('gmail.com', 'yahoo.com', 'mail.com') config_manager = self._setup_simple_config(domains=domains) with config_manager.context() as config: app = automatic_emails.AutomaticEmailsCronApp(config, '') # because 'gmail.com' and 'mail.com' is so similar, # we don't want correction of 'mail.com' to incorrectly # become 'gmail.com' eq_(app.correct_email('*****@*****.**', typo_correct=True), None) eq_(app.correct_email('*****@*****.**', typo_correct=True), None)
def test_run_no_generic_email(self, exacttarget_mock): config_manager = self._setup_simple_config(email_template='') with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') job.run(utc_now()) et_mock = exacttarget_mock.return_value # Only 1 crash has a valid classification, the others would # be sent the default email and are thus not processed. eq_(et_mock.trigger_send.call_count, 1) config_manager = self._setup_simple_config( email_template='', restrict_products=['NightlyTrain']) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') job.run(utc_now()) et_mock = exacttarget_mock.return_value # None have a valid classification, all would be sent the default # email and are thus not processed. The call count stays the same. eq_(et_mock.trigger_send.call_count, 1)
def test_run_with_classifications(self, exacttarget_mock): # Verify that classifications work. def mocked_trigger_send(email_template, fields): if fields['EMAIL_ADDRESS_'] == '*****@*****.**': eq_(email_template, 'socorro_bitguard_en') else: eq_(email_template, 'socorro_dev_test') exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') job.run(utc_now()) et_mock = exacttarget_mock.return_value eq_(et_mock.trigger_send.call_count, 4)
def test_error_in_send_email_with_ambiguous_domain(self, exacttarget_mock): """try to send to a mail.com but let it fail. Because `mail.com` easily is spell corrected to `gmail.com` but we add `mail.com` as a common email domain in the config. """ attempted_emails = [] def mocked_trigger_send(email_template, fields): # raise an error no matter what attempted_emails.append(fields['EMAIL_ADDRESS_']) raise exacttarget.NewsletterException('error') if fields['EMAIL_ADDRESS_'].endswith('@mail.com.'): raise exacttarget.NewsletterException('error') else: return True exacttarget_mock.return_value.trigger_send.side_effect = \ mocked_trigger_send config_manager = self._setup_simple_config( common_email_domains=['example.com', 'gmail.com', 'mail.com'] ) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') email = '*****@*****.**' job.send_email({ 'processed_crash.email': email, 'email_template': '', }) self.assertEqual(config.logger.error.call_count, 1) config.logger.error.assert_called_with( 'Unable to send an email to %s, error is: %s', email, 'error', exc_info=True ) self.assertEqual( attempted_emails, [email] )
def test_error_in_send_email(self, exacttarget_mock): list_service_mock = exacttarget_mock.return_value.list.return_value list_service_mock.get_subscriber.return_value = { 'token': '*****@*****.**' } exacttarget_mock.return_value.trigger_send.side_effect = ( exacttarget.NewsletterException('error')) config_manager = self._setup_simple_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') report = { 'email': '*****@*****.**', 'product': 'WaterWolf', 'version': '20.0', 'release_channel': 'Release', } job.send_email(report) fields = { 'EMAIL_ADDRESS_': '*****@*****.**', 'EMAIL_FORMAT_': 'H', 'TOKEN': '*****@*****.**' } exacttarget_mock.return_value.trigger_send.assert_called_with( 'socorro_dev_test', fields) self.assertEqual(config.logger.error.call_count, 1) config.logger.error.assert_called_with( 'Unable to send an email to %s, error is: %s', '*****@*****.**', 'error', exc_info=True) list_service = exacttarget_mock.return_value.list.return_value list_service.get_subscriber.side_effect = (Exception( 404, 'Bad Request')) exacttarget_mock.return_value.trigger_send.side_effect = (Exception( 404, 'Bad Request')) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') report = { 'email': '*****@*****.**', 'product': 'WaterWolf', 'version': '20.0', 'release_channel': 'Release', } job.send_email(report) fields = { 'EMAIL_ADDRESS_': u'*****@*****.**', 'EMAIL_FORMAT_': 'H', 'TOKEN': u'*****@*****.**' } exacttarget_mock.return_value.trigger_send.assert_called_with( 'socorro_dev_test', fields) self.assertEqual(config.logger.error.call_count, 2) config.logger.error.assert_called_with( 'Unable to send an email to %s, fields are %s, error is: %s', u'*****@*****.**', str(fields), "(404, 'Bad Request')", exc_info=True)
def test_error_in_send_email(self, exacttarget_mock): list_service_mock = exacttarget_mock.return_value.list.return_value list_service_mock.get_subscriber.return_value = { 'token': '*****@*****.**' } exacttarget_mock.return_value.trigger_send.side_effect = ( exacttarget.NewsletterException('error')) config_manager = self._setup_simple_config() with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') email = '*****@*****.**' job.send_email({ 'processed_crash.email': email, 'email_template': 'socorro_dev_test', }) fields = { 'EMAIL_ADDRESS_': email, 'EMAIL_FORMAT_': 'H', 'TOKEN': email } exacttarget_mock.return_value.trigger_send.assert_called_with( 'socorro_dev_test', fields) eq_(config.logger.error.call_count, 1) config.logger.error.assert_called_with( 'Unable to send an email to %s, error is: %s', email, 'error', exc_info=True) list_service = exacttarget_mock.return_value.list.return_value list_service.get_subscriber.side_effect = (Exception( 404, 'Bad Request')) exacttarget_mock.return_value.trigger_send.side_effect = (Exception( 404, 'Bad Request')) with config_manager.context() as config: job = automatic_emails.AutomaticEmailsCronApp(config, '') email = '*****@*****.**' job.send_email({ 'processed_crash.email': email, 'email_template': 'socorro_dev_test', }) fields = { 'EMAIL_ADDRESS_': '*****@*****.**', 'EMAIL_FORMAT_': 'H', 'TOKEN': '*****@*****.**' } exacttarget_mock.return_value.trigger_send.assert_called_with( 'socorro_dev_test', fields) eq_(config.logger.error.call_count, 2) config.logger.error.assert_called_with( 'Unable to send an email to %s, fields are %s, error is: %s', '*****@*****.**', str(fields), "(404, 'Bad Request')", exc_info=True)