Blacken
continuous-integration/drone/push Build is failing Details

This commit is contained in:
IamTheFij 2022-04-04 20:23:15 -07:00
parent 094c910cd4
commit e41d82f9d2
6 changed files with 207 additions and 207 deletions

View File

@ -1,17 +1,15 @@
repos: repos:
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.2.3 rev: v1.2.3
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: autopep8-wrapper
args:
- -i
- --ignore=E265,E309,E501
- id: debug-statements - id: debug-statements
language_version: python3 language_version: python3
- id: flake8
language_version: python3
- id: check-yaml - id: check-yaml
args: args:
- --allow-multiple-documents - --allow-multiple-documents

View File

@ -16,15 +16,14 @@ from prometheus_client import start_http_server
DEFAULT_METRICS_PORT = 8080 DEFAULT_METRICS_PORT = 8080
logging.basicConfig( logging.basicConfig(
level=logging.ERROR, level=logging.ERROR, format="%(asctime)s %(levelname)s %(name)s %(message)s"
format='%(asctime)s %(levelname)s %(name)s %(message)s'
) )
logging.getLogger(__name__).addHandler(logging.NullHandler()) logging.getLogger(__name__).addHandler(logging.NullHandler())
def read_yaml(path): def read_yaml(path):
"""Loads config from a YAML file with env interpolation""" """Loads config from a YAML file with env interpolation"""
with open(path, 'r') as yaml: with open(path, "r") as yaml:
contents = yaml.read() contents = yaml.read()
return yamlenv.load(contents) return yamlenv.load(contents)
@ -35,44 +34,40 @@ def validate_monitor_settings(settings):
Note: Cannot yet validate the Alerts exist from within this class. Note: Cannot yet validate the Alerts exist from within this class.
That will be done by Minitor later That will be done by Minitor later
""" """
name = settings.get('name') name = settings.get("name")
if not name: if not name:
raise InvalidMonitorException('Invalid name for monitor') raise InvalidMonitorException("Invalid name for monitor")
if not settings.get('command'): if not settings.get("command"):
raise InvalidMonitorException( raise InvalidMonitorException("Invalid command for monitor {}".format(name))
'Invalid command for monitor {}'.format(name)
)
type_assertions = ( type_assertions = (
('check_interval', int), ("check_interval", int),
('alert_after', int), ("alert_after", int),
('alert_every', int), ("alert_every", int),
) )
for key, val_type in type_assertions: for key, val_type in type_assertions:
val = settings.get(key) val = settings.get(key)
if not isinstance(val, val_type): if not isinstance(val, val_type):
raise InvalidMonitorException( raise InvalidMonitorException(
'Invalid type on {}: {}. Expected {} and found {}'.format( "Invalid type on {}: {}. Expected {} and found {}".format(
name, key, val_type.__name__, type(val).__name__ name, key, val_type.__name__, type(val).__name__
) )
) )
non_zero = ( non_zero = (
'check_interval', "check_interval",
'alert_after', "alert_after",
) )
for key in non_zero: for key in non_zero:
if settings.get(key) == 0: if settings.get(key) == 0:
raise InvalidMonitorException( raise InvalidMonitorException(
'Invalid value for {}: {}. Value cannot be 0'.format( "Invalid value for {}: {}. Value cannot be 0".format(name, key)
name, key
)
) )
def maybe_decode(bstr, encoding='utf-8'): def maybe_decode(bstr, encoding="utf-8"):
try: try:
return bstr.decode(encoding) return bstr.decode(encoding)
except TypeError: except TypeError:
@ -82,14 +77,14 @@ def maybe_decode(bstr, encoding='utf-8'):
def call_output(*popenargs, **kwargs): def call_output(*popenargs, **kwargs):
"""Similar to check_output, but instead returns output and exception""" """Similar to check_output, but instead returns output and exception"""
# So we can capture complete output, redirect sderr to stdout # So we can capture complete output, redirect sderr to stdout
kwargs.setdefault('stderr', subprocess.STDOUT) kwargs.setdefault("stderr", subprocess.STDOUT)
output, ex = None, None output, ex = None, None
try: try:
output = check_output(*popenargs, **kwargs) output = check_output(*popenargs, **kwargs)
except CalledProcessError as e: except CalledProcessError as e:
output, ex = e.output, e output, ex = e.output, e
output = output.rstrip(b'\n') output = output.rstrip(b"\n")
return output, ex return output, ex
@ -113,23 +108,23 @@ class Monitor(object):
def __init__(self, config, counter=None, logger=None): def __init__(self, config, counter=None, logger=None):
"""Accepts a dictionary of configuration items to override defaults""" """Accepts a dictionary of configuration items to override defaults"""
settings = { settings = {
'alerts': ['log'], "alerts": ["log"],
'check_interval': 30, "check_interval": 30,
'alert_after': 4, "alert_after": 4,
'alert_every': -1, "alert_every": -1,
} }
settings.update(config) settings.update(config)
validate_monitor_settings(settings) validate_monitor_settings(settings)
self.name = settings['name'] self.name = settings["name"]
self.command = settings['command'] self.command = settings["command"]
self.alert_down = settings.get('alert_down', []) self.alert_down = settings.get("alert_down", [])
if not self.alert_down: if not self.alert_down:
self.alert_down = settings.get('alerts', []) self.alert_down = settings.get("alerts", [])
self.alert_up = settings.get('alert_up', []) self.alert_up = settings.get("alert_up", [])
self.check_interval = settings.get('check_interval') self.check_interval = settings.get("check_interval")
self.alert_after = settings.get('alert_after') self.alert_after = settings.get("alert_after")
self.alert_every = settings.get('alert_every') self.alert_every = settings.get("alert_every")
self.alert_count = 0 self.alert_count = 0
self.last_check = None self.last_check = None
@ -140,18 +135,18 @@ class Monitor(object):
self._counter = counter self._counter = counter
if logger is None: if logger is None:
self._logger = logging.getLogger( self._logger = logging.getLogger(
'{}({})'.format(self.__class__.__name__, self.name) "{}({})".format(self.__class__.__name__, self.name)
) )
else: else:
self._logger = logger.getChild( self._logger = logger.getChild(
'{}({})'.format(self.__class__.__name__, self.name) "{}({})".format(self.__class__.__name__, self.name)
) )
def _count_check(self, is_success=True, is_alert=False): def _count_check(self, is_success=True, is_alert=False):
if self._counter is not None: if self._counter is not None:
self._counter.labels( self._counter.labels(
monitor=self.name, monitor=self.name,
status=('success' if is_success else 'failure'), status=("success" if is_success else "failure"),
is_alert=is_alert, is_alert=is_alert,
).inc() ).inc()
@ -199,7 +194,7 @@ class Monitor(object):
back_up = None back_up = None
if not self.is_up(): if not self.is_up():
back_up = MinitorAlert( back_up = MinitorAlert(
'{} check is up again!'.format(self.name), "{} check is up again!".format(self.name),
self, self,
) )
self.total_failure_count = 0 self.total_failure_count = 0
@ -215,7 +210,7 @@ class Monitor(object):
if self.total_failure_count < self.alert_after: if self.total_failure_count < self.alert_after:
return return
failure_count = (self.total_failure_count - self.alert_after) failure_count = self.total_failure_count - self.alert_after
if self.alert_every > 0: if self.alert_every > 0:
# Otherwise, we should check against our alert_every # Otherwise, we should check against our alert_every
should_alert = (failure_count % self.alert_every) == 0 should_alert = (failure_count % self.alert_every) == 0
@ -223,15 +218,15 @@ class Monitor(object):
# Only alert on the first failure # Only alert on the first failure
should_alert = failure_count == 1 should_alert = failure_count == 1
else: else:
should_alert = (failure_count >= (2 ** self.alert_count) - 1) should_alert = failure_count >= (2**self.alert_count) - 1
if should_alert: if should_alert:
self.alert_count += 1 self.alert_count += 1
raise MinitorAlert( raise MinitorAlert(
'{} check has failed {} times'.format( "{} check has failed {} times".format(
self.name, self.total_failure_count self.name, self.total_failure_count
), ),
self self,
) )
def is_up(self): def is_up(self):
@ -243,18 +238,18 @@ class Alert(object):
def __init__(self, name, config, counter=None, logger=None): def __init__(self, name, config, counter=None, logger=None):
"""An alert must be named and have a config dict""" """An alert must be named and have a config dict"""
self.name = name self.name = name
self.command = config.get('command') self.command = config.get("command")
if not self.command: if not self.command:
raise InvalidAlertException('Invalid alert {}'.format(self.name)) raise InvalidAlertException("Invalid alert {}".format(self.name))
self._counter = counter self._counter = counter
if logger is None: if logger is None:
self._logger = logging.getLogger( self._logger = logging.getLogger(
'{}({})'.format(self.__class__.__name__, self.name) "{}({})".format(self.__class__.__name__, self.name)
) )
else: else:
self._logger = logger.getChild( self._logger = logger.getChild(
'{}({})'.format(self.__class__.__name__, self.name) "{}({})".format(self.__class__.__name__, self.name)
) )
def _count_alert(self, monitor): def _count_alert(self, monitor):
@ -277,7 +272,7 @@ class Alert(object):
def _format_datetime(self, dt): def _format_datetime(self, dt):
"""Formats a datetime for an alert""" """Formats a datetime for an alert"""
if dt is None: if dt is None:
return 'Never' return "Never"
return dt.isoformat() return dt.isoformat()
def alert(self, message, monitor): def alert(self, message, monitor):
@ -313,64 +308,72 @@ class Minitor(object):
def _parse_args(self, args=None): def _parse_args(self, args=None):
"""Parses command line arguments and returns them""" """Parses command line arguments and returns them"""
parser = ArgumentParser(description='Minimal monitoring') parser = ArgumentParser(description="Minimal monitoring")
parser.add_argument( parser.add_argument(
'--config', '-c', "--config",
dest='config_path', "-c",
default='config.yml', dest="config_path",
help='Path to the config YAML file to use', default="config.yml",
help="Path to the config YAML file to use",
) )
parser.add_argument( parser.add_argument(
'--metrics', '-m', "--metrics",
dest='metrics', "-m",
action='store_true', dest="metrics",
help='Start webserver with metrics', action="store_true",
help="Start webserver with metrics",
) )
parser.add_argument( parser.add_argument(
'--metrics-port', '-p', "--metrics-port",
dest='metrics_port', "-p",
dest="metrics_port",
type=int, type=int,
default=DEFAULT_METRICS_PORT, default=DEFAULT_METRICS_PORT,
help='Port to use when serving metrics', help="Port to use when serving metrics",
) )
parser.add_argument( parser.add_argument(
'--verbose', '-v', "--verbose",
action='count', "-v",
help=('Adjust log verbosity by increasing arg count. Default log', action="count",
'level is ERROR. Level increases with each `v`'), help=(
"Adjust log verbosity by increasing arg count. Default log",
"level is ERROR. Level increases with each `v`",
),
) )
return parser.parse_args(args) return parser.parse_args(args)
def _setup(self, config_path): def _setup(self, config_path):
"""Load all setup from YAML file at provided path""" """Load all setup from YAML file at provided path"""
config = read_yaml(config_path) config = read_yaml(config_path)
self.check_interval = config.get('check_interval', 30) self.check_interval = config.get("check_interval", 30)
self.monitors = [ self.monitors = [
Monitor( Monitor(
mon, mon,
counter=self._monitor_counter, counter=self._monitor_counter,
logger=self._logger, logger=self._logger,
) )
for mon in config.get('monitors', []) for mon in config.get("monitors", [])
] ]
# Add default alert for logging # Add default alert for logging
self.alerts = { self.alerts = {
'log': Alert( "log": Alert(
'log', "log",
{'command': ['echo', '{alert_message}!']}, {"command": ["echo", "{alert_message}!"]},
counter=self._alert_counter, counter=self._alert_counter,
logger=self._logger, logger=self._logger,
) )
} }
self.alerts.update({ self.alerts.update(
alert_name: Alert( {
alert_name, alert_name: Alert(
alert, alert_name,
counter=self._alert_counter, alert,
logger=self._logger, counter=self._alert_counter,
) logger=self._logger,
for alert_name, alert in config.get('alerts', {}).items() )
}) for alert_name, alert in config.get("alerts", {}).items()
}
)
def _validate_monitors(self): def _validate_monitors(self):
"""Validates monitors are valid against other config values""" """Validates monitors are valid against other config values"""
@ -378,7 +381,7 @@ class Minitor(object):
# Validate that the interval is valid # Validate that the interval is valid
if monitor.check_interval < self.check_interval: if monitor.check_interval < self.check_interval:
raise InvalidMonitorException( raise InvalidMonitorException(
'Monitor {} check interval is lower global value {}'.format( "Monitor {} check interval is lower global value {}".format(
monitor.name, self.check_interval monitor.name, self.check_interval
) )
) )
@ -386,26 +389,26 @@ class Minitor(object):
for alert in chain(monitor.alert_down, monitor.alert_up): for alert in chain(monitor.alert_down, monitor.alert_up):
if alert not in self.alerts: if alert not in self.alerts:
raise InvalidMonitorException( raise InvalidMonitorException(
'Monitor {} contains an unknown alert: {}'.format( "Monitor {} contains an unknown alert: {}".format(
monitor.name, alert monitor.name, alert
) )
) )
def _init_metrics(self): def _init_metrics(self):
self._alert_counter = Counter( self._alert_counter = Counter(
'minitor_alert_total', "minitor_alert_total",
'Number of Minitor alerts', "Number of Minitor alerts",
['alert', 'monitor'], ["alert", "monitor"],
) )
self._monitor_counter = Counter( self._monitor_counter = Counter(
'minitor_check_total', "minitor_check_total",
'Number of Minitor checks', "Number of Minitor checks",
['monitor', 'status', 'is_alert'], ["monitor", "status", "is_alert"],
) )
self._monitor_status_gauge = Gauge( self._monitor_status_gauge = Gauge(
'minitor_monitor_up_count', "minitor_monitor_up_count",
'Currently responsive monitors', "Currently responsive monitors",
['monitor'], ["monitor"],
) )
def _loop(self): def _loop(self):
@ -420,9 +423,7 @@ class Minitor(object):
result = monitor.check() result = monitor.check()
if result is not None: if result is not None:
self._logger.info( self._logger.info(
'%s: %s', "%s: %s", monitor.name, "SUCCESS" if result else "FAILURE"
monitor.name,
'SUCCESS' if result else 'FAILURE'
) )
except MinitorAlert as minitor_alert: except MinitorAlert as minitor_alert:
self._logger.warning(minitor_alert) self._logger.warning(minitor_alert)
@ -475,5 +476,5 @@ def main(args=None):
return 0 return 0
if __name__ == '__main__': if __name__ == "__main__":
sys.exit(main()) sys.exit(main())

