Exemplo n.º 1
0
def test_invalid_apprise_config(tmpdir):
    """
    Parse invalid configuration includes

    """

    class BadConfig(ConfigBase):
        # always allow incusion
        allow_cross_includes = ContentIncludeMode.ALWAYS

        def __init__(self, **kwargs):
            super(BadConfig, self).__init__(**kwargs)

            # We intentionally fail whenever we're initialized
            raise TypeError()

        @staticmethod
        def parse_url(url, *args, **kwargs):
            # always parseable
            return ConfigBase.parse_url(url, verify_host=False)

    # Store our bad configuration in our schema map
    CONFIG_SCHEMA_MAP['bad'] = BadConfig

    # temporary file to work with
    t = tmpdir.mkdir("apprise-bad-obj").join("invalid")
    buf = """
    # Include an invalid schema
    include invalid://

    # An unparsable valid schema
    include https://

    # A valid configuration that will throw an exception
    include bad://

    # Include ourselves (So our recursive includes fails as well)
    include {}

    """.format(str(t))
    t.write(buf)

    # Create ourselves a config object with caching disbled
    ac = AppriseConfig(recursion=2, insecure_includes=True, cache=False)

    # Nothing loaded yet
    assert len(ac) == 0

    # Add our config
    assert ac.add(configs=str(t), asset=AppriseAsset()) is True

    # One configuration file
    assert len(ac) == 1

    # All of the servers were invalid and would not load
    assert len(ac.servers()) == 0
