def test_detect_language_windows_users(): """ API: Apprise() Detect language """ if not hasattr(ctypes, 'windll'): windll = mock.Mock() # 4105 = en_CA windll.kernel32.GetUserDefaultUILanguage.return_value = 4105 setattr(ctypes, 'windll', windll) # The below accesses the windows fallback code with environ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_CTYPE', LANG="en_CA"): assert AppriseLocale.AppriseLocale.detect_language() == 'en' assert AppriseLocale.AppriseLocale\ .detect_language(detect_fallback=False) is None # 0 = IndexError windll.kernel32.GetUserDefaultUILanguage.return_value = 0 setattr(ctypes, 'windll', windll) with environ('LANG', 'LC_ALL', 'LC_CTYPE', LANGUAGE="en_CA"): assert AppriseLocale.AppriseLocale.detect_language() == 'en' # The below accesses the windows fallback code and fail # then it will resort to the environment variables with environ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_CTYPE'): # Language can't be detected assert AppriseLocale.AppriseLocale.detect_language() is None with environ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', LANG="fr_CA"): # Detect french language assert AppriseLocale.AppriseLocale.detect_language() == 'fr' # Tidy delattr(ctypes, 'windll')
def test_detect_language_windows_users(): """ API: Apprise() Detect language """ if not hasattr(ctypes, 'windll'): windll = mock.Mock() # 4105 = en_CA windll.kernel32.GetUserDefaultUILanguage.return_value = 4105 setattr(ctypes, 'windll', windll) # The below accesses the windows fallback code with environ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_CTYPE', LANG="en_CA"): assert AppriseLocale.AppriseLocale.detect_language() == 'en' assert AppriseLocale.AppriseLocale\ .detect_language(detect_fallback=False) is None # 0 = IndexError windll.kernel32.GetUserDefaultUILanguage.return_value = 0 setattr(ctypes, 'windll', windll) with environ('LANG', 'LC_ALL', 'LC_CTYPE', LANGUAGE="en_CA"): assert AppriseLocale.AppriseLocale.detect_language() == 'en' # The below accesses the windows fallback code and fail # then it will resort to the environment variables with environ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_CTYPE'): # Language can't be detected assert AppriseLocale.AppriseLocale.detect_language() is None with environ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', LANG="fr_CA"): # Detect french language assert AppriseLocale.AppriseLocale.detect_language() == 'fr' # The following unsets all enviroment vaiables and sets LC_CTYPE # This was causing Python 2.7 to internally parse UTF-8 as an invalid # locale and throw an uncaught ValueError with environ(*list(os.environ.keys()), LC_CTYPE="UTF-8"): assert AppriseLocale.AppriseLocale.detect_language() is None # Test with absolutely no environment variables what-so-ever with environ(*list(os.environ.keys())): assert AppriseLocale.AppriseLocale.detect_language() is None # Tidy delattr(ctypes, 'windll')
def test_environ_temporary_change(): """utils: environ() testing """ e_key1 = 'APPRISE_TEMP1' e_key2 = 'APPRISE_TEMP2' e_key3 = 'APPRISE_TEMP3' e_val1 = 'ABCD' e_val2 = 'DEFG' e_val3 = 'HIJK' os.environ[e_key1] = e_val1 os.environ[e_key2] = e_val2 os.environ[e_key3] = e_val3 # Ensure our environment variable stuck assert e_key1 in os.environ assert e_val1 in os.environ[e_key1] assert e_key2 in os.environ assert e_val2 in os.environ[e_key2] assert e_key3 in os.environ assert e_val3 in os.environ[e_key3] with utils.environ(e_key1, e_key3): # Eliminates Environment Variable 1 and 3 assert e_key1 not in os.environ assert e_key2 in os.environ assert e_val2 in os.environ[e_key2] assert e_key3 not in os.environ # after with is over, environment is restored to normal assert e_key1 in os.environ assert e_val1 in os.environ[e_key1] assert e_key2 in os.environ assert e_val2 in os.environ[e_key2] assert e_key3 in os.environ assert e_val3 in os.environ[e_key3] d_key = 'APPRISE_NOT_SET' n_key = 'APPRISE_NEW_KEY' n_val = 'NEW_VAL' # Verify that our temporary variables (defined above) are not pre-existing # environemnt variables as we'll be setting them below assert n_key not in os.environ assert d_key not in os.environ # makes it easier to pass in the arguments updates = { e_key1: e_val3, e_key2: e_val1, n_key: n_val, } with utils.environ(d_key, e_key3, **updates): # Attempt to eliminate an undefined key (silently ignored) # Eliminates Environment Variable 3 # Environment Variable 1 takes on the value of Env 3 # Environment Variable 2 takes on the value of Env 1 # Set a brand new variable that previously didn't exist assert e_key1 in os.environ assert e_val3 in os.environ[e_key1] assert e_key2 in os.environ assert e_val1 in os.environ[e_key2] assert e_key3 not in os.environ # Can't delete a variable that doesn't exist; so we're in the same # state here. assert d_key not in os.environ # Our temporary variables will be found now assert n_key in os.environ assert n_val in os.environ[n_key] # after with is over, environment is restored to normal assert e_key1 in os.environ assert e_val1 in os.environ[e_key1] assert e_key2 in os.environ assert e_val2 in os.environ[e_key2] assert e_key3 in os.environ assert e_val3 in os.environ[e_key3] # Even our temporary variables are now missing assert n_key not in os.environ assert d_key not in os.environ
def test_apprise_cli_nux_env(tmpdir): """ CLI: Nux Environment """ class GoodNotification(NotifyBase): def __init__(self, *args, **kwargs): super(GoodNotification, self).__init__(*args, **kwargs) def notify(self, **kwargs): # Pretend everything is okay return True def url(self, *args, **kwargs): # Support url() return 'good://' class BadNotification(NotifyBase): def __init__(self, *args, **kwargs): super(BadNotification, self).__init__(*args, **kwargs) def notify(self, **kwargs): # Force a notification failure return False def url(self, *args, **kwargs): # Support url() return 'bad://' # Set up our notification types SCHEMA_MAP['good'] = GoodNotification SCHEMA_MAP['bad'] = BadNotification runner = CliRunner() result = runner.invoke(cli.main) # no servers specified; we return 1 (non-zero) assert result.exit_code == 1 result = runner.invoke(cli.main, ['-v']) assert result.exit_code == 1 result = runner.invoke(cli.main, ['-vv']) assert result.exit_code == 1 result = runner.invoke(cli.main, ['-vvv']) assert result.exit_code == 1 result = runner.invoke(cli.main, ['-vvvv']) assert result.exit_code == 1 # Display version information and exit result = runner.invoke(cli.main, ['-V']) assert result.exit_code == 0 result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'good://localhost', ]) assert result.exit_code == 0 with mock.patch('requests.post') as mock_post: # Prepare Mock mock_post.return_value = requests.Request() mock_post.return_value.status_code = requests.codes.ok result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body\\nsNewLine', # Test using interpret escapes '-e', # Use our JSON query 'json://localhost', ]) assert result.exit_code == 0 # Test our call count assert mock_post.call_count == 1 # Our string is now escaped correctly json.loads(mock_post.call_args_list[0][1]['data'])\ .get('message', '') == 'test body\nsNewLine' # Reset mock_post.reset_mock() result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body\\nsNewLine', # No -e switch at all (so we don't escape the above) # Use our JSON query 'json://localhost', ]) assert result.exit_code == 0 # Test our call count assert mock_post.call_count == 1 # Our string is now escaped correctly json.loads(mock_post.call_args_list[0][1]['data'])\ .get('message', '') == 'test body\\nsNewLine' # Run in synchronous mode result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'good://localhost', '--disable-async', ]) assert result.exit_code == 0 # Test Debug Mode (--debug) result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'good://localhost', '--debug', ]) assert result.exit_code == 0 # Test Debug Mode (-D) result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'good://localhost', '-D', ]) assert result.exit_code == 0 result = runner.invoke(cli.main, [ '-t', 'test title', 'good://localhost', ], input='test stdin body\n') assert result.exit_code == 0 # Run in synchronous mode result = runner.invoke(cli.main, [ '-t', 'test title', 'good://localhost', '--disable-async', ], input='test stdin body\n') assert result.exit_code == 0 result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'bad://localhost', ]) assert result.exit_code == 1 # Run in synchronous mode result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'bad://localhost', '-Da', ]) assert result.exit_code == 1 # Testing with the --dry-run flag reveals a successful response since we # don't actually execute the bad:// notification; we only display it result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'bad://localhost', '--dry-run', ]) assert result.exit_code == 0 # Write a simple text based configuration file t = tmpdir.mkdir("apprise-obj").join("apprise") buf = """ # Include ourselves include {} taga,tagb=good://localhost tagc=good://nuxref.com """.format(str(t)) t.write(buf) # This will read our configuration and not send any notices at all # because we assigned tags to all of our urls and didn't identify # a specific match below. # 'include' reference in configuration file would have included the file a # second time (since recursion default is 1). result = runner.invoke(cli.main, [ '-b', 'test config', '--config', str(t), ]) # Even when recursion take place, tags are all honored # so 2 is returned because nothing was notified assert result.exit_code == 3 # This will send out 1 notification because our tag matches # one of the entries above # translation: has taga result = runner.invoke(cli.main, [ '-b', 'has taga', '--config', str(t), '--tag', 'taga', ]) assert result.exit_code == 0 # Test recursion result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', '--config', str(t), '--tag', 'tagc', # Invalid entry specified for recursion '-R', 'invalid', ]) assert result.exit_code == 2 result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', '--config', str(t), '--tag', 'tagc', # missing entry specified for recursion '--recursive-depth', ]) assert result.exit_code == 2 result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', '--config', str(t), '--tag', 'tagc', # Disable recursion (thus inclusion will be ignored) '-R', '0', ]) assert result.exit_code == 0 # Test recursion result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', '--config', str(t), '--tag', 'tagc', # Recurse up to 5 times '--recursion-depth', '5', ]) assert result.exit_code == 0 # This will send out 2 notifications because by specifying 2 tag # entries, we 'or' them together: # translation: has taga or tagb or tagd result = runner.invoke(cli.main, [ '-b', 'has taga OR tagc OR tagd', '--config', str(t), '--tag', 'taga', '--tag', 'tagc', '--tag', 'tagd', ]) assert result.exit_code == 0 # Write a simple text based configuration file t = tmpdir.mkdir("apprise-obj2").join("apprise-test2") buf = """ good://localhost/1 good://localhost/2 good://localhost/3 good://localhost/4 good://localhost/5 myTag=good://localhost/6 """ t.write(buf) # This will read our configuration and send a notification to # the first 5 entries in the list, but not the one that has # the tag associated with it result = runner.invoke(cli.main, [ '-b', 'test config', '--config', str(t), ]) assert result.exit_code == 0 # Test our notification type switch (it defaults to info) so we want to # try it as a different value. Should return without a problem result = runner.invoke(cli.main, [ '-b', '# test config', '--config', str(t), '-n', 'success', ]) assert result.exit_code == 0 # Test our notification type switch when set to something unsupported result = runner.invoke(cli.main, [ '-b', 'test config', '--config', str(t), '--notification-type', 'invalid', ]) # An error code of 2 is returned if invalid input is specified on the # command line assert result.exit_code == 2 # The notification type switch is case-insensitive result = runner.invoke(cli.main, [ '-b', 'test config', '--config', str(t), '--notification-type', 'WARNING', ]) assert result.exit_code == 0 # Test our formatting switch (it defaults to text) so we want to try it as # a different value. Should return without a problem result = runner.invoke(cli.main, [ '-b', '# test config', '--config', str(t), '-i', 'markdown', ]) assert result.exit_code == 0 # Test our formatting switch when set to something unsupported result = runner.invoke(cli.main, [ '-b', 'test config', '--config', str(t), '--input-format', 'invalid', ]) # An error code of 2 is returned if invalid input is specified on the # command line assert result.exit_code == 2 # The formatting switch is not case sensitive result = runner.invoke(cli.main, [ '-b', '# test config', '--config', str(t), '--input-format', 'HTML', ]) assert result.exit_code == 0 # As a way of ensuring we match the first 5 entries, we can run a # --dry-run against the same result set above and verify the output result = runner.invoke(cli.main, [ '-b', 'test config', '--config', str(t), '--dry-run', ]) assert result.exit_code == 0 lines = re.split(r'[\r\n]', result.output.strip()) # 5 lines of all good:// entries matched assert len(lines) == 5 # Verify we match against the remaining good:// entries for i in range(0, 5): assert lines[i].endswith('good://') # This will fail because nothing matches mytag. It's case sensitive # and we would only actually match against myTag result = runner.invoke(cli.main, [ '-b', 'has mytag', '--config', str(t), '--tag', 'mytag', ]) assert result.exit_code == 3 # Same command as the one identified above except we set the --dry-run # flag. This causes our list of matched results to be printed only. # However, since we don't match anything; we still fail with a return code # of 2. result = runner.invoke(cli.main, [ '-b', 'has mytag', '--config', str(t), '--tag', 'mytag', '--dry-run' ]) assert result.exit_code == 3 # Here is a case where we get what was expected; we also attach a file result = runner.invoke(cli.main, [ '-b', 'has myTag', '--config', str(t), '--attach', join(dirname(__file__), 'var', 'apprise-test.gif'), '--tag', 'myTag', ]) assert result.exit_code == 0 # Testing with the --dry-run flag reveals the same positive results # because there was at least one match result = runner.invoke(cli.main, [ '-b', 'has myTag', '--config', str(t), '--tag', 'myTag', '--dry-run', ]) assert result.exit_code == 0 # # Test environment variables # # Write a simple text based configuration file t2 = tmpdir.mkdir("apprise-obj-env").join("apprise") buf = """ # A general one good://localhost # A failure (if we use the fail tag) fail=bad://localhost # A normal one tied to myTag myTag=good://nuxref.com """ t2.write(buf) with environ(APPRISE_URLS="good://localhost"): # This will load okay because we defined the environment # variable with a valid URL result = runner.invoke(cli.main, [ '-b', 'test environment', # Test that we ignore our tag '--tag', 'mytag', ]) assert result.exit_code == 0 # Same action but without --tag result = runner.invoke(cli.main, [ '-b', 'test environment', ]) assert result.exit_code == 0 with mock.patch('apprise.cli.DEFAULT_SEARCH_PATHS', []): with environ(APPRISE_URLS=" "): # An empty string is not valid and therefore not loaded so the # below fails. We override the DEFAULT_SEARCH_PATHS because we # don't want to detect ones loaded on the machine running the unit # tests result = runner.invoke(cli.main, [ '-b', 'test environment', ]) assert result.exit_code == 1 with environ(APPRISE_URLS="bad://localhost"): result = runner.invoke(cli.main, [ '-b', 'test environment', ]) assert result.exit_code == 1 # If we specify an inline URL, it will over-ride the environment # variable result = runner.invoke(cli.main, [ '-t', 'test title', '-b', 'test body', 'good://localhost', ]) assert result.exit_code == 0 # A Config file also over-rides the environment variable if # specified on the command line: result = runner.invoke(cli.main, [ '-b', 'has myTag', '--config', str(t2), '--tag', 'myTag', ]) assert result.exit_code == 0 with environ(APPRISE_CONFIG=str(t2)): # Our configuration file will load from our environmment variable result = runner.invoke(cli.main, [ '-b', 'has myTag', '--tag', 'myTag', ]) assert result.exit_code == 0 with mock.patch('apprise.cli.DEFAULT_SEARCH_PATHS', []): with environ(APPRISE_CONFIG=" "): # We will fail to send the notification as no path was # specified. # We override the DEFAULT_SEARCH_PATHS because we don't # want to detect ones loaded on the machine running the unit tests result = runner.invoke(cli.main, [ '-b', 'my message', ]) assert result.exit_code == 1 with environ(APPRISE_CONFIG="garbage/file/path.yaml"): # We will fail to send the notification as the path # specified is not loadable result = runner.invoke(cli.main, [ '-b', 'my message', ]) assert result.exit_code == 1 # We can force an over-ride by specifying a config file on the # command line options: result = runner.invoke(cli.main, [ '-b', 'has myTag', '--config', str(t2), '--tag', 'myTag', ]) assert result.exit_code == 0 # Just a general test; if both the --config and urls are specified # then the the urls trumps all result = runner.invoke(cli.main, [ '-b', 'has myTag', '--config', str(t2), 'good://localhost', '--tag', 'fail', ]) # Tags are ignored, URL specified, so it trump config assert result.exit_code == 0 # we just repeat the test as a proof that it only executes # the urls despite the fact the --config was specified result = runner.invoke(cli.main, [ '-b', 'reads the url entry only', '--config', str(t2), 'good://localhost', '--tag', 'fail', ]) # Tags are ignored, URL specified, so it trump config assert result.exit_code == 0 # once agian, but we call bad:// result = runner.invoke(cli.main, [ '-b', 'reads the url entry only', '--config', str(t2), 'bad://localhost', '--tag', 'myTag', ]) assert result.exit_code == 1