View File

@ -7,47 +7,50 @@ from setuptools import setup
here = path.abspath(path.dirname(__file__)) here = path.abspath(path.dirname(__file__))
# Get the long description from the README file # Get the long description from the README file
with open(path.join(here, 'README.md'), encoding='utf-8') as f: with open(path.join(here, "README.md"), encoding="utf-8") as f:
long_description = f.read() long_description = f.read()
setup( setup(
name='minitor', name="minitor",
version='1.0.3', version="1.0.3",
description='A minimal monitoring tool', description="A minimal monitoring tool",
long_description=long_description, long_description=long_description,
long_description_content_type='text/markdown', long_description_content_type="text/markdown",
url='https://git.iamthefij.com/iamthefij/minitor', url="https://git.iamthefij.com/iamthefij/minitor",
download_url=( download_url=("https://git.iamthefij.com/iamthefij/minitor/archive/master.tar.gz"),
'https://git.iamthefij.com/iamthefij/minitor/archive/master.tar.gz' author="Ian Fijolek",
), author_email="ian@iamthefij.com",
author='Ian Fijolek',
author_email='ian@iamthefij.com',
classifiers=[ classifiers=[
'Development Status :: 5 - Production/Stable', "Development Status :: 5 - Production/Stable",
'Intended Audience :: Developers', "Intended Audience :: Developers",
'Intended Audience :: System Administrators', "Intended Audience :: System Administrators",
'Topic :: System :: Monitoring', "Topic :: System :: Monitoring",
'License :: OSI Approved :: Apache Software License', "License :: OSI Approved :: Apache Software License",
'Programming Language :: Python :: 3', "Programming Language :: Python :: 3",
'Programming Language :: Python :: 3.5', "Programming Language :: Python :: 3.6",
'Programming Language :: Python :: 3.6', "Programming Language :: Python :: 3.7",
'Programming Language :: Python :: 3.7', "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
], ],
keywords='minitor monitoring alerting', keywords="minitor monitoring alerting",
packages=find_packages(exclude=[ packages=find_packages(
'contrib', exclude=[
'docs', "contrib",
'examples', "docs",
'scripts', "examples",
'tests', "scripts",
]), "tests",
]
),
install_requires=[ install_requires=[
'prometheus_client', "prometheus_client",
'yamlenv', "yamlenv",
], ],
scripts=["scripts/docker_check.sh"],
entry_points={ entry_points={
'console_scripts': [ "console_scripts": [
'minitor=minitor.main:main', "minitor=minitor.main:main",
], ],
}, },
) )