Exemplo n.º 2
0
def test_config_base_parse_yaml_file04(tmpdir):
    """
    API: ConfigBase.parse_yaml_file (#4)

    Test the always keyword

    """
    t = tmpdir.mkdir("always-keyword").join("apprise.yml")
    t.write("""urls:
  - pover://nsisxnvnqixq39t0cw54pxieyvtdd9@2jevtmstfg5a7hfxndiybasttxxfku:
    - tag: test1,always
  - 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 still matches `always` keyword
    assert sum(1 for _ in a.find('no-match')) == 1
    # Unless we explicitly do not look for that file
    assert sum(1 for _ in a.find('no-match', match_always=False)) == 0
    # Match everything
    assert sum(1 for _ in a.find('all')) == 3
    # Match test1 entry (also has `always` keyword
    assert sum(1 for _ in a.find('test1')) == 1
    assert sum(1 for _ in a.find('test1', match_always=False)) == 1
    # Match test2 entry (and test1 due to always keyword)
    assert sum(1 for _ in a.find('test2')) == 2
    assert sum(1 for _ in a.find('test2', match_always=False)) == 1
    # Match test3 entry (and test1 due to always keyword)
    assert sum(1 for _ in a.find('test3')) == 2
    assert sum(1 for _ in a.find('test3', match_always=False)) == 1
    # Match test1 or test3 entry
    assert sum(1 for _ in a.find('test1, test3')) == 2
Exemplo n.º 3
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
Exemplo n.º 4
0
def test_config_base_parse_yaml_file01(tmpdir):
    """
    API: ConfigBase.parse_yaml_file (#1)

    """
    t = tmpdir.mkdir("empty-file").join("apprise.yml")
    t.write("")

    # 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()) == 0
Exemplo n.º 5
0
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
Exemplo n.º 6
0
def test_apprise_config_instantiate():
    """
    API: AppriseConfig.instantiate()

    """
    assert AppriseConfig.instantiate(
        'file://?', suppress_exceptions=True) is None

    assert AppriseConfig.instantiate(
        'invalid://?', suppress_exceptions=True) is None

    class BadConfig(ConfigBase):
        # always allow incusion
        allow_cross_includes = ContentIncludeMode.ALWAYS

        def __init__(self, **kwargs):
            super(BadConfig, self).__init__(**kwargs)

            # We fail whenever we're initialized
            raise TypeError()

        @staticmethod
        def parse_url(url, *args, **kwargs):
            # always parseable
            return ConfigBase.parse_url(url, verify_host=False)

    # Store our bad configuration in our schema map
    CONFIG_SCHEMA_MAP['bad'] = BadConfig

    with pytest.raises(TypeError):
        AppriseConfig.instantiate(
            'bad://path', suppress_exceptions=False)

    # Same call but exceptions suppressed
    assert AppriseConfig.instantiate(
        'bad://path', suppress_exceptions=True) is None
Exemplo n.º 7
0
def test_config_base_parse_inaccessible_text_file(mock_getsize, tmpdir):
    """
    API: ConfigBase.parse_inaccessible_text_file

    """

    # temporary file to work with
    t = tmpdir.mkdir("inaccessible").join("apprise")
    buf = "gnome://"
    t.write(buf)

    # Set getsize return value
    mock_getsize.return_value = None
    mock_getsize.side_effect = OSError

    # Create ourselves a config object
    ac = AppriseConfig(paths=str(t))

    # The following internally throws an exception but still counts
    # as a loaded configuration file
    assert len(ac) == 1

    # Thus no notifications are loaded
    assert len(ac.servers()) == 0
Exemplo n.º 8
0
import logging
import traceback
from apprise import Apprise, AppriseConfig
from flask import Flask, request


app = Flask(__name__)

gw_config = AppriseConfig()
gw_config.add('file://apprise-config.yml')

gateway = Apprise()
gateway.add(gw_config)


@app.route('/push/<string:service>/send', methods=['POST'])
def push_send(service):
    """
    Emulating a RocketChat push gateway like the official https://gateway.rocket.chat
    Returns the service name (could be anything) along with a HTTP 200 OK status

    :see: Notification factory https://github.com/RocketChat/Rocket.Chat/blob/ed092fbe490c21d64c071772ce1da66515837353/app/push-notifications/server/lib/PushNotification.js#L6
    :see: Payload extension https://github.com/raix/push/blob/234eeb12daa9b553d246c0a6edd3d06d550aa41b/lib/common/notifications.js#L67
    :see: API call https://github.com/RocketChat/Rocket.Chat/blob/88c8c8c0b0e57e2d5d66a19d71775bc0b10f424c/server/lib/cordova.js#L97
    :return: string
    """

    app.logger.info('New notification received')
    app.logger.debug(request.headers)
    app.logger.debug(request.json)
Exemplo n.º 9
0
def test_recursive_config_inclusion(tmpdir):
    """
    API: Apprise() Recursive Config Inclusion

    """

    # To test our config classes, we make three dummy configs
    class ConfigCrossPostAlways(ConfigFile):
        """
        A dummy config that is set to always allow inclusion
        """

        service_name = 'always'

        # protocol
        protocol = 'always'

        # Always type
        allow_cross_includes = ContentIncludeMode.ALWAYS

    class ConfigCrossPostStrict(ConfigFile):
        """
        A dummy config that is set to strict inclusion
        """

        service_name = 'strict'

        # protocol
        protocol = 'strict'

        # Always type
        allow_cross_includes = ContentIncludeMode.STRICT

    class ConfigCrossPostNever(ConfigFile):
        """
        A dummy config that is set to never allow inclusion
        """

        service_name = 'never'

        # protocol
        protocol = 'never'

        # Always type
        allow_cross_includes = ContentIncludeMode.NEVER

    # store our entries
    CONFIG_SCHEMA_MAP['never'] = ConfigCrossPostNever
    CONFIG_SCHEMA_MAP['strict'] = ConfigCrossPostStrict
    CONFIG_SCHEMA_MAP['always'] = ConfigCrossPostAlways

    # Make our new path valid
    suite = tmpdir.mkdir("apprise_config_recursion")

    cfg01 = suite.join("cfg01.cfg")
    cfg02 = suite.mkdir("dir1").join("cfg02.cfg")
    cfg03 = suite.mkdir("dir2").join("cfg03.cfg")
    cfg04 = suite.mkdir("dir3").join("cfg04.cfg")

    # Populate our files with valid configuration include lines
    cfg01.write("""
# json entry
json://localhost:8080

# absolute path inclusion to ourselves
include {}""".format(str(cfg01)))

    cfg02.write("""
# syslog entry
syslog://

# recursively include ourselves
include cfg02.cfg""")

    cfg03.write("""
# xml entry
xml://localhost:8080

# relative path inclusion
include ../dir1/cfg02.cfg

# test that we can't include invalid entries
include invalid://entry

# Include non includable type
include memory://""")

    cfg04.write("""
# xml entry
xml://localhost:8080

# always include of our file
include always://{}

# never include of our file
include never://{}

# strict include of our file
include strict://{}""".format(str(cfg04), str(cfg04), str(cfg04)))

    # Create ourselves a config object
    ac = AppriseConfig()

    # There are no servers loaded
    assert len(ac) == 0

    # load our configuration
    assert ac.add(configs=str(cfg01)) is True

    # verify it loaded
    assert len(ac) == 1

    # 1 service will be loaded as there is no recursion at this point
    assert len(ac.servers()) == 1

    # Create ourselves a config object
    ac = AppriseConfig(recursion=1)

    # load our configuration
    assert ac.add(configs=str(cfg01)) is True

    # verify one configuration file loaded however since it recursively
    # loaded itself 1 more time, it still doesn't impact the load count:
    assert len(ac) == 1

    # 2 services loaded now that we loaded the same file twice
    assert len(ac.servers()) == 2

    #
    # Now we test relative file inclusion
    #

    # Create ourselves a config object
    ac = AppriseConfig(recursion=10)

    # There are no servers loaded
    assert len(ac) == 0

    # load our configuration
    assert ac.add(configs=str(cfg02)) is True

    # verify it loaded
    assert len(ac) == 1

    # 11 services loaded because we reloaded ourselves 10 times
    # after loading the first entry
    assert len(ac.servers()) == 11

    # Test our include modes (strict, always, and never)

    # Create ourselves a config object
    ac = AppriseConfig(recursion=1)

    # There are no servers loaded
    assert len(ac) == 0

    # load our configuration
    assert ac.add(configs=str(cfg04)) is True

    # verify it loaded
    assert len(ac) == 1

    # 2 servers loaded
    # 1 - from the file read (which is set at mode STRICT
    # 1 - from the always://
    #
    # The never:// can ever be includeed, and the strict:// is ot of type
    #  file:// (the one doing the include) so it is also ignored.
    #
    # By turning on the insecure_includes, we can include the strict files too
    assert len(ac.servers()) == 2

    # Create ourselves a config object
    ac = AppriseConfig(recursion=1, insecure_includes=True)

    # There are no servers loaded
    assert len(ac) == 0

    # load our configuration
    assert ac.add(configs=str(cfg04)) is True

    # verify it loaded
    assert len(ac) == 1

    # 3 servers loaded
    # 1 - from the file read (which is set at mode STRICT
    # 1 - from the always://
    # 1 - from the strict:// (due to insecure_includes set)
    assert len(ac.servers()) == 3
Exemplo n.º 10
0
def test_apprise_config_with_apprise_obj(tmpdir):
    """
    API: ConfigBase - parse valid config

    """

    # temporary file to work with
    t = tmpdir.mkdir("apprise-obj").join("apprise")
    buf = """
    good://hostname
    localhost=good://localhost
    """
    t.write(buf)

    # Define our good:// url
    class GoodNotification(NotifyBase):
        def __init__(self, **kwargs):
            super(GoodNotification, self).__init__(
                notify_format=NotifyFormat.HTML, **kwargs)

        def notify(self, **kwargs):
            # Pretend everything is okay
            return True

        def url(self, **kwargs):
            # support url()
            return ''

    # Store our good notification in our schema map
    NOTIFY_SCHEMA_MAP['good'] = GoodNotification

    # Create ourselves a config object
    ac = AppriseConfig(cache=False)

    # Nothing loaded yet
    assert len(ac) == 0

    # Add an item associated with tag a
    assert ac.add(configs=str(t), asset=AppriseAsset(), tag='a') is True

    # One configuration file
    assert len(ac) == 1

    # 2 services found in it
    assert len(ac.servers()) == 2

    # Pop one of them (at index 0)
    ac.server_pop(0)

    # Verify that it no longer listed
    assert len(ac.servers()) == 1

    # 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 1 entry (originally there were 2 but we deleted one)
    assert len(a) == 1

    # Notify our service
    assert a.notify(body='apprise configuration power!') is True

    # Add our configuration object
    assert a.add(
        servers=[AppriseConfig(str(t)), AppriseConfig(str(t))]) is True

    # Detect our 5 loaded entries now; 1 from first config, and another
    # 2x2 based on adding our list above
    assert len(a) == 5

    # We can't add garbage
    assert a.add(servers=object()) is False
    assert a.add(servers=[object(), object()]) is False

    # Our length is unchanged
    assert len(a) == 5

    # reference index 0 of our list
    ref = a[0]
    assert isinstance(ref, NotifyBase) is True

    # Our length is unchanged
    assert len(a) == 5

    # pop the index
    ref_popped = a.pop(0)

    # Verify our response
    assert isinstance(ref_popped, NotifyBase) is True

    # Our length drops by 1
    assert len(a) == 4

    # Content popped is the same as one referenced by index
    # earlier
    assert ref == ref_popped

    # pop an index out of range
    try:
        a.pop(len(a))
        # We'll thrown an IndexError and not make it this far
        assert False

    except IndexError:
        # As expected
        assert True

    # Our length remains unchanged
    assert len(a) == 4

    # Reference content out of range
    try:
        a[len(a)]

        # We'll thrown an IndexError and not make it this far
        assert False

    except IndexError:
        # As expected
        assert True

    # reference index at the end of our list
    ref = a[len(a) - 1]

    # Verify our response
    assert isinstance(ref, NotifyBase) is True

    # Our length stays the same
    assert len(a) == 4

    # We can pop from the back of the list without a problem too
    ref_popped = a.pop(len(a) - 1)

    # Verify our response
    assert isinstance(ref_popped, NotifyBase) is True

    # Content popped is the same as one referenced by index
    # earlier
    assert ref == ref_popped

    # Our length drops by 1
    assert len(a) == 3

    # Now we'll test adding another element to the list so that it mixes up
    # our response object.
    # Below we add 3 different types, a ConfigBase, NotifyBase, and URL
    assert a.add(
        servers=[
            ConfigFile(path=(str(t))),
            'good://another.host',
            GoodNotification(**{'host': 'nuxref.com'})]) is True

    # Our length increases by 4 (2 entries in the config file, + 2 others)
    assert len(a) == 7

    # reference index at the end of our list
    ref = a[len(a) - 1]

    # Verify our response
    assert isinstance(ref, NotifyBase) is True

    # We can pop from the back of the list without a problem too
    ref_popped = a.pop(len(a) - 1)

    # Verify our response
    assert isinstance(ref_popped, NotifyBase) is True

    # Content popped is the same as one referenced by index
    # earlier
    assert ref == ref_popped

    # Our length drops by 1
    assert len(a) == 6

    # pop our list
    while len(a) > 0:
        assert isinstance(a.pop(len(a) - 1), NotifyBase) is True
Exemplo n.º 11
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/

    # XML
    xml://localhost/?+HeaderEntry=Test&:IgnoredEntry=Ignored
    """)

    # 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 4 servers from that
    assert len(ac.servers()) == 4

    # 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
Exemplo n.º 12
0
def test_apprise_config_tagging(tmpdir):
    """
    API: AppriseConfig tagging

    """

    # temporary file to work with
    t = tmpdir.mkdir("tagging").join("apprise")
    buf = "gnome://"
    t.write(buf)

    # Create ourselves a config object
    ac = AppriseConfig()

    # Add an item associated with tag a
    assert ac.add(configs=str(t), asset=AppriseAsset(), tag='a') is True
    # Add an item associated with tag b
    assert ac.add(configs=str(t), asset=AppriseAsset(), tag='b') is True
    # Add an item associated with tag a or b
    assert ac.add(configs=str(t), asset=AppriseAsset(), tag='a,b') is True

    # Now filter: a:
    assert len(ac.servers(tag='a')) == 2
    # Now filter: a or b:
    assert len(ac.servers(tag='a,b')) == 3
    # Now filter: a and b
    assert len(ac.servers(tag=[('a', 'b')])) == 1
    # all matches everything
    assert len(ac.servers(tag='all')) == 3

    # Test cases using the `always` keyword
    # Create ourselves a config object
    ac = AppriseConfig()

    # Add an item associated with tag a
    assert ac.add(configs=str(t), asset=AppriseAsset(), tag='a,always') is True
    # Add an item associated with tag b
    assert ac.add(configs=str(t), asset=AppriseAsset(), tag='b') is True
    # Add an item associated with tag a or b
    assert ac.add(configs=str(t), asset=AppriseAsset(), tag='c,d') is True

    # Now filter: a:
    assert len(ac.servers(tag='a')) == 1
    # Now filter: a or b:
    assert len(ac.servers(tag='a,b')) == 2
    # Now filter: e
    # we'll match the `always'
    assert len(ac.servers(tag='e')) == 1
    assert len(ac.servers(tag='e', match_always=False)) == 0
    # all matches everything
    assert len(ac.servers(tag='all')) == 3

    # Now filter: d
    # we'll match the `always' tag
    assert len(ac.servers(tag='d')) == 2
    assert len(ac.servers(tag='d', match_always=False)) == 1
Exemplo n.º 13
0
def test_apprise_add_config():
    """
    API AppriseConfig.add_config()

    """
    content = """
    # 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()
    assert ac.add_config(content=content) is True

    # One configuration file should have been found
    assert len(ac) == 1
    assert ac[0].config_format is ConfigFormat.TEXT

    # 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 invalid content
    assert ac.add_config(content=object()) is False
    assert ac.add_config(content=42) is False
    assert ac.add_config(content=None) is False

    # Still only one server loaded
    assert len(ac) == 1

    # Test having a pre-defined asset object and tag created
    assert ac.add_config(
        content=content, asset=AppriseAsset(), tag='a') is True

    # Now there are 2 servers loaded
    assert len(ac) == 2

    # and 6 urls.. (as we've doubled up)
    assert len(ac.servers()) == 6

    content = """
    # A YAML File
    urls:
       - mailto://usera:[email protected]
       - gnome://:
          tag: taga,tagb

       - json://localhost:
          +HeaderEntry1: 'a header entry'
          -HeaderEntryDepricated: 'a deprecated entry'
          :HeaderEntryIgnored: 'an ignored header entry'

       - xml://localhost:
          +HeaderEntry1: 'a header entry'
          -HeaderEntryDepricated: 'a deprecated entry'
          :HeaderEntryIgnored: 'an ignored header entry'
    """

    # Create ourselves a config object
    ac = AppriseConfig()
    assert ac.add_config(content=content) is True

    # One configuration file should have been found
    assert len(ac) == 1
    assert ac[0].config_format is ConfigFormat.YAML

    # 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 4 servers from that
    assert len(ac.servers()) == 4

    # Now an invalid configuration file
    content = "invalid"

    # Create ourselves a config object
    ac = AppriseConfig()
    assert ac.add_config(content=content) is False

    # Nothing is loaded
    assert len(ac.servers()) == 0
Exemplo n.º 14
0
def test_apprise_multi_config_entries(tmpdir):
    """
    API: AppriseConfig basic multi-adding functionality

    """
    # temporary file to work with
    t = tmpdir.mkdir("apprise-multi-add").join("apprise")
    buf = """
    good://hostname
    """
    t.write(buf)

    # temporary empty file to work with
    te = tmpdir.join("apprise-multi-add", "apprise-empty")
    te.write("")

    # Define our good:// url
    class GoodNotification(NotifyBase):
        def __init__(self, **kwargs):
            super(GoodNotification, self).__init__(
                notify_format=NotifyFormat.HTML, **kwargs)

        def notify(self, **kwargs):
            # Pretend everything is okay
            return True

        def url(self, **kwargs):
            # support url()
            return ''

    # Store our good notification in our schema map
    NOTIFY_SCHEMA_MAP['good'] = GoodNotification

    # Create ourselves a config object
    ac = AppriseConfig()

    # There are no servers loaded
    assert len(ac) == 0

    # Support adding of muilt strings and objects:
    assert ac.add(configs=(str(t), str(t))) is True
    assert ac.add(configs=(
        ConfigFile(path=str(te)), ConfigFile(path=str(t)))) is True

    # don't support the adding of invalid content
    assert ac.add(configs=(object(), object())) is False
    assert ac.add(configs=object()) is False

    # Try to pop an element out of range
    try:
        ac.server_pop(len(ac.servers()))
        # We should have thrown an exception here
        assert False

    except IndexError:
        # We expect to be here
        assert True

    # Pop our elements
    while len(ac.servers()) > 0:
        assert isinstance(
            ac.server_pop(len(ac.servers()) - 1), NotifyBase) is True
Exemplo n.º 15
0
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'
Exemplo n.º 16
0
def test_msteams_yaml_config(mock_post, tmpdir):
    """
    NotifyMSTeams() YAML Configuration Entries

    """

    # Disable Throttling to speed testing
    plugins.NotifyBase.request_rate_per_sec = 0

    # Prepare Mock
    mock_post.return_value = requests.Request()
    mock_post.return_value.status_code = requests.codes.ok

    uuid4 = '8b799edf-6f98-4d3a-9be7-2862fb4e5752'
    url = 'msteams://{}@{}/{}/{}'.format(uuid4, uuid4, 'a' * 32, uuid4)

    # Test cases where our URL is invalid
    template = tmpdir.join("simple.json")
    template.write("""
    {
      "@type": "MessageCard",
      "@context": "https://schema.org/extensions",
      "summary": "{{name}}",
      "themeColor": "{{app_color}}",
      "sections": [
        {
          "activityImage": null,
          "activityTitle": "{{title}}",
          "text": "{{body}}"
        }
      ]
    }
    """)

    # Test Invalid Filename
    config = tmpdir.join("msteams01.yml")
    config.write("""
    urls:
      - {url}:
        - tag: 'msteams'
          template:  {template}.missing
          :name: 'Template.Missing'
          :body: 'test body'
          :title: 'test title'
    """.format(url=url, template=str(template)))

    cfg = AppriseConfig()
    cfg.add(str(config))
    assert len(cfg) == 1
    assert len(cfg[0]) == 1

    obj = cfg[0][0]
    assert isinstance(obj, plugins.NotifyMSTeams)
    assert obj.notify(
        body="body", title='title',
        notify_type=NotifyType.INFO) is False
    assert mock_post.called is False

    # Test token identifiers
    config = tmpdir.join("msteams01.yml")
    config.write("""
    urls:
      - {url}:
        - tag: 'msteams'
          template:  {template}
          :name: 'Testing'
          :body: 'test body'
          :title: 'test title'
    """.format(url=url, template=str(template)))

    cfg = AppriseConfig()
    cfg.add(str(config))
    assert len(cfg) == 1
    assert len(cfg[0]) == 1

    obj = cfg[0][0]
    assert isinstance(obj, plugins.NotifyMSTeams)
    assert obj.notify(
        body="body", title='title',
        notify_type=NotifyType.INFO) is True

    assert mock_post.called is True
    assert mock_post.call_args_list[0][0][0].startswith(
        'https://outlook.office.com/webhook/')

    # Our Posted JSON Object
    posted_json = json.loads(mock_post.call_args_list[0][1]['data'])
    assert 'summary' in posted_json
    assert posted_json['summary'] == 'Testing'
    assert posted_json['themeColor'] == '#3AA3E3'
    assert posted_json['sections'][0]['activityTitle'] == 'test title'
    assert posted_json['sections'][0]['text'] == 'test body'

    #
    # Now again but without a bullet under the url definition
    #
    mock_post.reset_mock()
    config = tmpdir.join("msteams02.yml")
    config.write("""
    urls:
      - {url}:
          tag: 'msteams'
          template:  {template}
          :name: 'Testing2'
          :body: 'test body2'
          :title: 'test title2'
    """.format(url=url, template=str(template)))

    cfg = AppriseConfig()
    cfg.add(str(config))
    assert len(cfg) == 1
    assert len(cfg[0]) == 1

    obj = cfg[0][0]
    assert isinstance(obj, plugins.NotifyMSTeams)
    assert obj.notify(
        body="body", title='title',
        notify_type=NotifyType.INFO) is True

    assert mock_post.called is True
    assert mock_post.call_args_list[0][0][0].startswith(
        'https://outlook.office.com/webhook/')

    # Our Posted JSON Object
    posted_json = json.loads(mock_post.call_args_list[0][1]['data'])
    assert 'summary' in posted_json
    assert posted_json['summary'] == 'Testing2'
    assert posted_json['themeColor'] == '#3AA3E3'
    assert posted_json['sections'][0]['activityTitle'] == 'test title2'
    assert posted_json['sections'][0]['text'] == 'test body2'

    #
    # Try again but store the content as a dictionary in the cofiguration file
    #
    mock_post.reset_mock()
    config = tmpdir.join("msteams03.yml")
    config.write("""
    urls:
      - {url}:
        - tag: 'msteams'
          template:  {template}
          tokens:
            name: 'Testing3'
            body: 'test body3'
            title: 'test title3'
    """.format(url=url, template=str(template)))

    cfg = AppriseConfig()
    cfg.add(str(config))
    assert len(cfg) == 1
    assert len(cfg[0]) == 1

    obj = cfg[0][0]
    assert isinstance(obj, plugins.NotifyMSTeams)
    assert obj.notify(
        body="body", title='title',
        notify_type=NotifyType.INFO) is True

    assert mock_post.called is True
    assert mock_post.call_args_list[0][0][0].startswith(
        'https://outlook.office.com/webhook/')

    # Our Posted JSON Object
    posted_json = json.loads(mock_post.call_args_list[0][1]['data'])
    assert 'summary' in posted_json
    assert posted_json['summary'] == 'Testing3'
    assert posted_json['themeColor'] == '#3AA3E3'
    assert posted_json['sections'][0]['activityTitle'] == 'test title3'
    assert posted_json['sections'][0]['text'] == 'test body3'

    #
    # Now again but without a bullet under the url definition
    #
    mock_post.reset_mock()
    config = tmpdir.join("msteams04.yml")
    config.write("""
    urls:
      - {url}:
          tag: 'msteams'
          template:  {template}
          tokens:
            name: 'Testing4'
            body: 'test body4'
            title: 'test title4'
    """.format(url=url, template=str(template)))

    cfg = AppriseConfig()
    cfg.add(str(config))
    assert len(cfg) == 1
    assert len(cfg[0]) == 1

    obj = cfg[0][0]
    assert isinstance(obj, plugins.NotifyMSTeams)
    assert obj.notify(
        body="body", title='title',
        notify_type=NotifyType.INFO) is True

    assert mock_post.called is True
    assert mock_post.call_args_list[0][0][0].startswith(
        'https://outlook.office.com/webhook/')

    # Our Posted JSON Object
    posted_json = json.loads(mock_post.call_args_list[0][1]['data'])
    assert 'summary' in posted_json
    assert posted_json['summary'] == 'Testing4'
    assert posted_json['themeColor'] == '#3AA3E3'
    assert posted_json['sections'][0]['activityTitle'] == 'test title4'
    assert posted_json['sections'][0]['text'] == 'test body4'

    # Now let's do a combination of the two
    mock_post.reset_mock()
    config = tmpdir.join("msteams05.yml")
    config.write("""
    urls:
      - {url}:
        - tag: 'msteams'
          template:  {template}
          tokens:
              body: 'test body5'
              title: 'test title5'
          :name: 'Testing5'
    """.format(url=url, template=str(template)))

    cfg = AppriseConfig()
    cfg.add(str(config))
    assert len(cfg) == 1
    assert len(cfg[0]) == 1

    obj = cfg[0][0]
    assert isinstance(obj, plugins.NotifyMSTeams)
    assert obj.notify(
        body="body", title='title',
        notify_type=NotifyType.INFO) is True

    assert mock_post.called is True
    assert mock_post.call_args_list[0][0][0].startswith(
        'https://outlook.office.com/webhook/')

    # Our Posted JSON Object
    posted_json = json.loads(mock_post.call_args_list[0][1]['data'])
    assert 'summary' in posted_json
    assert posted_json['summary'] == 'Testing5'
    assert posted_json['themeColor'] == '#3AA3E3'
    assert posted_json['sections'][0]['activityTitle'] == 'test title5'
    assert posted_json['sections'][0]['text'] == 'test body5'

    # Now let's do a test where our tokens is not the expected
    # dictionary we want to see
    mock_post.reset_mock()
    config = tmpdir.join("msteams06.yml")
    config.write("""
    urls:
      - {url}:
        - tag: 'msteams'
          template:  {template}
          # Not a dictionary
          tokens:
            body
    """.format(url=url, template=str(template)))

    cfg = AppriseConfig()
    cfg.add(str(config))
    assert len(cfg) == 1

    # It could not load because of invalid tokens
    assert len(cfg[0]) == 0