def test_smtplib_send_okay(mock_smtplib): """ API: Test a successfully sent email """ # Disable Throttling to speed testing plugins.NotifyBase.NotifyBase.request_rate_per_sec = 0 # Defaults to HTML obj = Apprise.instantiate('mailto://*****:*****@gmail.com', suppress_exceptions=False) assert (isinstance(obj, plugins.NotifyEmail)) # Support an email simulation where we can correctly quit mock_smtplib.starttls.return_value = True mock_smtplib.login.return_value = True mock_smtplib.sendmail.return_value = True mock_smtplib.quit.return_value = True assert (obj.notify(body='body', title='test', notify_type=NotifyType.INFO) is True) # Set Text obj = Apprise.instantiate('mailto://*****:*****@gmail.com?format=text', suppress_exceptions=False) assert (isinstance(obj, plugins.NotifyEmail)) assert (obj.notify(body='body', title='test', notify_type=NotifyType.INFO) is True)
def main(): """ Loops over all the Swarm services, checking if they need updates. :raises Exception when Docker Engine is not in Swarm Mode """ update_delay = getenv('UPDATE_DELAY', '300') notification_url = getenv('NOTIFICATION_URL', '') try: client = docker.from_env() except ConnectionError: logger.error( 'Could not connect to Docker Engine. Check https://git.io/JJujV for possible solutions' ) return logger.info('Started checking for updates') apprise = Apprise() if len(notification_url) > 0: # Add notification provider from URL if provided apprise.add(notification_url) if not is_swarm_manager(client): raise Exception('Docker Engine is not in Swarm Mode') while True: update_services(client, apprise) time.sleep(float(update_delay))
def test_twist_plugin_init(): """ API: NotifyTwist init() """ try: plugins.NotifyTwist(email='invalid', targets=None) assert False except TypeError: # Invalid email address assert True try: plugins.NotifyTwist(email='user@domain', targets=None) assert False except TypeError: # No password was specified assert True # Simple object initialization result = plugins.NotifyTwist(password='******', email='*****@*****.**', targets=None) assert result.user == 'user' assert result.host == 'domain.com' assert result.password == 'abc123' # Channel Instantiation by name obj = Apprise.instantiate('twist://*****:*****@example.com/#Channel') assert isinstance(obj, plugins.NotifyTwist) # Channel Instantiation by id (faster if you know the translation) obj = Apprise.instantiate('twist://*****:*****@example.com/12345') assert isinstance(obj, plugins.NotifyTwist) # Invalid Channel - (max characters is 64), the below drops it obj = Apprise.instantiate('twist://*****:*****@example.com/{}'.format( 'a' * 65)) assert isinstance(obj, plugins.NotifyTwist) # No User detect result = plugins.NotifyTwist.parse_url('twist://example.com') assert result is None # test usage of to= result = plugins.NotifyTwist.parse_url( 'twist://*****:*****@example.com?to=#channel') assert isinstance(result, dict) assert 'user' in result assert result['user'] == 'user' assert 'host' in result assert result['host'] == 'example.com' assert 'password' in result assert result['password'] == 'password' assert 'targets' in result assert isinstance(result['targets'], list) is True assert len(result['targets']) == 1 assert '#channel' in result['targets']
def test_fcm_plugin(mock_post): """ API: NotifyFCM() General Checks """ # Valid Keyfile path = os.path.join(PRIVATE_KEYFILE_DIR, 'service_account.json') # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Prepare a good response response = mock.Mock() response.content = json.dumps({ "access_token": "ya29.c.abcd", "expires_in": 3599, "token_type": "Bearer", }) response.status_code = requests.codes.ok mock_post.return_value = response # Test having a valid keyfile, but not a valid project id match obj = Apprise.instantiate( 'fcm://invalid_project_id/device/?keyfile={}'.format(str(path))) # we'll fail as a result assert obj.notify("test") is False # Test our call count assert mock_post.call_count == 0 # Now we test using a valid Project ID but we can't open our file obj = Apprise.instantiate( 'fcm://mock-project-id/device/?keyfile={}'.format(str(path))) with mock.patch('io.open', side_effect=OSError): # we'll fail as a result assert obj.notify("test") is False # Test our call count assert mock_post.call_count == 0 # Now we test using a valid Project ID obj = Apprise.instantiate( 'fcm://mock-project-id/device/#topic/?keyfile={}'.format(str(path))) # we'll fail as a result assert obj.notify("test") is True # Test our call count assert mock_post.call_count == 3 assert mock_post.call_args_list[0][0][0] == \ 'https://accounts.google.com/o/oauth2/token' assert mock_post.call_args_list[1][0][0] == \ 'https://fcm.googleapis.com/v1/projects/mock-project-id/messages:send' assert mock_post.call_args_list[2][0][0] == \ 'https://fcm.googleapis.com/v1/projects/mock-project-id/messages:send'
def run_server(apprise: Apprise): apprise.notify("Starting notification service") public_ip = "" while True: current_ip = get_public_ip() if public_ip != current_ip: apprise.notify(f"Public IP changed to '{current_ip}'") public_ip = current_ip sleep(CHECK_INTERVAL_IN_SECONDS)
def update_services(client: DockerClient, apprise: Apprise): """ Update all the services found on the Docker Swarm. :param client: Docker Client that is connected to a Docker Swarm Manager :param apprise: Apprise notification service """ services = client.services.list() logger.info(f'Checking for updates on {len(services)} service(s).') for service in services: name = service.name outdated, tag, digest = is_service_outdated(client, service) if outdated: update_message = f'Found update for `{tag}`, updating.' mode = service.attrs['Spec']['Mode'] replicated = 'Replicated' in mode if replicated: replicas = mode['Replicated']['Replicas'] plural = 's' if replicas > 1 else '' update_message = f"Found update for `{tag}`, updating {replicas} replica{plural}." apprise.notify(title=f'Service: `{name}`', body=update_message, notify_type=NotifyType.INFO) logger.info( f'Found update for service \'{name}\', updating using image {tag}' ) start = time.time() full_image = f"{tag}@{digest}" service.update(image=full_image, force_update=True) # Update the service end = time.time() elapsed = str(( end - start))[:4] # Calculate the time it took to update the service logger.info( f'Update for service \'{name}\' successful, took {elapsed} seconds ({full_image})' ) success_message = f'Update successful. Took {elapsed} seconds.' apprise.notify(title=f'Service: `{name}`', body=success_message, notify_type=NotifyType.SUCCESS) if getenv('PARALLEL_UPDATES', '').lower() in ['false', 'no', 'off', '0']: # When there are more images to update in one pass then update them one by one return else: logger.debug(f'No update found for service \'{name}\'')
def test_object_parsing(): """ API: NotifySNS Plugin() Object Parsing """ # Create our object a = Apprise() # Now test failing variations of our URL assert a.add('sns://') is False assert a.add('sns://nosecret') is False assert a.add('sns://nosecret/noregion/') is False # This is valid but without valid recipients; while it's still a valid URL # it won't do much when the user goes to send a notification assert a.add('sns://norecipient/norecipient/us-west-2') is True assert len(a) == 1 # Parse a good one assert a.add('sns://oh/yeah/us-west-2/abcdtopic/+12223334444') is True assert len(a) == 2 assert a.add('sns://oh/yeah/us-west-2/12223334444') is True assert len(a) == 3
def test_plugin_homeassistant_general(mock_post): """ NotifyHomeAssistant() General Checks """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 response = mock.Mock() response.content = '' response.status_code = requests.codes.ok # Prepare Mock mock_post.return_value = response # Variation Initializations obj = Apprise.instantiate('hassio://localhost/accesstoken') assert isinstance(obj, plugins.NotifyHomeAssistant) is True assert isinstance(obj.url(), six.string_types) is True # Send Notification assert obj.send(body="test") is True assert mock_post.call_count == 1 assert mock_post.call_args_list[0][0][0] == \ 'http://localhost:8123/api/services/persistent_notification/create'
def test_smtplib_init_fail(mock_smtplib): """ API: Test exception handling when calling smtplib.SMTP() """ from apprise.plugins import NotifyEmailBase obj = Apprise.instantiate('mailto://*****:*****@gmail.com', suppress_exceptions=False) assert (isinstance(obj, plugins.NotifyEmail)) # Support Exception handling of smtplib.SMTP mock_smtplib.side_effect = TypeError('Test') try: obj.notify(title='test', body='body', notify_type=NotifyType.INFO) # We should have thrown an exception assert False except TypeError: # Exception thrown as expected assert True except Exception: # Un-Expected assert False # A handled and expected exception mock_smtplib.side_effect = smtplib.SMTPException('Test') assert obj.notify(title='test', body='body', notify_type=NotifyType.INFO) is False
def test_discord_attachments(mock_post): """ API: NotifyDiscord() Attachment Checks """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Initialize some generic (but valid) tokens webhook_id = 'C' * 24 webhook_token = 'D' * 64 # Prepare Mock return object response = mock.Mock() response.status_code = requests.codes.ok # Throw an exception on the second call to requests.post() mock_post.side_effect = [response, OSError()] # Test our markdown obj = Apprise.instantiate('discord://{}/{}/?format=markdown'.format( webhook_id, webhook_token)) # attach our content attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # We'll fail now because of an internal exception assert obj.send(body="test", attach=attach) is False
def test_email_url_escaping(): """ API: Test that user/passwords are properly escaped from URL """ # quote(' %20') passwd = '%20%2520' # Basically we want to check that ' ' equates to %20 and % equates to %25 # So the above translates to ' %20' (a space in front of %20). We want # to verify the handling of the password escaping and when it happens. # a very bad response would be ' ' (double space) obj = plugins.NotifyEmail.parse_url( 'mailto://*****:*****@gmail.com?format=text'.format(passwd)) assert isinstance(obj, dict) is True assert 'password' in obj # Escaping doesn't happen at this stage because we want to leave this to # the plugins discretion assert obj.get('password') == '%20%2520' obj = Apprise.instantiate( 'mailto://*****:*****@gmail.com?format=text'.format(passwd), suppress_exceptions=False) assert isinstance(obj, plugins.NotifyEmail) is True # The password is escapped 'once' at this point assert obj.password == ' %20'
def test_webbase_lookup(mock_smtp, mock_smtpssl): """ API: Web Based Lookup Tests """ # Insert a test email at the head of our table NotifyEmailBase.WEBBASE_LOOKUP_TABLE = ( ( # Testing URL 'Testing Lookup', re.compile(r'^(?P<id>[^@]+)@(?P<domain>l2g\.com)$', re.I), { 'port': 123, 'smtp_host': 'smtp.l2g.com', 'secure': True, 'login_type': (NotifyEmailBase.WebBaseLogin.USERID, ) }, ), ) + NotifyEmailBase.WEBBASE_LOOKUP_TABLE obj = Apprise.instantiate('mailto://*****:*****@l2g.com', suppress_exceptions=True) assert (isinstance(obj, plugins.NotifyEmail)) assert obj.to_addr == '*****@*****.**' assert obj.from_addr == '*****@*****.**' assert obj.password == 'pass' assert obj.user == 'user' assert obj.secure is True assert obj.port == 123 assert obj.smtp_host == 'smtp.l2g.com'
def test_apprise_schemas(tmpdir): """ API: Apprise().schema() tests """ # Caling load matix a second time which is an internal function causes it # to skip over content already loaded into our matrix and thefore accesses # other if/else parts of the code that aren't otherwise called __load_matrix() a = Apprise() # no items assert len(a) == 0 class TextNotification(NotifyBase): # set our default notification format notify_format = NotifyFormat.TEXT # Garbage Protocol Entries protocol = None secure_protocol = (None, object) class HtmlNotification(NotifyBase): protocol = ('html', 'htm') secure_protocol = ('htmls', 'htms') class MarkDownNotification(NotifyBase): protocol = 'markdown' secure_protocol = 'markdowns' # Store our notifications into our schema map SCHEMA_MAP['text'] = TextNotification SCHEMA_MAP['html'] = HtmlNotification SCHEMA_MAP['markdown'] = MarkDownNotification schemas = URLBase.schemas(TextNotification) assert isinstance(schemas, set) is True # We didn't define a protocol or secure protocol assert len(schemas) == 0 schemas = URLBase.schemas(HtmlNotification) assert isinstance(schemas, set) is True assert len(schemas) == 4 assert 'html' in schemas assert 'htm' in schemas assert 'htmls' in schemas assert 'htms' in schemas # Invalid entries do not disrupt schema calls for garbage in (object(), None, 42): schemas = URLBase.schemas(garbage) assert isinstance(schemas, set) is True assert len(schemas) == 0
def test_config_base_parse_yaml_file03(tmpdir): """ API: ConfigBase.parse_yaml_file (#3) """ t = tmpdir.mkdir("bad-first-entry").join("apprise.yml") # The first entry is -tag and not <dash><space>tag # The element is therefore not picked up; This causes us to display # some warning messages to the screen complaining of this typo yet # still allowing us to load the URL since it is valid t.write("""urls: - pover://nsisxnvnqixq39t0cw54pxieyvtdd9@2jevtmstfg5a7hfxndiybasttxxfku: -tag: test1 - pover://rg8ta87qngcrkc6t4qbykxktou0uug@tqs3i88xlufexwl8t4asglt4zp5wfn: - tag: test2 - pover://jcqgnlyq2oetea4qg3iunahj8d5ijm@evalvutkhc8ipmz2lcgc70wtsm0qpb: - tag: test3""") # Create ourselves a config object ac = AppriseConfig(paths=str(t)) # The number of configuration files that exist assert len(ac) == 1 # no notifications lines processed is 3 assert len(ac.servers()) == 3 # Test our ability to add Config objects to our apprise object a = Apprise() # Add our configuration object assert a.add(servers=ac) is True # Detect our 3 entry as they should have loaded successfully assert len(a) == 3 # No match assert sum(1 for _ in a.find('no-match')) == 0 # Match everything assert sum(1 for _ in a.find('all')) == 3 # No match for bad entry assert sum(1 for _ in a.find('test1')) == 0 # Match test2 entry assert sum(1 for _ in a.find('test2')) == 1 # Match test3 entry assert sum(1 for _ in a.find('test3')) == 1 # Match test1 or test3 entry; (only matches test3) assert sum(1 for _ in a.find('test1, test3')) == 1
def test_apprise_details(): """ API: Apprise() Details """ # Caling load matix a second time which is an internal function causes it # to skip over content already loaded into our matrix and thefore accesses # other if/else parts of the code that aren't otherwise called __load_matrix() a = Apprise() # Details object details = a.details() # Dictionary response assert isinstance(details, dict) # Apprise version assert 'version' in details assert details.get('version') == __version__ # Defined schemas identify each plugin assert 'schemas' in details assert isinstance(details.get('schemas'), list) # We have an entry per defined plugin assert 'asset' in details assert isinstance(details.get('asset'), dict) assert 'app_id' in details['asset'] assert 'app_desc' in details['asset'] assert 'default_extension' in details['asset'] assert 'theme' in details['asset'] assert 'image_path_mask' in details['asset'] assert 'image_url_mask' in details['asset'] assert 'image_url_logo' in details['asset'] # All plugins must have a name defined; the below generates # a list of entrys that do not have a string defined. assert (not len([ x['service_name'] for x in details['schemas'] if not isinstance(x['service_name'], six.string_types) ]))
def test_plugin_fcm_cryptography_import_error(): """ NotifySimplePush() Cryptography loading failure """ # Attempt to instantiate our object obj = Apprise.instantiate('spush://{}'.format('Y' * 14)) # It's not possible because our cryptography depedancy is missing assert obj is None
def test_plugin_signal_edge_cases(mock_post): """ NotifySignalAPI() Edge Cases """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Prepare our response response = requests.Request() response.status_code = requests.codes.ok # Prepare Mock mock_post.return_value = response # Initialize some generic (but valid) tokens source = '+1 (555) 123-3456' target = '+1 (555) 987-5432' body = "test body" title = "My Title" # No apikey specified with pytest.raises(TypeError): plugins.NotifySignalAPI(source=None) aobj = Apprise() assert aobj.add("signals://*****:*****@localhost:231/{}/{}?status=True".format( source, target)) assert aobj.notify(title=title, body=body) assert mock_post.call_count == 1 details = mock_post.call_args_list[0] assert details[0][0] == 'https://localhost:231/v2/send' payload = loads(details[1]['data']) # Status flag is set assert payload['message'] == '[i] My Title\r\ntest body'
def test_plugin(mock_oauth, mock_api): """ API: NotifyTwitter Plugin() (pt1) """ # iterate over our dictionary and test it out for (url, meta) in TEST_URLS: # Our expected instance instance = meta.get('instance', None) # Our expected server objects self = meta.get('self', None) # Our expected Query response (True, False, or exception type) response = meta.get('response', True) # Allow us to force the server response code to be something other then # the defaults response = meta.get( 'response', True if response else False) try: obj = Apprise.instantiate(url, suppress_exceptions=False) if instance is None: # Check that we got what we came for assert obj is instance continue assert(isinstance(obj, instance)) if self: # Iterate over our expected entries inside of our object for key, val in self.items(): # Test that our object has the desired key assert(hasattr(key, obj)) assert(getattr(key, obj) == val) # check that we're as expected assert obj.notify( title='test', body='body', notify_type=NotifyType.INFO) == response except AssertionError: # Don't mess with these entries raise except Exception as e: # Handle our exception assert(instance is not None) assert(isinstance(e, instance))
def test_email_dict_variations(): """ API: Test email dictionary variations to ensure parsing is correct """ # Test variations of username required to be an email address # [email protected] obj = Apprise.instantiate({ 'schema': 'mailto', 'user': '******', 'password': '******', 'host': 'example.com'}, suppress_exceptions=False) assert isinstance(obj, plugins.NotifyEmail) is True
def test_webbase_lookup(mock_smtp, mock_smtpssl): """ API: Web Based Lookup Tests """ # Insert a test email at the head of our table NotifyEmailBase.EMAIL_TEMPLATES = ( ( # Testing URL 'Testing Lookup', re.compile(r'^(?P<id>[^@]+)@(?P<domain>l2g\.com)$', re.I), { 'port': 123, 'smtp_host': 'smtp.l2g.com', 'secure': True, 'login_type': (NotifyEmailBase.WebBaseLogin.USERID, ) }, ), ) + NotifyEmailBase.EMAIL_TEMPLATES obj = Apprise.instantiate( 'mailto://*****:*****@l2g.com', suppress_exceptions=True) assert isinstance(obj, plugins.NotifyEmail) assert len(obj.targets) == 1 assert (False, '*****@*****.**') in obj.targets assert obj.from_addr == '*****@*****.**' assert obj.password == 'pass' assert obj.user == 'user' assert obj.secure is True assert obj.port == 123 assert obj.smtp_host == 'smtp.l2g.com' # We get the same results if an email is identified as the username # because the USERID variable forces that we can't use an email obj = Apprise.instantiate( 'mailto://*****:*****@[email protected]', suppress_exceptions=True) assert obj.user == 'user'
def test_config_base_parse_yaml_file02(tmpdir): """ API: ConfigBase.parse_yaml_file (#2) """ t = tmpdir.mkdir("matching-tags").join("apprise.yml") t.write("""urls: - pover://nsisxnvnqixq39t0cw54pxieyvtdd9@2jevtmstfg5a7hfxndiybasttxxfku: - tag: test1 - pover://rg8ta87qngcrkc6t4qbykxktou0uug@tqs3i88xlufexwl8t4asglt4zp5wfn: - tag: test2 - pover://jcqgnlyq2oetea4qg3iunahj8d5ijm@evalvutkhc8ipmz2lcgc70wtsm0qpb: - tag: test3""") # Create ourselves a config object ac = AppriseConfig(paths=str(t)) # The number of configuration files that exist assert len(ac) == 1 # no notifications are loaded assert len(ac.servers()) == 3 # Test our ability to add Config objects to our apprise object a = Apprise() # Add our configuration object assert a.add(servers=ac) is True # Detect our 3 entry as they should have loaded successfully assert len(a) == 3 # No match assert sum(1 for _ in a.find('no-match')) == 0 # Match everything assert sum(1 for _ in a.find('all')) == 3 # Match test1 entry assert sum(1 for _ in a.find('test1')) == 1 # Match test2 entry assert sum(1 for _ in a.find('test2')) == 1 # Match test3 entry assert sum(1 for _ in a.find('test3')) == 1 # Match test1 or test3 entry assert sum(1 for _ in a.find('test1, test3')) == 2
def test_discord_attachments(mock_post): """ API: NotifyDiscord() Attachment Checks """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Initialize some generic (but valid) tokens webhook_id = 'C' * 24 webhook_token = 'D' * 64 # Prepare Mock return object mock_post.return_value = requests.Request() mock_post.return_value.status_code = requests.codes.ok # Test our markdown obj = Apprise.instantiate('discord://{}/{}/?format=markdown'.format( webhook_id, webhook_token)) # attach our content attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) assert obj.notify( body='body', title='title', notify_type=NotifyType.INFO, attach=attach) is True # An invalid attachment will cause a failure path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg') attach = AppriseAttachment(path) assert obj.notify( body='body', title='title', notify_type=NotifyType.INFO, attach=path) is False # Throw an exception on the second call to requests.post() mock_post.return_value = None response = mock.Mock() response.status_code = requests.codes.ok mock_post.side_effect = [response, OSError()] # update our attachment to be valid attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # Test our markdown # We'll fail now because of an internal exception assert obj.send(body="test", attach=attach) is False
def test_plugin_ses_attachments(mock_post): """ NotifySES() Attachment Checks """ # Disable Throttling to speed testing plugins.NotifySES.request_rate_per_sec = 0 # Prepare Mock return object response = mock.Mock() response.content = AWS_SES_GOOD_RESPONSE response.status_code = requests.codes.ok mock_post.return_value = response # prepare our attachment attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # Test our markdown obj = Apprise.instantiate('ses://%s/%s/%s/%s/' % ('*****@*****.**', TEST_ACCESS_KEY_ID, TEST_ACCESS_KEY_SECRET, TEST_REGION)) # Send a good attachment assert obj.notify(body="test", attach=attach) is True # Reset our mock object mock_post.reset_mock() # Add another attachment so we drop into the area of the PushBullet code # that sends remaining attachments (if more detected) attach.add(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # Send our attachments assert obj.notify(body="test", attach=attach) is True # Test our call count assert mock_post.call_count == 1 # Reset our mock object mock_post.reset_mock() # An invalid attachment will cause a failure path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg') attach = AppriseAttachment(path) assert obj.notify(body="test", attach=attach) is False
def send_notification(message, subject): apprise_client = Apprise() if APPRISE_CONFIG_STRING: apprise_client.add(APPRISE_CONFIG_STRING) if APPRISE_CONFIG_URL: config = AppriseConfig() config.add(APPRISE_CONFIG_URL) apprise_client.add(config) res = apprise_client.notify(body=message, title=subject) print(res) if not res: print('Failed to send notification') else: print('Successfully sent notification') return res
def test_plugin_simplepush_general(mock_post): """ NotifySimplePush() General Tests """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Prepare a good response response = mock.Mock() response.content = json.dumps({ 'status': 'OK', }) response.status_code = requests.codes.ok mock_post.return_value = response obj = Apprise.instantiate('spush://{}'.format('Y' * 14)) # Verify our content works as expected assert obj.notify(title="test", body="test") is True
def test_smtplib_send_okay(mock_smtplib): """ API: Test a successfully sent email """ from apprise.plugins import NotifyEmailBase obj = Apprise.instantiate('mailto://*****:*****@gmail.com', suppress_exceptions=False) assert (isinstance(obj, plugins.NotifyEmail)) # Support an email simulation where we can correctly quit mock_smtplib.starttls.return_value = True mock_smtplib.login.return_value = True mock_smtplib.sendmail.return_value = True mock_smtplib.quit.return_value = True obj.notify(title='test', body='body', notify_type=NotifyType.INFO)
def test_smtplib_init_fail(mock_smtplib): """ API: Test exception handling when calling smtplib.SMTP() """ # Disable Throttling to speed testing plugins.NotifyBase.NotifyBase.request_rate_per_sec = 0 obj = Apprise.instantiate('mailto://*****:*****@gmail.com', suppress_exceptions=False) assert (isinstance(obj, plugins.NotifyEmail)) # Support Exception handling of smtplib.SMTP mock_smtplib.side_effect = RuntimeError('Test') assert obj.notify(body='body', title='test', notify_type=NotifyType.INFO) is False # A handled and expected exception mock_smtplib.side_effect = smtplib.SMTPException('Test') assert obj.notify(body='body', title='test', notify_type=NotifyType.INFO) is False
def test_object_parsing(): """ API: NotifySNS Plugin() Object Parsing """ # Create our object a = Apprise() # Now test failing variations of our URL assert(a.add('sns://') is False) assert(a.add('sns://nosecret') is False) assert(a.add('sns://nosecret/noregion/') is False) # This is valid, but a rather useless URL; there is nothing to notify assert(a.add('sns://norecipient/norecipient/us-west-2') is True) assert(len(a) == 1) # Parse a good one assert(a.add('sns://oh/yeah/us-west-2/abcdtopic/+12223334444') is True) assert(len(a) == 2) assert(a.add('sns://oh/yeah/us-west-2/12223334444') is True) assert(len(a) == 3)
def test_object_parsing(): """ API: NotifySNS Plugin() Object Parsing """ # Create our object a = Apprise() # Now test failing variations of our URL assert a.add('sns://') is False assert a.add('sns://nosecret') is False assert a.add('sns://nosecret/noregion/') is False # This is valid but without valid recipients, the URL is actually useless assert a.add('sns://norecipient/norecipient/us-west-2') is False assert len(a) == 0 # Parse a good one assert a.add('sns://oh/yeah/us-west-2/abcdtopic/+12223334444') is True assert len(a) == 1 assert a.add('sns://oh/yeah/us-west-2/12223334444') is True assert len(a) == 2
def test_pushbullet_attachments(mock_post): """ API: NotifyPushBullet() Attachment Checks """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Initialize some generic (but valid) tokens access_token = 't' * 32 # Prepare Mock return object response = mock.Mock() response.content = dumps({ "file_name": "cat.jpg", "file_type": "image/jpeg", "file_url": "https://dl.pushb.com/abc/cat.jpg", "upload_url": "https://upload.pushbullet.com/abcd123", }) response.status_code = requests.codes.ok mock_post.return_value = response # prepare our attachment attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # Test our markdown obj = Apprise.instantiate( 'pbul://{}/?format=markdown'.format(access_token)) # Send a good attachment assert obj.notify(body="test", attach=attach) is True # Add another attachment so we drop into the area of the PushBullet code # that sends remaining attachments (if more detected) attach.add(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # Send our attachments assert obj.notify(body="test", attach=attach) is True # An invalid attachment will cause a failure path = os.path.join(TEST_VAR_DIR, '/invalid/path/to/an/invalid/file.jpg') attach = AppriseAttachment(path) assert obj.notify(body="test", attach=attach) is False # Throw an exception on the first call to requests.post() mock_post.return_value = None mock_post.side_effect = requests.RequestException() # We'll fail now because of an internal exception assert obj.send(body="test", attach=attach) is False # Throw an exception on the second call to requests.post() mock_post.side_effect = [response, OSError()] # We'll fail now because of an internal exception assert obj.send(body="test", attach=attach) is False # Throw an exception on the third call to requests.post() mock_post.side_effect = [ response, response, requests.RequestException()] # We'll fail now because of an internal exception assert obj.send(body="test", attach=attach) is False # Throw an exception on the forth call to requests.post() mock_post.side_effect = [ response, response, response, requests.RequestException()] # We'll fail now because of an internal exception assert obj.send(body="test", attach=attach) is False # Test case where we don't get a valid response back response.content = '}' mock_post.side_effect = response # We'll fail because of an invalid json object assert obj.send(body="test", attach=attach) is False # # Test bad responses # # Prepare a bad response response.content = dumps({ "file_name": "cat.jpg", "file_type": "image/jpeg", "file_url": "https://dl.pushb.com/abc/cat.jpg", "upload_url": "https://upload.pushbullet.com/abcd123", }) bad_response = mock.Mock() bad_response.content = response.content bad_response.status_code = 400 # Throw an exception on the third call to requests.post() mock_post.return_value = bad_response # prepare our attachment attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # We'll fail now because we were unable to send the attachment assert obj.send(body="test", attach=attach) is False # Throw an exception on the second call mock_post.side_effect = [response, bad_response, response] assert obj.send(body="test", attach=attach) is False # Throw an OSError mock_post.side_effect = [response, OSError()] assert obj.send(body="test", attach=attach) is False # Throw an exception on the third call mock_post.side_effect = [response, response, bad_response] assert obj.send(body="test", attach=attach) is False # Throw an exception on the fourth call mock_post.side_effect = [response, response, response, bad_response] assert obj.send(body="test", attach=attach) is False # A good message mock_post.side_effect = [response, response, response, response] assert obj.send(body="test", attach=attach) is True
def notify(self, servers, body, title, notify_type=NotifyType.INFO, body_format=NotifyFormat.MARKDOWN): """ processes list of servers specified """ # Decode our data body = decode(body) title = decode(title) # Apprise Asset Object asset = AppriseAsset(theme=self.default_theme) asset.app_id = 'NZB-Notify' asset.app_desc = 'NZB Notification' asset.app_url = 'https://github.com/caronc/nzb-notify' # Source Theme from GitHub Page asset.image_url_mask = 'https://raw.githubusercontent.com' \ '/caronc/nzb-notify/master/Notify' \ '/apprise-theme/{THEME}/apprise-{TYPE}-{XY}.png' asset.image_path_mask = join( dirname(__file__), 'Notify', 'apprise-theme', '{THEME}', 'apprise-{TYPE}-{XY}.png') # Include Image Flag _url = self.parse_url(self.get('IncludeImage')) # Define some globals to use in this function image_path = None image_url = None if _url: # Toggle our include image flag right away to True include_image = True # Get some more details if not re.match('^(https?|file)$', _url['schema'], re.IGNORECASE): self.logger.error( 'An invalid image url protocol (%s://) was specified.' % _url['schema'], ) return False if _url['schema'] == 'file': if not isfile(_url['fullpath']): self.logger.error( 'The specified file %s was not found.' % _url['fullpath'], ) return False image_path = _url['fullpath'] else: # We're dealing with a web request image_url = _url['url'] else: # Dealing with the old way of doing things; just toggling a # true/false flag include_image = self.parse_bool(self.get('IncludeImage'), False) if isinstance(servers, basestring): # servers can be a list of URLs, or it can be # a string which will be parsed into this list # we wanted. servers = self.parse_list(self.get('Servers', '')) # Create our apprise object a = Apprise(asset=asset) for server in servers: # Add our URL if not a.add(server): # Validation Failure self.logger.error( 'Could not initialize %s instance.' % server, ) continue # Notify our servers a.notify(body=body, title=title, notify_type=notify_type, body_format=body_format) # Always return true return True