View File

@ -9,54 +9,47 @@ from tests.util import assert_called_once_with
class TestAlert(object): class TestAlert(object):
@pytest.fixture @pytest.fixture
def monitor(self): def monitor(self):
return Monitor({ return Monitor(
'name': 'Dummy Monitor', {
'command': ['echo', 'foo'], "name": "Dummy Monitor",
}) "command": ["echo", "foo"],
}
)
@pytest.fixture @pytest.fixture
def echo_alert(self): def echo_alert(self):
return Alert( return Alert(
'log', "log",
{ {
'command': [ "command": [
'echo', ( "echo",
'{monitor_name} has failed {failure_count} time(s)!\n' (
'We have alerted {alert_count} time(s)\n' "{monitor_name} has failed {failure_count} time(s)!\n"
'Last success was {last_success}\n' "We have alerted {alert_count} time(s)\n"
'Last output was: {last_output}' "Last success was {last_success}\n"
) "Last output was: {last_output}"
),
] ]
} },
) )
@pytest.mark.parametrize( @pytest.mark.parametrize(
'last_success,expected_success', "last_success,expected_success",
[ [(None, "Never"), (datetime(2018, 4, 10), "2018-04-10T00:00:00")],
(None, 'Never'),
(datetime(2018, 4, 10), '2018-04-10T00:00:00')
]
) )
def test_simple_alert( def test_simple_alert(self, monitor, echo_alert, last_success, expected_success):
self,
monitor,
echo_alert,
last_success,
expected_success
):
monitor.alert_count = 1 monitor.alert_count = 1
monitor.last_output = 'beep boop' monitor.last_output = "beep boop"
monitor.last_success = last_success monitor.last_success = last_success
monitor.total_failure_count = 1 monitor.total_failure_count = 1
with patch.object(echo_alert._logger, 'error') as mock_error: with patch.object(echo_alert._logger, "error") as mock_error:
echo_alert.alert('Exception message', monitor) echo_alert.alert("Exception message", monitor)
assert_called_once_with( assert_called_once_with(
mock_error, mock_error,
'Dummy Monitor has failed 1 time(s)!\n' "Dummy Monitor has failed 1 time(s)!\n"
'We have alerted 1 time(s)\n' "We have alerted 1 time(s)\n"
'Last success was ' + expected_success + '\n' "Last success was " + expected_success + "\n"
'Last output was: beep boop' "Last output was: beep boop",
) )

