def test_file_expiry(tmpdir): """ API: AttachFile Expiry """ path = join(TEST_VAR_DIR, 'apprise-test.gif') image = tmpdir.mkdir("apprise_file").join("test.jpg") with open(path, 'rb') as data: image.write(data) aa = AppriseAttachment.instantiate(str(image), cache=30) # Our file is now available assert aa.exists() # Our second call has the file already downloaded, but now compares # it's date against when we consider it to have expire. We're well # under 30 seconds here (our set value), so this will succeed assert aa.exists() with mock.patch('time.time', return_value=time.time() + 31): # This will force a re-download as our cache will have # expired assert aa.exists() with mock.patch('time.time', side_effect=OSError): # We will throw an exception assert aa.exists()
def test_plugin_pushover_attachments(mock_post, tmpdir): """ NotifyPushover() Attachment Checks """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Initialize some generic (but valid) tokens user_key = 'u' * 30 api_token = 'a' * 30 # Prepare a good response response = mock.Mock() response.content = dumps( {"status": 1, "request": "647d2300-702c-4b38-8b2f-d56326ae460b"}) response.status_code = requests.codes.ok # Prepare a bad response bad_response = mock.Mock() response.content = dumps( {"status": 1, "request": "647d2300-702c-4b38-8b2f-d56326ae460b"}) bad_response.status_code = requests.codes.internal_server_error # Assign our good response mock_post.return_value = response # prepare our attachment attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # Instantiate our object obj = Apprise.instantiate( 'pover://{}@{}/'.format(user_key, api_token)) assert isinstance(obj, plugins.NotifyPushover) # Test our attachment assert obj.notify(body="test", attach=attach) is True # Test our call count assert mock_post.call_count == 1 assert mock_post.call_args_list[0][0][0] == \ 'https://api.pushover.net/1/messages.json' # Reset our mock object for multiple tests mock_post.reset_mock() # Test multiple attachments assert attach.add(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) assert obj.notify(body="test", attach=attach) is True # Test our call count assert mock_post.call_count == 2 assert mock_post.call_args_list[0][0][0] == \ 'https://api.pushover.net/1/messages.json' assert mock_post.call_args_list[1][0][0] == \ 'https://api.pushover.net/1/messages.json' # Reset our mock object for multiple tests mock_post.reset_mock() image = tmpdir.mkdir("pover_image").join("test.jpg") image.write('a' * plugins.NotifyPushover.attach_max_size_bytes) attach = AppriseAttachment.instantiate(str(image)) assert obj.notify(body="test", attach=attach) is True # Test our call count assert mock_post.call_count == 1 assert mock_post.call_args_list[0][0][0] == \ 'https://api.pushover.net/1/messages.json' # Reset our mock object for multiple tests mock_post.reset_mock() # Add 1 more byte to the file (putting it over the limit) image.write('a' * (plugins.NotifyPushover.attach_max_size_bytes + 1)) attach = AppriseAttachment.instantiate(str(image)) assert obj.notify(body="test", attach=attach) is False # Test our call count assert mock_post.call_count == 0 # Test case when file is missing attach = AppriseAttachment.instantiate( 'file://{}?cache=False'.format(str(image))) os.unlink(str(image)) assert obj.notify( body='body', title='title', attach=attach) is False # Test our call count assert mock_post.call_count == 0 # Test unsuported files: image = tmpdir.mkdir("pover_unsupported").join("test.doc") image.write('a' * 256) attach = AppriseAttachment.instantiate(str(image)) # Content is silently ignored assert obj.notify(body="test", attach=attach) is True # prepare our attachment attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) # Throw an exception on the first call to requests.post() for side_effect in (requests.RequestException(), OSError(), bad_response): mock_post.side_effect = [side_effect, side_effect] # We'll fail now because of our error handling assert obj.send(body="test", attach=attach) is False # Same case without an attachment assert obj.send(body="test") is False
def test_attach_http(mock_get): """ API: AttachHTTP() object """ # Define our good:// url class GoodNotification(NotifyBase): def __init__(self, *args, **kwargs): super(GoodNotification, self).__init__(*args, **kwargs) def notify(self, *args, **kwargs): # Pretend everything is okay return True def url(self): # Support url() function return '' # Store our good notification in our schema map SCHEMA_MAP['good'] = GoodNotification # Temporary path path = join(TEST_VAR_DIR, 'apprise-test.gif') class DummyResponse(object): """ A dummy response used to manage our object """ status_code = requests.codes.ok headers = { 'Content-Length': getsize(path), 'Content-Type': 'image/gif', } # Pointer to file ptr = None # used to return random keep-alive chunks _keepalive_chunk_ref = 0 def close(self): return def iter_content(self, chunk_size=1024): """Lazy function (generator) to read a file piece by piece. Default chunk size: 1k.""" while True: self._keepalive_chunk_ref += 1 if 16 % self._keepalive_chunk_ref == 0: # Yield a keep-alive block yield '' data = self.ptr.read(chunk_size) if not data: break yield data def raise_for_status(self): return def __enter__(self): self.ptr = open(path, 'rb') return self def __exit__(self, *args, **kwargs): self.ptr.close() # Prepare Mock dummy_response = DummyResponse() mock_get.return_value = dummy_response # Test custom url get parameters results = AttachHTTP.parse_url( 'http://*****:*****@localhost/apprise.gif?dl=1&cache=300') assert isinstance(results, dict) attachment = AttachHTTP(**results) assert isinstance(attachment.url(), six.string_types) is True # Test that our extended variables are passed along assert mock_get.call_count == 0 assert attachment assert mock_get.call_count == 1 assert 'params' in mock_get.call_args_list[0][1] assert 'dl' in mock_get.call_args_list[0][1]['params'] # Verify that arguments that are reserved for apprise are not # passed along assert 'cache' not in mock_get.call_args_list[0][1]['params'] results = AttachHTTP.parse_url( 'http://*****:*****@localhost/apprise.gif?+key=value&cache=True') assert isinstance(results, dict) attachment = AttachHTTP(**results) assert isinstance(attachment.url(), six.string_types) is True # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', attachment.url()) is None assert re.search(r'[?&]name=', attachment.url()) is None # No Content-Disposition; so we use filename from path assert attachment.name == 'apprise.gif' assert attachment.mimetype == 'image/gif' results = AttachHTTP.parse_url( 'http://*****:*****@localhost/ignore-filename.gif') assert isinstance(results, dict) attachment = AttachHTTP(**results) assert isinstance(attachment.url(), six.string_types) is True # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', attachment.url()) is None assert re.search(r'[?&]name=', attachment.url()) is None assert attachment.mimetype == 'image/gif' # Because we could determine our mime type, we could build an extension # for our unknown filename assert attachment.name == 'myimage.gif' assert attachment assert len(attachment) == getsize(path) # Similar to test above except we make our max message size just 1 byte # smaller then our gif file. This will cause us to fail to read the # attachment AttachHTTP.max_file_size = getsize(path) - 1 results = AttachHTTP.parse_url('http://localhost/toobig.jpg') assert isinstance(results, dict) attachment = AttachHTTP(**results) # we can not download this attachment assert not attachment assert isinstance(attachment.url(), six.string_types) is True # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', attachment.url()) is None assert re.search(r'[?&]name=', attachment.url()) is None assert attachment.mimetype is None assert attachment.name is None assert len(attachment) == 0 # Disable our file size limitations AttachHTTP.max_file_size = 0 results = AttachHTTP.parse_url('http://user@localhost') assert isinstance(results, dict) attachment = AttachHTTP(**results) assert isinstance(attachment.url(), six.string_types) is True # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', attachment.url()) is None assert re.search(r'[?&]name=', attachment.url()) is None assert attachment.mimetype == 'image/gif' # Because we could determine our mime type, we could build an extension # for our unknown filename assert attachment.name == 'myimage.gif' assert attachment assert len(attachment) == getsize(path) # Set our header up with an invalid Content-Length; we can still process # this data. It just means we track it lower when reading back content dummy_response.headers = {'Content-Length': 'invalid'} results = AttachHTTP.parse_url('http://localhost/invalid-length.gif') assert isinstance(results, dict) attachment = AttachHTTP(**results) assert isinstance(attachment.url(), six.string_types) is True # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', attachment.url()) is None assert re.search(r'[?&]name=', attachment.url()) is None assert attachment.mimetype == 'image/gif' # Because we could determine our mime type, we could build an extension # for our unknown filename assert attachment.name == 'invalid-length.gif' assert attachment # Give ourselves nothing to work with dummy_response.headers = {} results = AttachHTTP.parse_url('http://user@localhost') assert isinstance(results, dict) attachment = AttachHTTP(**results) # we can not download this attachment assert attachment assert isinstance(attachment.url(), six.string_types) is True # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', attachment.url()) is None assert re.search(r'[?&]name=', attachment.url()) is None # Handle edge-case where detected_name is None for whatever reason attachment.detected_name = None assert attachment.mimetype == attachment.unknown_mimetype assert attachment.name.startswith(AttachHTTP.unknown_filename) assert len(attachment) == getsize(path) # Exception handling mock_get.return_value = None for _exception in REQUEST_EXCEPTIONS: aa = AppriseAttachment.instantiate( 'http://localhost/exception.gif?cache=30') assert isinstance(aa, AttachHTTP) mock_get.side_effect = _exception assert not aa # Restore value AttachHTTP.max_file_size = max_file_size
def test_attach_file(): """ API: AttachFile() """ # Simple gif test path = join(TEST_VAR_DIR, 'apprise-test.gif') response = AppriseAttachment.instantiate(path) assert isinstance(response, AttachFile) assert response.path == path assert response.name == 'apprise-test.gif' assert response.mimetype == 'image/gif' # Download is successful and has already been called by now; below pulls # results from cache assert response.download() assert response.url().startswith('file://{}'.format(path)) # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', response.url()) is None assert re.search(r'[?&]name=', response.url()) is None # File handling (even if image is set to maxium allowable) response = AppriseAttachment.instantiate(path) assert isinstance(response, AttachFile) with mock.patch('os.path.getsize', return_value=AttachBase.max_file_size): # It will still work assert response.path == path # File handling when size is to large response = AppriseAttachment.instantiate(path) assert isinstance(response, AttachFile) with mock.patch('os.path.getsize', return_value=AttachBase.max_file_size + 1): # We can't work in this case assert response.path is None # File handling when image is not available response = AppriseAttachment.instantiate(path) assert isinstance(response, AttachFile) with mock.patch('os.path.isfile', return_value=False): # This triggers a full check and will fail the isfile() check assert response.path is None # The call to AttachBase.path automatically triggers a call to download() # but this same is done with a call to AttachBase.name as well. Above # test cases reference 'path' right after instantiation; here we reference # 'name' response = AppriseAttachment.instantiate(path) assert response.name == 'apprise-test.gif' assert response.path == path assert response.mimetype == 'image/gif' # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', response.url()) is None assert re.search(r'[?&]name=', response.url()) is None # continuation to cheking 'name' instead of 'path' first where our call # to download() fails response = AppriseAttachment.instantiate(path) assert isinstance(response, AttachFile) with mock.patch('os.path.isfile', return_value=False): # This triggers a full check and will fail the isfile() check assert response.name is None # The call to AttachBase.path automatically triggers a call to download() # but this same is done with a call to AttachBase.mimetype as well. Above # test cases reference 'path' right after instantiation; here we reference # 'mimetype' response = AppriseAttachment.instantiate(path) assert response.mimetype == 'image/gif' assert response.name == 'apprise-test.gif' assert response.path == path # No mime-type and/or filename over-ride was specified, so therefore it # won't show up in the generated URL assert re.search(r'[?&]mime=', response.url()) is None assert re.search(r'[?&]name=', response.url()) is None # continuation to cheking 'name' instead of 'path' first where our call # to download() fails response = AppriseAttachment.instantiate(path) assert isinstance(response, AttachFile) with mock.patch('os.path.isfile', return_value=False): # download() fails so we don't have a mimetpe assert response.mimetype is None assert response.name is None assert response.path is None # This triggers a full check and will fail the isfile() check # Force a mime-type and new name response = AppriseAttachment.instantiate( 'file://{}?mime={}&name={}'.format(path, 'image/jpeg', 'test.jpeg')) assert isinstance(response, AttachFile) assert response.path == path assert response.name == 'test.jpeg' assert response.mimetype == 'image/jpeg' # We will match on mime type now (%2F = /) assert re.search(r'[?&]mime=image%2Fjpeg', response.url(), re.I) assert re.search(r'[?&]name=test\.jpeg', response.url(), re.I)
def test_pushover_attachments(mock_post, tmpdir): """ API: NotifyPushover() Attachment Checks """ # Disable Throttling to speed testing plugins.NotifyBase.request_rate_per_sec = 0 # Initialize some generic (but valid) tokens user_key = 'u' * 30 api_token = 'a' * 30 # Prepare Mock return object response = mock.Mock() response.content = dumps({ "status": 1, "request": "647d2300-702c-4b38-8b2f-d56326ae460b" }) 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')) # Instantiate our object obj = Apprise.instantiate('pover://{}@{}/'.format(user_key, api_token)) assert isinstance(obj, plugins.NotifyPushover) # Test our attachment assert obj.notify(body="test", attach=attach) is True # Test multiple attachments assert attach.add(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) assert obj.notify(body="test", attach=attach) is True image = tmpdir.mkdir("pover_image").join("test.jpg") image.write('a' * plugins.NotifyPushover.attach_max_size_bytes) attach = AppriseAttachment.instantiate(str(image)) assert obj.notify(body="test", attach=attach) is True # Add 1 more byte to the file (putting it over the limit) image.write('a' * (plugins.NotifyPushover.attach_max_size_bytes + 1)) attach = AppriseAttachment.instantiate(str(image)) assert obj.notify(body="test", attach=attach) is False # Test case when file is missing attach = AppriseAttachment.instantiate('file://{}?cache=False'.format( str(image))) os.unlink(str(image)) assert obj.notify(body='body', title='title', attach=attach) is False # Test unsuported files: image = tmpdir.mkdir("pover_unsupported").join("test.doc") image.write('a' * 256) attach = AppriseAttachment.instantiate(str(image)) # Content is silently ignored assert obj.notify(body="test", attach=attach) is True # Throw an exception on the second call to requests.post() mock_post.side_effect = OSError() # prepare our attachment attach = AppriseAttachment(os.path.join(TEST_VAR_DIR, 'apprise-test.gif')) assert obj.notify(body="test", attach=attach) is False