def test_config_base_detect_config_format(): """ API: ConfigBase.detect_config_format """ # Garbage Handling for garbage in (object(), None, 42): # A response is always correctly returned assert ConfigBase.detect_config_format(garbage) is None # Empty files are valid assert ConfigBase.detect_config_format('') is ConfigFormat.TEXT # Valid Text Configuration assert ConfigBase.detect_config_format(""" # A comment line over top of a URL mailto://userb:[email protected] """) is ConfigFormat.TEXT # A text file that has semi-colon as comment characters # is valid too assert ConfigBase.detect_config_format(""" ; A comment line over top of a URL mailto://userb:[email protected] """) is ConfigFormat.TEXT # Valid YAML Configuration assert ConfigBase.detect_config_format(""" # A comment line over top of a URL version: 1 """) is ConfigFormat.YAML # Just a whole lot of blank lines... assert ConfigBase.detect_config_format('\n\n\n') is ConfigFormat.TEXT # Invalid Config assert ConfigBase.detect_config_format("3") is None
def test_config_base(): """ API: ConfigBase() object """ # invalid types throw exceptions try: ConfigBase(**{'format': 'invalid'}) # We should never reach here as an exception should be thrown assert (False) except TypeError: assert (True) # Config format types are not the same as ConfigBase ones try: ConfigBase(**{'format': 'markdown'}) # We should never reach here as an exception should be thrown assert (False) except TypeError: assert (True) cb = ConfigBase(**{'format': 'yaml'}) assert isinstance(cb, ConfigBase) cb = ConfigBase(**{'format': 'text'}) assert isinstance(cb, ConfigBase) # Set encoding cb = ConfigBase(encoding='utf-8', format='text') assert isinstance(cb, ConfigBase) # read is not supported in the base object; only the children assert cb.read() is None # There are no servers loaded on a freshly created object assert len(cb.servers()) == 0 # Unsupported URLs are not parsed assert ConfigBase.parse_url(url='invalid://') is None # Valid URL & Valid Format results = ConfigBase.parse_url( url='file://relative/path?format=yaml&encoding=latin-1') assert isinstance(results, dict) # These are moved into the root assert results.get('format') == 'yaml' assert results.get('encoding') == 'latin-1' # But they also exist in the qsd location assert isinstance(results.get('qsd'), dict) assert results['qsd'].get('encoding') == 'latin-1' assert results['qsd'].get('format') == 'yaml' # Valid URL & Invalid Format results = ConfigBase.parse_url( url='file://relative/path?format=invalid&encoding=latin-1') assert isinstance(results, dict) # Only encoding is moved into the root assert 'format' not in results assert results.get('encoding') == 'latin-1' # But they will always exist in the qsd location assert isinstance(results.get('qsd'), dict) assert results['qsd'].get('encoding') == 'latin-1' assert results['qsd'].get('format') == 'invalid'
def test_config_base_config_parse_yaml(): """ API: ConfigBase.config_parse_yaml object """ # general reference used below asset = AppriseAsset() # Garbage Handling assert isinstance(ConfigBase.config_parse_yaml(object()), list) assert isinstance(ConfigBase.config_parse_yaml(None), list) assert isinstance(ConfigBase.config_parse_yaml(''), list) # Invalid Version result = ConfigBase.config_parse_yaml("version: 2a", asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid Syntax (throws a ScannerError) result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Missing url token result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # No urls defined result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid url defined result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 # Invalid URL definition; yet the answer to life at the same time urls: 43 """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid url/schema result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: - invalid:// """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid url/schema result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: - invalid://: - a: b """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid url/schema result = ConfigBase.config_parse_yaml(""" urls: - just some free text that isn't valid: - a garbage entry to go with it """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid url/schema result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: - not even a proper url """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid url/schema result = ConfigBase.config_parse_yaml(""" # no lists... just no urls: [milk, pumpkin pie, eggs, juice] """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Invalid url/schema result = ConfigBase.config_parse_yaml(""" urls: # a very invalid sns entry - sns://T1JJ3T3L2/ - sns://:@/: - invalid: test - sns://T1JJ3T3L2/: - invalid: test # some strangness - - - test """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # Valid Configuration result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 # # Define your notification urls: # urls: - pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b - mailto://test:[email protected] """, asset=asset) # We expect to parse 2 entries from the above assert isinstance(result, list) assert len(result) == 2 assert len(result[0].tags) == 0 # Valid Configuration result = ConfigBase.config_parse_yaml(""" urls: - json://localhost: - tag: my-custom-tag, my-other-tag # How to stack multiple entries: - mailto://: - user: jeff pass: 123abc from: [email protected] - user: jack pass: pass123 from: [email protected] # This is an illegal entry; the schema can not be changed schema: json # accidently left a colon at the end of the url; no problem # we'll accept it - mailto://oscar:[email protected]: # A telegram entry (returns a None in parse_url()) - tgram://invalid """, asset=asset) # We expect to parse 4 entries from the above because the tgram:// entry # would have failed to be loaded assert isinstance(result, list) assert len(result) == 4 assert len(result[0].tags) == 2 # Global Tags result = ConfigBase.config_parse_yaml(""" # Global Tags stacked as a list tag: - admin - devops urls: - json://localhost - dbus:// """, asset=asset) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 2 # all entries will have our global tags defined in them for entry in result: assert 'admin' in entry.tags assert 'devops' in entry.tags # Global Tags result = ConfigBase.config_parse_yaml(""" # Global Tags tag: admin, devops urls: # The following tags will get added to the global set - json://localhost: - tag: string-tag, my-other-tag, text # Tags can be presented in this list format too: - dbus://: - tag: - list-tag - dbus """, asset=asset) # all entries will have our global tags defined in them for entry in result: assert 'admin' in entry.tags assert 'devops' in entry.tags # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 2 # json:// has 2 globals + 3 defined assert len(result[0].tags) == 5 assert 'text' in result[0].tags # json:// has 2 globals + 2 defined assert len(result[1].tags) == 4 assert 'list-tag' in result[1].tags # An invalid set of entries result = ConfigBase.config_parse_yaml(""" urls: # The following tags will get added to the global set - json://localhost: - - - entry """, asset=asset) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 0 # An asset we'll manipulate asset = AppriseAsset() # Global Tags result = ConfigBase.config_parse_yaml(""" # Test the creation of our apprise asset object asset: app_id: AppriseTest app_desc: Apprise Test Notifications app_url: http://nuxref.com # Support setting empty values image_url_mask: image_url_logo: image_path_mask: tmp/path # invalid entry theme: - - - entry # Now for some invalid entries invalid: entry __init__: can't be over-ridden nolists: - we don't support these entries - in the apprise object urls: - json://localhost: """, asset=asset) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 1 assert asset.app_id == "AppriseTest" assert asset.app_desc == "Apprise Test Notifications" assert asset.app_url == "http://nuxref.com" # the theme was not updated and remains the same as it was assert asset.theme == AppriseAsset().theme # Empty string assignment assert isinstance(asset.image_url_mask, six.string_types) is True assert asset.image_url_mask == "" assert isinstance(asset.image_url_logo, six.string_types) is True assert asset.image_url_logo == "" # For on-lookers looking through this file; here is a perfectly formatted # YAML configuration file for your reference so you can see it without # all of the errors like the ones identified above result = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed. Thus this is a # completely optional field. It's a good idea to just add this line because it # will help with future ambiguity (if it ever occurs). version: 1 # Define an Asset object if you wish (Optional) asset: app_id: AppriseTest app_desc: Apprise Test Notifications app_url: http://nuxref.com # Optionally define some global tags to associate with ALL of your # urls below. tag: admin, devops # Define your URLs (Mandatory!) urls: # Either on-line each entry like this: - json://localhost # Or add a colon to the end of the URL where you can optionally provide # over-ride entries. One of the most likely entry to be used here # is the tag entry. This gets extended to the global tag (if defined) # above - xml://localhost: - tag: customer # The more elements you specify under a URL the more times the URL will # get replicated and used. Hence this entry actually could be considered # 2 URLs being called with just the destination email address changed: - mailto://george:[email protected]: - to: [email protected] - to: [email protected] # Again... to re-iterate, the above mailto:// would actually fire two (2) # separate emails each with a different destination address specified. # Be careful when defining your arguments and differentiating between # when to use the dash (-) and when not to. Each time you do, you will # cause another instance to be created. # Defining more then 1 element to a muti-set is easy, it looks like this: - mailto://jackson:[email protected]: - to: [email protected] tag: jeff, customer - to: [email protected] tag: chris, customer """, asset=asset) # okay, here is how we get our total based on the above (read top-down) # +1 json:// entry # +1 xml:// entry # +2 mailto:// entry to [email protected] and [email protected] # +2 mailto:// entry to [email protected] and [email protected] # = 6 assert len(result) == 6 # all six entries will have our global tags defined in them for entry in result: assert 'admin' in entry.tags assert 'devops' in entry.tags # Entries can be directly accessed as they were added # our json:// had no additional tags added; so just the global ones # So just 2; admin and devops (these were already validated above in the # for loop assert len(result[0].tags) == 2 # our xml:// object has 1 tag added (customer) assert len(result[1].tags) == 3 assert 'customer' in result[1].tags # You get the idea, here is just a direct mapping to the remaining entries # in the same order they appear above assert len(result[2].tags) == 2 assert len(result[3].tags) == 2 assert len(result[4].tags) == 4 assert 'customer' in result[4].tags assert 'jeff' in result[4].tags assert len(result[5].tags) == 4 assert 'customer' in result[5].tags assert 'chris' in result[5].tags
def test_config_base_config_parse_text(): """ API: ConfigBase.config_parse_text object """ # Garbage Handling assert isinstance(ConfigBase.config_parse_text(object()), list) assert isinstance(ConfigBase.config_parse_text(None), list) assert isinstance(ConfigBase.config_parse_text(''), list) # Valid Configuration result = ConfigBase.config_parse_text(""" # A comment line over top of a URL mailto://userb:[email protected] # A line with mulitiple tag assignments to it taga,tagb=kde:// """, asset=AppriseAsset()) # We expect to parse 2 entries from the above assert isinstance(result, list) assert len(result) == 2 assert len(result[0].tags) == 0 # Our second element will have tags associated with it assert len(result[1].tags) == 2 assert 'taga' in result[1].tags assert 'tagb' in result[1].tags # Here is a similar result set however this one has an invalid line # in it which invalidates the entire file result = ConfigBase.config_parse_text(""" # A comment line over top of a URL mailto://userc:[email protected] # A line with mulitiple tag assignments to it taga,tagb=windows:// I am an invalid line that does not follow any of the Apprise file rules! """) # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # More invalid data result = ConfigBase.config_parse_text(""" # An invalid URL invalid://user:[email protected] # A tag without a url taga= # A very poorly structured url sns://:@/ # Just 1 token provided sns://T1JJ3T3L2/ """) # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # Here is an empty file result = ConfigBase.config_parse_text('') # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0
def test_apprise_config(tmpdir): """ API: AppriseConfig basic testing """ # Create ourselves a config object ac = AppriseConfig() # There are no servers loaded assert len(ac) == 0 # Object can be directly checked as a boolean; response is False # when there are no entries loaded assert not ac # lets try anyway assert len(ac.servers()) == 0 t = tmpdir.mkdir("simple-formatting").join("apprise") t.write(""" # A comment line over top of a URL mailto://usera:[email protected] # A line with mulitiple tag assignments to it taga,tagb=gnome:// # Event if there is accidental leading spaces, this configuation # is accepting of htat and will not exclude them tagc=kde:// # A very poorly structured url sns://:@/ # Just 1 token provided causes exception sns://T1JJ3T3L2/ """) # Create ourselves a config object ac = AppriseConfig(paths=str(t)) # One configuration file should have been found assert len(ac) == 1 # Object can be directly checked as a boolean; response is True # when there is at least one entry assert ac # We should be able to read our 3 servers from that assert len(ac.servers()) == 3 # Get our URL back assert isinstance(ac[0].url(), six.string_types) # Test cases where our URL is invalid t = tmpdir.mkdir("strange-lines").join("apprise") t.write(""" # basicly this consists of defined tags and no url tag= """) # Create ourselves a config object ac = AppriseConfig(paths=str(t), asset=AppriseAsset()) # One configuration file should have been found assert len(ac) == 1 # No urls were set assert len(ac.servers()) == 0 # Create a ConfigBase object cb = ConfigBase() # Test adding of all entries assert ac.add(configs=cb, asset=AppriseAsset(), tag='test') is True # Test adding of all entries assert ac.add( configs=['file://?', ], asset=AppriseAsset(), tag='test') is False # Test the adding of garbage assert ac.add(configs=object()) is False # Try again but enforce our format ac = AppriseConfig(paths='file://{}?format=text'.format(str(t))) # One configuration file should have been found assert len(ac) == 1 # No urls were set assert len(ac.servers()) == 0 # # Test Internatialization and the handling of unicode characters # istr = """ # Iñtërnâtiônàlization Testing windows://""" if six.PY2: # decode string into unicode istr = istr.decode('utf-8') # Write our content to our file t = tmpdir.mkdir("internationalization").join("apprise") with io.open(str(t), 'wb') as f: f.write(istr.encode('latin-1')) # Create ourselves a config object ac = AppriseConfig(paths=str(t)) # One configuration file should have been found assert len(ac) == 1 # This will fail because our default encoding is utf-8; however the file # we opened was not; it was latin-1 and could not be parsed. assert len(ac.servers()) == 0 # Test iterator count = 0 for entry in ac: count += 1 assert len(ac) == count # We can fix this though; set our encoding to latin-1 ac = AppriseConfig(paths='file://{}?encoding=latin-1'.format(str(t))) # One configuration file should have been found assert len(ac) == 1 # Our URL should be found assert len(ac.servers()) == 1 # Get our URL back assert isinstance(ac[0].url(), six.string_types) # pop an entry from our list assert isinstance(ac.pop(0), ConfigBase) is True # Determine we have no more configuration entries loaded assert len(ac) == 0 # # Test buffer handling (and overflow) t = tmpdir.mkdir("buffer-handling").join("apprise") buf = "gnome://" t.write(buf) # Reset our config object ac.clear() # Create ourselves a config object ac = AppriseConfig(paths=str(t)) # update our length to be the size of our actual file ac[0].max_buffer_size = len(buf) # One configuration file should have been found assert len(ac) == 1 assert len(ac.servers()) == 1 # update our buffer size to be slightly smaller then what we allow ac[0].max_buffer_size = len(buf) - 1 # Content is automatically cached; so even though we adjusted the buffer # above, our results have been cached so we get a 1 response. assert len(ac.servers()) == 1
def test_config_base_config_parse_yaml(): """ API: ConfigBase.config_parse_yaml object """ # general reference used below asset = AppriseAsset() # Garbage Handling for garbage in (object(), None, '', 42): # A response is always correctly returned result = ConfigBase.config_parse_yaml(garbage) # response is a tuple... assert isinstance(result, tuple) # containing 2 items (plugins, config) assert len(result) == 2 # In the case of garbage in, we get garbage out; both lists are empty assert result == (list(), list()) # Invalid Version result, config = ConfigBase.config_parse_yaml("version: 2a", asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Invalid Syntax (throws a ScannerError) result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Missing url token result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # No urls defined result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Invalid url defined result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 # Invalid URL definition; yet the answer to life at the same time urls: 43 """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Invalid url/schema result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: - invalid:// """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Invalid url/schema result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: - invalid://: - a: b """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Invalid url/schema result, config = ConfigBase.config_parse_yaml(""" # Include entry with nothing associated with it include: urls: - just some free text that isn't valid: - a garbage entry to go with it """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Invalid url/schema result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 urls: - not even a proper url """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Invalid url/schema result, config = ConfigBase.config_parse_yaml(""" # no lists... just no urls: [milk, pumpkin pie, eggs, juice] # Including by list is okay include: [file:///absolute/path/, relative/path, http://test.com] """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were 3 include entries assert len(config) == 3 assert 'file:///absolute/path/' in config assert 'relative/path' in config assert 'http://test.com' in config # Invalid url/schema result, config = ConfigBase.config_parse_yaml(""" urls: # a very invalid sns entry - sns://T1JJ3T3L2/ - sns://:@/: - invalid: test - sns://T1JJ3T3L2/: - invalid: test # some strangeness - - - test """, asset=asset) # Invalid data gets us an empty result set assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # Valid Configuration result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed version: 1 # Including by dict include: # File includes - file:///absolute/path/ - relative/path # Trailing colon shouldn't disrupt include - http://test.com: # invalid (numeric) - 4 # some strangeness - - - test # # Define your notification urls: # urls: - pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b - mailto://test:[email protected] - https://apprise.ryver.com/application/webhook/ckhrjW8w672m6HG - https://not.a.native.url/ """, asset=asset) # We expect to parse 3 entries from the above # The Ryver one is in a native form and the 4th one is invalid assert isinstance(result, list) assert len(result) == 3 assert len(result[0].tags) == 0 # There were 3 include entries assert len(config) == 3 assert 'file:///absolute/path/' in config assert 'relative/path' in config assert 'http://test.com' in config # Valid Configuration result, config = ConfigBase.config_parse_yaml(""" # A single line include is supported include: http://localhost:8080/notify/apprise urls: - json://localhost: - tag: my-custom-tag, my-other-tag # How to stack multiple entries: - mailto://user:[email protected]: - to: [email protected] - to: [email protected] # This is an illegal entry; the schema can not be changed schema: json # accidently left a colon at the end of the url; no problem # we'll accept it - mailto://oscar:[email protected]: # A Ryver URL (using Native format); still accepted - https://apprise.ryver.com/application/webhook/ckhrjW8w672m6HG: # An invalid URL with colon (ignored) - https://not.a.native.url/: # A telegram entry (returns a None in parse_url()) - tgram://invalid """, asset=asset) # We expect to parse 4 entries from the above because the tgram:// entry # would have failed to be loaded assert isinstance(result, list) assert len(result) == 5 assert len(result[0].tags) == 2 # Our single line included assert len(config) == 1 assert 'http://localhost:8080/notify/apprise' in config # Global Tags result, config = ConfigBase.config_parse_yaml(""" # Global Tags stacked as a list tag: - admin - devops urls: - json://localhost - dbus:// """, asset=asset) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 2 # There were no include entries defined assert len(config) == 0 # all entries will have our global tags defined in them for entry in result: assert 'admin' in entry.tags assert 'devops' in entry.tags # Global Tags result, config = ConfigBase.config_parse_yaml(""" # Global Tags tag: admin, devops urls: # The following tags will get added to the global set - json://localhost: - tag: string-tag, my-other-tag, text # Tags can be presented in this list format too: - dbus://: - tag: - list-tag - dbus """, asset=asset) # all entries will have our global tags defined in them for entry in result: assert 'admin' in entry.tags assert 'devops' in entry.tags # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 2 # json:// has 2 globals + 3 defined assert len(result[0].tags) == 5 assert 'text' in result[0].tags # json:// has 2 globals + 2 defined assert len(result[1].tags) == 4 assert 'list-tag' in result[1].tags # There were no include entries defined assert len(config) == 0 # An invalid set of entries result, config = ConfigBase.config_parse_yaml(""" urls: # The following tags will get added to the global set - json://localhost: - - - entry """, asset=asset) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # An asset we'll manipulate asset = AppriseAsset() # Global Tags result, config = ConfigBase.config_parse_yaml(""" # Test the creation of our apprise asset object asset: app_id: AppriseTest app_desc: Apprise Test Notifications app_url: http://nuxref.com # Support setting empty values image_url_mask: image_url_logo: image_path_mask: tmp/path # invalid entry theme: - - - entry # Now for some invalid entries invalid: entry __init__: can't be over-ridden nolists: - we don't support these entries - in the apprise object urls: - json://localhost: """, asset=asset) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 1 # There were no include entries defined assert len(config) == 0 assert asset.app_id == "AppriseTest" assert asset.app_desc == "Apprise Test Notifications" assert asset.app_url == "http://nuxref.com" # the theme was not updated and remains the same as it was assert asset.theme == AppriseAsset().theme # Empty string assignment assert isinstance(asset.image_url_mask, six.string_types) is True assert asset.image_url_mask == "" assert isinstance(asset.image_url_logo, six.string_types) is True assert asset.image_url_logo == "" # For on-lookers looking through this file; here is a perfectly formatted # YAML configuration file for your reference so you can see it without # all of the errors like the ones identified above result, config = ConfigBase.config_parse_yaml(""" # if no version is specified then version 1 is presumed. Thus this is a # completely optional field. It's a good idea to just add this line because it # will help with future ambiguity (if it ever occurs). version: 1 # Define an Asset object if you wish (Optional) asset: app_id: AppriseTest app_desc: Apprise Test Notifications app_url: http://nuxref.com # Optionally define some global tags to associate with ALL of your # urls below. tag: admin, devops # Define your URLs (Mandatory!) urls: # Either on-line each entry like this: - json://localhost # Or add a colon to the end of the URL where you can optionally provide # over-ride entries. One of the most likely entry to be used here # is the tag entry. This gets extended to the global tag (if defined) # above - xml://localhost: - tag: customer # The more elements you specify under a URL the more times the URL will # get replicated and used. Hence this entry actually could be considered # 2 URLs being called with just the destination email address changed: - mailto://george:[email protected]: - to: [email protected] - to: [email protected] # Again... to re-iterate, the above mailto:// would actually fire two (2) # separate emails each with a different destination address specified. # Be careful when defining your arguments and differentiating between # when to use the dash (-) and when not to. Each time you do, you will # cause another instance to be created. # Defining more then 1 element to a muti-set is easy, it looks like this: - mailto://jackson:[email protected]: - to: [email protected] tag: jeff, customer - to: [email protected] tag: chris, customer """, asset=asset) # okay, here is how we get our total based on the above (read top-down) # +1 json:// entry # +1 xml:// entry # +2 mailto:// entry to [email protected] and [email protected] # +2 mailto:// entry to [email protected] and [email protected] # = 6 assert len(result) == 6 # all six entries will have our global tags defined in them for entry in result: assert 'admin' in entry.tags assert 'devops' in entry.tags # Entries can be directly accessed as they were added # our json:// had no additional tags added; so just the global ones # So just 2; admin and devops (these were already validated above in the # for loop assert len(result[0].tags) == 2 # our xml:// object has 1 tag added (customer) assert len(result[1].tags) == 3 assert 'customer' in result[1].tags # You get the idea, here is just a direct mapping to the remaining entries # in the same order they appear above assert len(result[2].tags) == 2 assert len(result[3].tags) == 2 assert len(result[4].tags) == 4 assert 'customer' in result[4].tags assert 'jeff' in result[4].tags assert len(result[5].tags) == 4 assert 'customer' in result[5].tags assert 'chris' in result[5].tags # There were no include entries defined assert len(config) == 0 # Valid Configuration (multi inline configuration entries) result, config = ConfigBase.config_parse_yaml(""" # A configuration file that contains 2 includes separated by a comma and/or # space: include: http://localhost:8080/notify/apprise, http://localhost/apprise/cfg """, asset=asset) # We will have loaded no results assert isinstance(result, list) assert len(result) == 0 # But our two configuration files will be present: assert len(config) == 2 assert 'http://localhost:8080/notify/apprise' in config assert 'http://localhost/apprise/cfg' in config # Valid Configuration (another way of specifying more then one include) result, config = ConfigBase.config_parse_yaml(""" # A configuration file that contains 4 includes on their own # lines beneath the keyword `include`: include: http://localhost:8080/notify/apprise http://localhost/apprise/cfg01 http://localhost/apprise/cfg02 http://localhost/apprise/cfg03 """, asset=asset) # We will have loaded no results assert isinstance(result, list) assert len(result) == 0 # But our 4 configuration files will be present: assert len(config) == 4 assert 'http://localhost:8080/notify/apprise' in config assert 'http://localhost/apprise/cfg01' in config assert 'http://localhost/apprise/cfg02' in config assert 'http://localhost/apprise/cfg03' in config # Valid Configuration (we allow comma separated entries for # each defined bullet) result, config = ConfigBase.config_parse_yaml(""" # A configuration file that contains 4 includes on their own # lines beneath the keyword `include`: include: - http://localhost:8080/notify/apprise, http://localhost/apprise/cfg01 http://localhost/apprise/cfg02 - http://localhost/apprise/cfg03 """, asset=asset) # We will have loaded no results assert isinstance(result, list) assert len(result) == 0 # But our 4 configuration files will be present: assert len(config) == 4 assert 'http://localhost:8080/notify/apprise' in config assert 'http://localhost/apprise/cfg01' in config assert 'http://localhost/apprise/cfg02' in config assert 'http://localhost/apprise/cfg03' in config
def test_config_base_config_parse_text(): """ API: ConfigBase.config_parse_text object """ # Garbage Handling for garbage in (object(), None, 42): # A response is always correctly returned result = ConfigBase.config_parse_text(garbage) # response is a tuple... assert isinstance(result, tuple) # containing 2 items (plugins, config) assert len(result) == 2 # In the case of garbage in, we get garbage out; both lists are empty assert result == (list(), list()) # Valid Configuration result, config = ConfigBase.config_parse_text(""" # A comment line over top of a URL mailto://userb:[email protected] # Test a URL using it's native format; in this case Ryver https://apprise.ryver.com/application/webhook/ckhrjW8w672m6HG # Invalid URL as it's not associated with a plugin # or a native url https://not.a.native.url/ # A line with mulitiple tag assignments to it taga,tagb=kde:// # An include statement to Apprise API with trailing spaces: include http://localhost:8080/notify/apprise # A relative include statement (with trailing spaces) include apprise.cfg """, asset=AppriseAsset()) # We expect to parse 3 entries from the above assert isinstance(result, list) assert isinstance(config, list) assert len(result) == 3 assert len(result[0].tags) == 0 # Our last element will have 2 tags associated with it assert len(result[-1].tags) == 2 assert 'taga' in result[-1].tags assert 'tagb' in result[-1].tags assert len(config) == 2 assert 'http://localhost:8080/notify/apprise' in config assert 'apprise.cfg' in config # Here is a similar result set however this one has an invalid line # in it which invalidates the entire file result, config = ConfigBase.config_parse_text(""" # A comment line over top of a URL mailto://userc:[email protected] # A line with mulitiple tag assignments to it taga,tagb=windows:// I am an invalid line that does not follow any of the Apprise file rules! """) # We expect to parse 0 entries from the above because the invalid line # invalidates the entire configuration file. This is for security reasons; # we don't want to point at files load content in them just because they # resemble an Apprise configuration. assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0 # More invalid data result, config = ConfigBase.config_parse_text(""" # An invalid URL invalid://user:[email protected] # A tag without a url taga= # A very poorly structured url sns://:@/ # Just 1 token provided sns://T1JJ3T3L2/ # Even with the above invalid entries, we can still # have valid include lines include file:///etc/apprise.cfg # An invalid include (nothing specified afterwards) include # An include of a config type we don't support include invalid:// """) # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # There was 1 valid entry assert len(config) == 0 # Test case where a comment is on it's own line with nothing else result, config = ConfigBase.config_parse_text("#") # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # There were no include entries defined assert len(config) == 0
def test_config_base_config_parse(): """ API: ConfigBase.config_parse """ # Garbage Handling for garbage in (object(), None, 42): # A response is always correctly returned result = ConfigBase.config_parse(garbage) # response is a tuple... assert isinstance(result, tuple) # containing 2 items (plugins, config) assert len(result) == 2 # In the case of garbage in, we get garbage out; both lists are empty assert result == (list(), list()) # Valid Text Configuration result = ConfigBase.config_parse(""" # A comment line over top of a URL mailto://userb:[email protected] """, asset=AppriseAsset()) # We expect to parse 1 entry from the above assert isinstance(result, tuple) assert len(result) == 2 # The first element is the number of notification services processed assert len(result[0]) == 1 # If we index into the item, we can check to see the tags associate # with it assert len(result[0][0].tags) == 0 # The second is the number of configuration include lines parsed assert len(result[1]) == 0 # Valid Configuration result = ConfigBase.config_parse(""" # if no version is specified then version 1 is presumed version: 1 # # Define your notification urls: # urls: - pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b - mailto://test:[email protected] - syslog://: - tag: devops, admin """, asset=AppriseAsset()) # We expect to parse 3 entries from the above assert isinstance(result, tuple) assert len(result) == 2 assert isinstance(result[0], list) assert len(result[0]) == 3 assert len(result[0][0].tags) == 0 assert len(result[0][1].tags) == 0 assert len(result[0][2].tags) == 2 # Test case where we pass in a bad format result = ConfigBase.config_parse(""" ; A comment line over top of a URL mailto://userb:[email protected] """, config_format='invalid-format') # This is not parseable despite the valid text assert isinstance(result, tuple) assert isinstance(result[0], list) assert len(result[0]) == 0 result, _ = ConfigBase.config_parse(""" ; A comment line over top of a URL mailto://userb:[email protected] """, config_format=ConfigFormat.TEXT) # Parseable assert isinstance(result, list) assert len(result) == 1
def test_config_base_config_parse_text(): """ API: ConfigBase.config_parse_text object """ # Garbage Handling assert isinstance(ConfigBase.config_parse_text(object()), list) assert isinstance(ConfigBase.config_parse_text(None), list) assert isinstance(ConfigBase.config_parse_text(''), list) # Valid Configuration result = ConfigBase.config_parse_text(""" # A comment line over top of a URL mailto://userb:[email protected] # Test a URL using it's native format; in this case Ryver https://apprise.ryver.com/application/webhook/ckhrjW8w672m6HG # Invalid URL as it's not associated with a plugin # or a native url https://not.a.native.url/ # A line with mulitiple tag assignments to it taga,tagb=kde:// """, asset=AppriseAsset()) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 3 assert len(result[0].tags) == 0 # Our last element will have 2 tags associated with it assert len(result[-1].tags) == 2 assert 'taga' in result[-1].tags assert 'tagb' in result[-1].tags # Here is a similar result set however this one has an invalid line # in it which invalidates the entire file result = ConfigBase.config_parse_text(""" # A comment line over top of a URL mailto://userc:[email protected] # A line with mulitiple tag assignments to it taga,tagb=windows:// I am an invalid line that does not follow any of the Apprise file rules! """) # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # More invalid data result = ConfigBase.config_parse_text(""" # An invalid URL invalid://user:[email protected] # A tag without a url taga= # A very poorly structured url sns://:@/ # Just 1 token provided sns://T1JJ3T3L2/ """) # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # Here is an empty file result = ConfigBase.config_parse_text('') # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # Test case where a comment is on it's own line with nothing else result = ConfigBase.config_parse_text("#") # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0 # Test case of empty file result = ConfigBase.config_parse_text("") # We expect to parse 0 entries from the above assert isinstance(result, list) assert len(result) == 0
def parse_url(url, *args, **kwargs): # always parseable return ConfigBase.parse_url(url, verify_host=False)
def test_apprise_config_template_parse(tmpdir): """ API: AppriseConfig parsing of templates """ # Create ourselves a config object ac = AppriseConfig() t = tmpdir.mkdir("template-testing").join("apprise.yml") t.write(""" tag: - company # A comment line over top of a URL urls: - mailto://user:[email protected]: - to: [email protected] cc: [email protected] - to: [email protected] tag: co-worker """) # Create ourselves a config object ac = AppriseConfig(paths=str(t)) # 2 emails to be sent assert len(ac.servers()) == 2 # The below checks are very customized for NotifyMail but just # test that the content got passed correctly assert (False, '*****@*****.**') in ac[0][0].targets assert '*****@*****.**' in ac[0][0].cc assert 'company' in ac[0][1].tags assert (False, '*****@*****.**') in ac[0][1].targets assert 'company' in ac[0][1].tags assert 'co-worker' in ac[0][1].tags # # Specifically test _special_token_handler() # tokens = { # This maps to itself (bcc); no change here 'bcc': '*****@*****.**', # This should get mapped to 'targets' 'to': '*****@*****.**', # white space and tab is intentionally added to the end to verify we # do not play/tamper with information 'targets': '[email protected], [email protected] \t', # If the end user provides a configuration for data we simply don't use # this isn't a proble... we simply don't touch it either; we leave it # as is. 'ignore': 'not-used' } result = ConfigBase._special_token_handler('mailto', tokens) # to gets mapped to targets assert 'to' not in result # bcc is allowed here assert 'bcc' in result assert 'targets' in result # Not used, but also not touched; this entry should still be in our result # set assert 'ignore' in result # We'll concatinate all of our targets together assert len(result['targets']) == 2 assert '*****@*****.**' in result['targets'] # Content is passed as is assert '[email protected], [email protected] \t' in result['targets'] # We re-do the simmiar test above. The very key difference is the # `targets` is a list already (it's expected type) so `to` can properly be # concatinated into the list vs the above (which tries to correct the # situation) tokens = { # This maps to itself (bcc); no change here 'bcc': '*****@*****.**', # This should get mapped to 'targets' 'to': '*****@*****.**', # similar to the above test except targets is now a proper # dictionary allowing the `to` (when translated to `targets`) to get # appended to it 'targets': ['*****@*****.**', '*****@*****.**'], # If the end user provides a configuration for data we simply don't use # this isn't a proble... we simply don't touch it either; we leave it # as is. 'ignore': 'not-used' } result = ConfigBase._special_token_handler('mailto', tokens) # to gets mapped to targets assert 'to' not in result # bcc is allowed here assert 'bcc' in result assert 'targets' in result # Not used, but also not touched; this entry should still be in our result # set assert 'ignore' in result # Now we'll see the new user added as expected (concatinated into our list) assert len(result['targets']) == 3 assert '*****@*****.**' in result['targets'] assert '*****@*****.**' in result['targets'] assert '*****@*****.**' in result['targets'] # Test providing a list t.write(""" # A comment line over top of a URL urls: - mailtos://user:[email protected]: - smtp: smtp3-dev.google.gmail.com to: - John Smith <*****@*****.**> - Jason Tater <*****@*****.**> - [email protected] - to: Henry Fisher <*****@*****.**>, Jason Archie <*****@*****.**> smtp_host: smtp5-dev.google.gmail.com tag: drinking-buddy # provide case where the URL includes some input too # In both of these cases, the cc and targets (to) get over-ridden # by values below - mailtos://user:[email protected]/[email protected]/[email protected]/: to: - [email protected] cc: - [email protected] - sinch://: - spi: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa token: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb # Test a case where we expect a string, but yaml reads it in as # a number from: 10005243890 to: +1(123)555-1234 """) # Create ourselves a config object ac = AppriseConfig(paths=str(t)) # 2 emails to be sent and 1 Sinch service call assert len(ac.servers()) == 4 # Verify our users got placed into the to assert len(ac[0][0].targets) == 3 assert ("John Smith", '*****@*****.**') in ac[0][0].targets assert ("Jason Tater", '*****@*****.**') in ac[0][0].targets assert (False, '*****@*****.**') in ac[0][0].targets assert ac[0][0].smtp_host == 'smtp3-dev.google.gmail.com' assert len(ac[0][1].targets) == 2 assert ("Henry Fisher", '*****@*****.**') in ac[0][1].targets assert ("Jason Archie", '*****@*****.**') in ac[0][1].targets assert 'drinking-buddy' in ac[0][1].tags assert ac[0][1].smtp_host == 'smtp5-dev.google.gmail.com' # Our third test tests cases where some variables are defined inline # and additional ones are defined below that share the same token space assert len(ac[0][2].targets) == 1 assert len(ac[0][2].cc) == 1 assert (False, '*****@*****.**') in ac[0][2].targets assert '*****@*****.**' in ac[0][2].cc # Test our Since configuration now: assert len(ac[0][3].targets) == 1 assert ac[0][3].service_plan_id == 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' assert ac[0][3].source == '+10005243890' assert ac[0][3].targets[0] == '+11235551234'
def test_config_base_config_parse(): """ API: ConfigBase.config_parse """ # Garbage Handling assert isinstance(ConfigBase.config_parse(object()), list) assert isinstance(ConfigBase.config_parse(None), list) assert isinstance(ConfigBase.config_parse(''), list) assert isinstance(ConfigBase.config_parse(12), list) # Valid Text Configuration result = ConfigBase.config_parse(""" # A comment line over top of a URL mailto://userb:[email protected] """, asset=AppriseAsset()) # We expect to parse 1 entry from the above assert isinstance(result, list) assert len(result) == 1 assert len(result[0].tags) == 0 # Valid Configuration result = ConfigBase.config_parse(""" # if no version is specified then version 1 is presumed version: 1 # # Define your notification urls: # urls: - pbul://o.gn5kj6nfhv736I7jC3cj3QLRiyhgl98b - mailto://test:[email protected] - syslog://: - tag: devops, admin """, asset=AppriseAsset()) # We expect to parse 3 entries from the above assert isinstance(result, list) assert len(result) == 3 assert len(result[0].tags) == 0 assert len(result[1].tags) == 0 assert len(result[2].tags) == 2 # Test case where we pass in a bad format result = ConfigBase.config_parse(""" ; A comment line over top of a URL mailto://userb:[email protected] """, config_format='invalid-format') # This is not parseable despite the valid text assert isinstance(result, list) assert len(result) == 0 result = ConfigBase.config_parse(""" ; A comment line over top of a URL mailto://userb:[email protected] """, config_format=ConfigFormat.TEXT) # Parseable assert isinstance(result, list) assert len(result) == 1