Skip to content

Commit

Permalink
Add more tests (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettheel committed Dec 23, 2016
1 parent 32de047 commit ae198d6
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 26 deletions.
25 changes: 10 additions & 15 deletions graphite_beacon/core.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import logging
import os
import sys
from re import compile as re
Expand Down Expand Up @@ -92,9 +91,7 @@ def reinit(self, **options): # pylint: disable=unused-argument
self.reinit_handlers('critical')
self.reinit_handlers('normal')

for alert in list(self.alerts):
alert.stop()
self.alerts.remove(alert)
self.remove_alerts()

self.alerts = set(
BaseAlert.get(self, **opts) for opts in self.options.get('alerts')) # pylint: disable=no-member
Expand All @@ -107,6 +104,11 @@ def reinit(self, **options): # pylint: disable=unused-argument
LOGGER.debug(json.dumps(self.options, indent=2))
return self

def remove_alerts(self):
for alert in list(self.alerts):
alert.stop()
self.alerts.remove(alert)

def start_alerts(self):
for alert in self.alerts:
alert.start()
Expand Down Expand Up @@ -159,9 +161,11 @@ def start(self, start_loop=True):
if start_loop:
self.loop.start()

def stop(self):
def stop(self, stop_loop=True):
self.callback.stop()
self.loop.stop()
self.remove_alerts()
if stop_loop:
self.loop.stop()
if self.options.get('pidfile'):
os.unlink(self.options.get('pidfile'))
LOGGER.info('Reactor has stopped')
Expand All @@ -177,15 +181,6 @@ def notify(self, level, alert, value, target=None, ntype=None, rule=None):
for handler in self.handlers.get(level, []):
handler.notify(level, alert, value, target=target, ntype=ntype, rule=rule)

_LOG_LEVELS = {
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARN': logging.WARN,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL
}


def _get_loader(config):
"""Determine which config file type and loader to use based on a filename.
Expand Down
18 changes: 11 additions & 7 deletions tests/integration/graphite_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from graphite_beacon.alerts import GraphiteAlert
from graphite_beacon.core import Reactor
from graphite_beacon._compat import StringIO

from ..util import build_graphite_response

Expand All @@ -22,7 +23,7 @@ def get_new_ioloop(self):
@mock.patch('graphite_beacon.handlers.smtp.SMTPHandler.notify')
@gen_test
def test_graphite(self, mock_smpt_notify, mock_fetch):
rr = Reactor(
self.reactor = Reactor(
alerts=[
{
'name': 'test',
Expand All @@ -39,20 +40,20 @@ def test_graphite(self, mock_smpt_notify, mock_fetch):
until='1minute',
)

assert not rr.is_running()
assert not self.reactor.is_running()

alert = list(rr.alerts)[0]
assert len(rr.alerts) == 1
alert = list(self.reactor.alerts)[0]
assert len(self.reactor.alerts) == 1
assert isinstance(alert, GraphiteAlert)

metric_data = [5, 7, 9]
build_resp = lambda: HTTPResponse(HTTPRequest('http://localhost:80/graphite'), 200,
buffer=build_graphite_response(data=metric_data))
buffer=StringIO(build_graphite_response(data=metric_data)))

mock_fetch.side_effect = iter(tornado.gen.maybe_future(build_resp())
for _ in range(100))
for _ in range(10))

rr.start(start_loop=False)
self.reactor.start(start_loop=False)
yield tornado.gen.sleep(0.5)

# There should be at least 1 immediate fetch + 1 instance of the PeriodicCallback
Expand All @@ -62,6 +63,7 @@ def test_graphite(self, mock_smpt_notify, mock_fetch):
assert fetch_mock_url(mock_fetch) == expected

assert alert.state['*'] == 'warning'

assert mock_smpt_notify.call_count == 1
mock_smpt_notify.assert_called_once_with(
'warning',
Expand All @@ -70,3 +72,5 @@ def test_graphite(self, mock_smpt_notify, mock_fetch):
ntype='graphite',
rule=ANY,
target='*')

self.reactor.stop(stop_loop=False)
74 changes: 74 additions & 0 deletions tests/integration/url_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import mock
import tornado.gen
from mock import ANY
from tornado import ioloop
from tornado.httpclient import HTTPRequest, HTTPResponse
from tornado.testing import AsyncTestCase, gen_test

from graphite_beacon.alerts import URLAlert
from graphite_beacon.core import Reactor
from graphite_beacon._compat import StringIO

from ..util import build_graphite_response

fetch_mock_url = lambda m: m.call_args_list[0][0][0]


class TestGraphite(AsyncTestCase):

target_url = 'http://localhost/check'

def get_new_ioloop(self):
return ioloop.IOLoop.instance()

@mock.patch('graphite_beacon.alerts.hc.AsyncHTTPClient.fetch')
@mock.patch('graphite_beacon.handlers.smtp.SMTPHandler.notify')
@gen_test
def test_graphite(self, mock_smpt_notify, mock_fetch):
self.reactor = Reactor(
alerts=[
{
'name': 'test',
'source': 'url',
'query': self.target_url,
'rules': ['warning: != 200']
}
],
smtp={
'from': 'graphite@localhost',
'to': ['alerts@localhost'],
},
interval='0.25second',
)

assert not self.reactor.is_running()

alert = list(self.reactor.alerts)[0]
assert len(self.reactor.alerts) == 1
assert isinstance(alert, URLAlert)

metric_data = [5, 7, 9]
build_resp = lambda: HTTPResponse(HTTPRequest(self.target_url), 500,
buffer=StringIO(''))

mock_fetch.side_effect = iter(tornado.gen.maybe_future(build_resp())
for _ in range(10))

self.reactor.start(start_loop=False)
yield tornado.gen.sleep(0.5)

# There should be at least 1 immediate fetch + 1 instance of the PeriodicCallback
assert mock_fetch.call_count >= 2
assert fetch_mock_url(mock_fetch) == self.target_url

assert alert.state[self.target_url] == 'warning'
assert mock_smpt_notify.call_count == 1
mock_smpt_notify.assert_called_once_with(
'warning',
alert,
500.0,
ntype='url',
rule=ANY,
target=self.target_url)

self.reactor.stop(stop_loop=False)
Empty file added tests/unit/__init__.py
Empty file.
45 changes: 45 additions & 0 deletions tests/unit/graphite_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import pytest

from graphite_beacon.graphite import GraphiteRecord

from ..util import build_graphite_response


build_record = lambda data: GraphiteRecord(build_graphite_response(data=data))


class TestGraphiteRecord(object):
def test_invalid_record(self):
with pytest.raises(ValueError):
GraphiteRecord('not,legit,data')

def test_invalid_record_long(self):
with pytest.raises(ValueError) as e:
GraphiteRecord('<http>' + ('<tag>' * 50))
assert '<http>' in str(e.value)
assert str(e.value).endswith('..')

def test_record(self):
assert build_record([1, 2, 3]).values == [1.0, 2.0, 3.0]

def test_average(self):
assert build_record([1]).average == 1.0
assert build_record([1, 2, 3]).average == 2.0
assert build_record([5, 5, 5, 10]).average == 6.25
assert build_record([1.5, 2.5, 3.5]).average == 2.5

def test_last_value(self):
assert build_record([1]).last_value == 1.0
assert build_record([1, 2, 3]).last_value == 3.0

def test_sum(self):
assert build_record([1]).sum == 1.0
assert build_record([1.5, 2.5, 3]).sum == 7.0

def test_minimum(self):
assert build_record([1]).minimum == 1.0
assert build_record([9.0, 2.3, 4]).minimum == 2.3

def test_maximum(self):
assert build_record([1]).maximum == 1.0
assert build_record([9.0, 2.3, 4]).maximum == 9.0
9 changes: 5 additions & 4 deletions tests/util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import random

from graphite_beacon._compat import StringIO



def build_graphite_response(target_name='*', start_timestamp=1480000000,
Expand All @@ -18,7 +18,8 @@ def build_graphite_response(target_name='*', start_timestamp=1480000000,
:rtype: StringIO
"""
data = data or []
return StringIO(
"{},{},{},{}|{}".format(target_name, start_timestamp, end_timestamp,
series_step, ','.join(str(d) for d in data))
return (
"{},{},{},{}|{}"
.format(target_name, start_timestamp, end_timestamp, series_step,
','.join(str(d) for d in data))
)

0 comments on commit ae198d6

Please sign in to comment.