Improve test coverage (#4)

This commit is contained in:
Major Hayden 2017-03-07 16:34:14 -06:00 committed by GitHub
parent 38eb764c4c
commit f943e47f70
10 changed files with 230 additions and 52 deletions

View File

@ -1,6 +1,9 @@
CHANGES
=======
* Improve test coverage
* Remove sys from uptime test (#3)
* Increase test coverage for uptime
* Added KVM Metric plugin (#2)
* Couple of updates: telegraf line protocol, dynamic imports, metadata (#1)
* Proof of concept

View File

@ -14,11 +14,9 @@
# limitations under the License.
"""Handle all shell commands/arguments/options."""
import importlib
import json
import os
import pkgutil
import sys
import time
import click
@ -26,12 +24,6 @@ import click
context_settings = dict(auto_envvar_prefix='MonitorStack')
def current_time():
"""Return the current time in nanoseconds"""
return int(time.time() * 1000000000)
class Context(object):
"""Set up a context object that we can pass."""
@ -42,8 +34,6 @@ class Context(object):
def log(self, msg, *args):
"""Log a message to stderr."""
if args:
msg %= args
click.echo(msg, file=sys.stderr)
def vlog(self, msg, *args):
@ -60,6 +50,7 @@ class MonitorStackCLI(click.MultiCommand):
@property
def cmd_folder(self):
"""Get the path to the plugin directory."""
return os.path.abspath(
os.path.join(
os.path.dirname(__file__),
@ -115,40 +106,13 @@ def cli(ctx, output_format, verbose):
@cli.resultcallback(replace=True)
def process_result(result, output_format, verbose):
"""Render the output into the proper format."""
if output_format == 'json':
click.echo(json.dumps(result, indent=2))
elif output_format == 'line':
for key, value in result['variables'].items():
click.echo("{} {}".format(key, value))
elif output_format == 'telegraf':
def line_format(sets, quote=False):
store = list()
for k, v in sets.items():
k = k.replace(' ', '_')
for v_type in [int, float]:
try:
v = v_type(v)
except ValueError:
pass # v was not a int, float, or long
else:
break
if not isinstance(v, (int, float, bool)) and quote:
store.append('{}="{}"'.format(k, v))
else:
store.append('{}={}'.format(k, v))
return ','.join(store).rstrip(',')
resultant = [result['measurement_name']]
if 'meta' in result:
resultant.append(line_format(sets=result['meta']))
resultant.append(line_format(sets=result['variables'], quote=True))
resultant.append(current_time())
click.echo(' '.join(resultant))
elif output_format == 'csv':
pass
module_name = 'monitorstack.common.formatters'
method_name = 'write_{}'.format(output_format)
output_formatter = getattr(
importlib.import_module(module_name),
method_name
)
output_formatter(result)
if __name__ == '__main__':

View File

View File

@ -0,0 +1,67 @@
# Copyright 2017, Major Hayden <major@mhtx.net>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Output methods."""
import json
import time
import click
def current_time():
"""Return the current time in nanoseconds."""
return int(time.time() * 1000000000)
def write_json(result):
"""Output in raw JSON format."""
output = json.dumps(result, indent=2)
click.echo(output)
return True
def write_line(result):
"""Output in line format."""
for key, value in result['variables'].items():
click.echo("{} {}".format(key, value))
return True
def write_telegraf(result):
"""Output in telegraf format."""
def line_format(sets, quote=False):
store = list()
for k, v in sets.items():
k = k.replace(' ', '_')
for v_type in [int, float]:
try:
v = v_type(v)
except ValueError:
pass # v was not a int, float, or long
else:
break
if not isinstance(v, (int, float, bool)) and quote:
store.append('{}="{}"'.format(k, v))
else:
store.append('{}={}'.format(k, v))
return ','.join(store).rstrip(',')
resultant = [result['measurement_name']]
if 'meta' in result:
resultant.append(line_format(sets=result['meta']))
resultant.append(line_format(sets=result['variables'], quote=True))
resultant.append(str(current_time()))
click.echo(' '.join(resultant))
return True

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright 2017, Kevin Carter <kevin@cloudnull.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
@ -12,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Plugin for KVM metrics."""
import platform
import socket

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright 2017, Major Hayden <major@mhtx.net>
#
# Licensed under the Apache License, Version 2.0 (the "License");
@ -12,8 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Base monitoring class."""
"""Get system uptime."""
import platform

85
tests/test_cli.py Normal file
View File

@ -0,0 +1,85 @@
# Copyright 2017, Major Hayden <major@mhtx.net>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the base cli module."""
import click
from monitorstack.cli import Context
from monitorstack.cli import MonitorStackCLI
import pytest
class TestCLI(object):
"""Tests for the base cli module."""
def test_context_log(self, monkeypatch):
"""Test log() method of Context class."""
def echofixer(msg, file):
return msg
monkeypatch.setattr(click, 'echo', echofixer)
context = Context()
result = context.log("TEST", 'test')
assert callable(context.log)
assert result is None
def test_context_vlog_verbose_disabled(self, monkeypatch):
"""Test vlog() method of Context class."""
def echofixer(msg, file):
return msg
monkeypatch.setattr(click, 'echo', echofixer)
context = Context()
context.verbose = False
result = context.vlog("TEST", 'test')
assert callable(context.vlog)
assert result is None
def test_context_vlog_verbose_enabled(self, monkeypatch):
"""Test vlog() method of Context class."""
def echofixer(msg, file):
return msg
monkeypatch.setattr(click, 'echo', echofixer)
context = Context()
context.verbose = True
result = context.vlog("TEST", 'test')
assert callable(context.vlog)
assert result is None
def test_get_command_invalid(self):
"""Test MonitorStackCLI.get_command()."""
ctx = Context()
cli = MonitorStackCLI()
with pytest.raises(SystemExit) as excinfo:
cli.get_command(ctx, 'silly_rabbit_trix_are_for_kids')
assert 'silly_rabbit_trix_are_for_kids' in str(excinfo.value)
assert 'Not Found' in str(excinfo.value)
def test_get_command_valid(self):
"""Test MonitorStackCLI.get_command()."""
ctx = Context()
cli = MonitorStackCLI()
result = cli.get_command(ctx, 'uptime')
assert isinstance(result, object)
assert callable(result)
def test_list_commands(self):
"""Test MonitorStackCLI.list_commands()."""
ctx = Context()
cli = MonitorStackCLI()
result = cli.list_commands(ctx)
assert isinstance(result, list)
assert len(result) > 1
assert 'uptime' in result

63
tests/test_formatters.py Normal file
View File

@ -0,0 +1,63 @@
# Copyright 2017, Major Hayden <major@mhtx.net>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests the output methods."""
import json
import platform
from monitorstack.common import formatters
SAMPLE_RESULT = {
'exit_code': 0,
'message': 'uptime is ok',
'measurement_name': 'system_uptime',
'meta': {
'platform': platform.platform(),
},
'variables': {
'uptime': '29587.75'
}
}
class TestFormatters(object):
"""Tests for the base cli module."""
def test_current_time(self):
"""Test current_time()."""
result = formatters.current_time()
assert isinstance(result, int)
assert result > 0
def test_write_json(self, capsys):
"""Test write_json() module."""
formatters.write_json(SAMPLE_RESULT)
out, err = capsys.readouterr()
result_json = json.loads(out)
assert isinstance(result_json, dict)
assert result_json['measurement_name'] == \
SAMPLE_RESULT['measurement_name']
def test_write_line(self, capsys):
"""Test write_line() module."""
formatters.write_line(SAMPLE_RESULT)
out, err = capsys.readouterr()
assert out == "uptime {}\n".format(
SAMPLE_RESULT['variables']['uptime']
)
def test_write_telegraf(self, capsys):
"""Test write_telegraf() module."""
formatters.write_telegraf(SAMPLE_RESULT)
out, err = capsys.readouterr()
assert out.startswith(SAMPLE_RESULT['measurement_name'])

View File

@ -11,8 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the base class."""
"""Tests for the KVM plugin."""
import json
import sys

View File

@ -11,8 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the base class."""
"""Tests for the uptime plugin."""
import json