View File

@ -6,30 +6,31 @@ from minitor.main import Minitor
class TestMinitor(object): class TestMinitor(object):
def test_call_output(self): def test_call_output(self):
# valid command should have result and no exception # valid command should have result and no exception
output, ex = call_output(['echo', 'test']) output, ex = call_output(["echo", "test"])
assert output == b'test' assert output == b"test"
assert ex is None assert ex is None
output, ex = call_output(['ls', '--not-a-real-flag']) output, ex = call_output(["ls", "--not-a-real-flag"])
assert output.startswith(b'ls: ') assert output.startswith(b"ls: ")
assert ex is not None assert ex is not None
def test_run(self): def test_run(self):
"""Doesn't really check much, but a simple integration sanity test""" """Doesn't really check much, but a simple integration sanity test"""
test_loop_count = 5 test_loop_count = 5
os.environ.update({ os.environ.update(
'MAILGUN_API_KEY': 'test-mg-key', {
'AVAILABLE_NUMBER': '555-555-5050', "MAILGUN_API_KEY": "test-mg-key",
'MY_PHONE': '555-555-0505', "AVAILABLE_NUMBER": "555-555-5050",
'ACCOUNT_SID': 'test-account-id', "MY_PHONE": "555-555-0505",
'AUTH_TOKEN': 'test-account-token', "ACCOUNT_SID": "test-account-id",
}) "AUTH_TOKEN": "test-account-token",
args = '--config ./sample-config.yml'.split(' ') }
)
args = "--config ./sample-config.yml".split(" ")
minitor = Minitor() minitor = Minitor()
with patch.object(minitor, '_loop'): with patch.object(minitor, "_loop"):
minitor.run(args) minitor.run(args)
# Skip the loop, but run a single check # Skip the loop, but run a single check
for _ in range(test_loop_count): for _ in range(test_loop_count):

View File

@ -11,40 +11,44 @@ from tests.util import assert_called_once
class TestMonitor(object): class TestMonitor(object):
@pytest.fixture @pytest.fixture
def monitor(self): def monitor(self):
return Monitor({ return Monitor(
'name': 'Sample Monitor', {
'command': ['echo', 'foo'], "name": "Sample Monitor",
'alert_down': ['log'], "command": ["echo", "foo"],
'alert_up': ['log'], "alert_down": ["log"],
'check_interval': 1, "alert_up": ["log"],
'alert_after': 1, "check_interval": 1,
'alert_every': 1, "alert_after": 1,
}) "alert_every": 1,
}
)
@pytest.mark.parametrize('settings', [ @pytest.mark.parametrize(
{'alert_after': 0}, "settings",
{'alert_every': 0}, [
{'check_interval': 0}, {"alert_after": 0},
{'alert_after': 'invalid'}, {"alert_every": 0},
{'alert_every': 'invalid'}, {"check_interval": 0},
{'check_interval': 'invalid'}, {"alert_after": "invalid"},
]) {"alert_every": "invalid"},
{"check_interval": "invalid"},
],
)
def test_monitor_invalid_configuration(self, settings): def test_monitor_invalid_configuration(self, settings):
with pytest.raises(InvalidMonitorException): with pytest.raises(InvalidMonitorException):
validate_monitor_settings(settings) validate_monitor_settings(settings)
@pytest.mark.parametrize( @pytest.mark.parametrize(
'alert_after', "alert_after",
[1, 20], [1, 20],
ids=lambda arg: 'alert_after({})'.format(arg), ids=lambda arg: "alert_after({})".format(arg),
) )
@pytest.mark.parametrize( @pytest.mark.parametrize(
'alert_every', "alert_every",
[-1, 1, 2, 1440], [-1, 1, 2, 1440],
ids=lambda arg: 'alert_every({})'.format(arg), ids=lambda arg: "alert_every({})".format(arg),
) )
def test_monitor_alert_after(self, monitor, alert_after, alert_every): def test_monitor_alert_after(self, monitor, alert_after, alert_every):
monitor.alert_after = alert_after monitor.alert_after = alert_after
@ -59,14 +63,14 @@ class TestMonitor(object):
monitor.failure() monitor.failure()
@pytest.mark.parametrize( @pytest.mark.parametrize(
'alert_after', "alert_after",
[1, 20], [1, 20],
ids=lambda arg: 'alert_after({})'.format(arg), ids=lambda arg: "alert_after({})".format(arg),
) )
@pytest.mark.parametrize( @pytest.mark.parametrize(
'alert_every', "alert_every",
[1, 2, 1440], [1, 2, 1440],
ids=lambda arg: 'alert_every({})'.format(arg), ids=lambda arg: "alert_every({})".format(arg),
) )
def test_monitor_alert_every(self, monitor, alert_after, alert_every): def test_monitor_alert_every(self, monitor, alert_after, alert_every):
monitor.alert_after = alert_after monitor.alert_after = alert_after
@ -102,27 +106,27 @@ class TestMonitor(object):
else: else:
monitor.failure() monitor.failure()
@pytest.mark.parametrize('last_check', [None, datetime(2018, 4, 10)]) @pytest.mark.parametrize("last_check", [None, datetime(2018, 4, 10)])
def test_monitor_should_check(self, monitor, last_check): def test_monitor_should_check(self, monitor, last_check):
monitor.last_check = last_check monitor.last_check = last_check
assert monitor.should_check() assert monitor.should_check()
def test_monitor_check_fail(self, monitor): def test_monitor_check_fail(self, monitor):
assert monitor.last_output is None assert monitor.last_output is None
with patch.object(monitor, 'failure') as mock_failure: with patch.object(monitor, "failure") as mock_failure:
monitor.command = ['ls', '--not-real'] monitor.command = ["ls", "--not-real"]
assert not monitor.check() assert not monitor.check()
assert_called_once(mock_failure) assert_called_once(mock_failure)
assert monitor.last_output is not None assert monitor.last_output is not None
def test_monitor_check_success(self, monitor): def test_monitor_check_success(self, monitor):
assert monitor.last_output is None assert monitor.last_output is None
with patch.object(monitor, 'success') as mock_success: with patch.object(monitor, "success") as mock_success:
assert monitor.check() assert monitor.check()
assert_called_once(mock_success) assert_called_once(mock_success)
assert monitor.last_output is not None assert monitor.last_output is not None
@pytest.mark.parametrize('failure_count', [0, 1]) @pytest.mark.parametrize("failure_count", [0, 1])
def test_monitor_success(self, monitor, failure_count): def test_monitor_success(self, monitor, failure_count):
monitor.alert_count = 0 monitor.alert_count = 0
monitor.total_failure_count = failure_count monitor.total_failure_count = failure_count