Add unit tests
This commit is contained in:
parent
eb7291590b
commit
f1297f72a5
|
@ -1,2 +1,5 @@
|
|||
.tox
|
||||
.testrepository
|
||||
.unit-state.db
|
||||
.stestr/
|
||||
__pycache__/
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[DEFAULT]
|
||||
test_path=./unit_tests
|
||||
top_dir=./
|
|
@ -1,3 +1,12 @@
|
|||
name: pacemaker-remote
|
||||
summary: Basic pacemaker-remote interface
|
||||
version: 1
|
||||
ignore:
|
||||
- 'unit_tests'
|
||||
- 'Makefile'
|
||||
- '.testr.conf'
|
||||
- 'test-requirements.txt'
|
||||
- 'tox.ini'
|
||||
- '.gitignore'
|
||||
- '.gitreview'
|
||||
- '.unit-state.db'
|
||||
|
|
13
provides.py
13
provides.py
|
@ -2,23 +2,28 @@ import base64
|
|||
from charms.reactive import Endpoint
|
||||
|
||||
|
||||
class PacemakerProvides(Endpoint):
|
||||
class PacemakerRemoteProvides(Endpoint):
|
||||
|
||||
def publish_info(self, stonith_hostname=None):
|
||||
def publish_info(self, remote_hostname, stonith_hostname=None,
|
||||
enable_resources=True):
|
||||
"""
|
||||
Publish the stonith info
|
||||
"""
|
||||
for relation in self.relations:
|
||||
relation.to_publish['stonith-hostname'] = stonith_hostname
|
||||
relation.to_publish['remote-hostname'] = remote_hostname
|
||||
relation.to_publish['enable-resources'] = enable_resources
|
||||
|
||||
def get_pacemaker_key(self):
|
||||
for relation in self.relations:
|
||||
pacemaker_keys = []
|
||||
for unit in relation.units:
|
||||
pacemaker_keys.append(unit.received['pacemaker-key'])
|
||||
key = unit.received.get('pacemaker-key')
|
||||
if key:
|
||||
pacemaker_keys.append(key)
|
||||
unique_keys = len(set(pacemaker_keys))
|
||||
if unique_keys > 1:
|
||||
raise Exception("Inconsistent keys")
|
||||
elif unique_keys == 1:
|
||||
return base64.decode(unique_keys[0])
|
||||
return base64.b64decode(pacemaker_keys[0])
|
||||
return None
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Lint and unit test requirements
|
||||
flake8
|
||||
os-testr>=0.4.1
|
||||
charms.reactive
|
||||
mock>=1.2
|
||||
coverage>=3.6
|
|
@ -0,0 +1,28 @@
|
|||
[tox]
|
||||
skipsdist = True
|
||||
envlist = pep8,py3
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
PYTHONHASHSEED=0
|
||||
TERM=linux
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
|
||||
[testenv:py3]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = flake8 {posargs} .
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
# E402 ignore necessary for path append before sys module import in actions
|
||||
ignore = E402
|
|
@ -0,0 +1,147 @@
|
|||
import unittest
|
||||
import mock
|
||||
|
||||
|
||||
with mock.patch('charmhelpers.core.hookenv.metadata') as _meta:
|
||||
_meta.return_Value = 'ss'
|
||||
import provides
|
||||
|
||||
|
||||
_hook_args = {}
|
||||
|
||||
TO_PATCH = [
|
||||
]
|
||||
|
||||
|
||||
def mock_hook(*args, **kwargs):
|
||||
|
||||
def inner(f):
|
||||
# remember what we were passed. Note that we can't actually determine
|
||||
# the class we're attached to, as the decorator only gets the function.
|
||||
_hook_args[f.__name__] = dict(args=args, kwargs=kwargs)
|
||||
return f
|
||||
return inner
|
||||
|
||||
|
||||
class _unit_mock:
|
||||
def __init__(self, unit_name, received=None):
|
||||
self.unit_name = unit_name
|
||||
self.received = received or {}
|
||||
|
||||
|
||||
class _relation_mock:
|
||||
def __init__(self, application_name=None, units=None):
|
||||
self.to_publish_raw = {}
|
||||
self.to_publish = {}
|
||||
self.application_name = application_name
|
||||
self.units = units
|
||||
|
||||
|
||||
class TestPacemakerRemoteProvides(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls._patched_hook = mock.patch('charms.reactive.when', mock_hook)
|
||||
cls._patched_hook_started = cls._patched_hook.start()
|
||||
# force provides to rerun the mock_hook decorator:
|
||||
# try except is Python2/Python3 compatibility as Python3 has moved
|
||||
# reload to importlib.
|
||||
try:
|
||||
reload(provides)
|
||||
except NameError:
|
||||
import importlib
|
||||
importlib.reload(provides)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls._patched_hook.stop()
|
||||
cls._patched_hook_started = None
|
||||
cls._patched_hook = None
|
||||
# and fix any breakage we did to the module
|
||||
try:
|
||||
reload(provides)
|
||||
except NameError:
|
||||
import importlib
|
||||
importlib.reload(provides)
|
||||
|
||||
def patch(self, method):
|
||||
_m = mock.patch.object(self.obj, method)
|
||||
_mock = _m.start()
|
||||
self.addCleanup(_m.stop)
|
||||
return _mock
|
||||
|
||||
def setUp(self):
|
||||
self.relation_obj = provides.PacemakerRemoteProvides(
|
||||
'some-relation',
|
||||
[])
|
||||
self._patches = {}
|
||||
self._patches_start = {}
|
||||
self.obj = provides
|
||||
for method in TO_PATCH:
|
||||
setattr(self, method, self.patch(method))
|
||||
|
||||
def tearDown(self):
|
||||
self.relation_obj = None
|
||||
for k, v in self._patches.items():
|
||||
v.stop()
|
||||
setattr(self, k, None)
|
||||
self._patches = None
|
||||
self._patches_start = None
|
||||
|
||||
def patch_relation_obj(self, attr, return_value=None):
|
||||
mocked = mock.patch.object(self.relation_obj, attr)
|
||||
self._patches[attr] = mocked
|
||||
started = mocked.start()
|
||||
started.return_value = return_value
|
||||
self._patches_start[attr] = started
|
||||
setattr(self, attr, started)
|
||||
|
||||
def test_publish_info(self):
|
||||
mock_rel = _relation_mock()
|
||||
self.relation_obj._relations = [mock_rel]
|
||||
self.relation_obj.publish_info(
|
||||
'node1.az1.local',
|
||||
stonith_hostname='node1.stonith',
|
||||
enable_resources=True)
|
||||
expect = {
|
||||
'remote-hostname': 'node1.az1.local',
|
||||
'stonith-hostname': 'node1.stonith',
|
||||
'enable-resources': True}
|
||||
self.assertEqual(
|
||||
mock_rel.to_publish,
|
||||
expect)
|
||||
|
||||
def test_get_pacemaker_key(self):
|
||||
unit1 = _unit_mock(
|
||||
'unit1',
|
||||
received={'pacemaker-key': 'cG1ha2Vya2V5MQo='})
|
||||
mock_rel = _relation_mock(units=[unit1])
|
||||
self.relation_obj._relations = [mock_rel]
|
||||
self.assertEqual(
|
||||
self.relation_obj.get_pacemaker_key(),
|
||||
b'pmakerkey1\n')
|
||||
|
||||
def test_get_pacemaker_key_inconsistent(self):
|
||||
unit1 = _unit_mock(
|
||||
'unit1',
|
||||
received={'pacemaker-key': 'cG1ha2Vya2V5MQo='})
|
||||
unit2 = _unit_mock(
|
||||
'unit2',
|
||||
received={'pacemaker-key': 'cG1ha2Vya2V5Mgo='})
|
||||
mock_rel = _relation_mock(units=[unit1, unit2])
|
||||
self.relation_obj._relations = [mock_rel]
|
||||
with self.assertRaises(Exception):
|
||||
self.relation_obj.get_pacemaker_key()
|
||||
|
||||
def test_get_pacemaker_key_missing(self):
|
||||
unit1 = _unit_mock(
|
||||
'unit1',
|
||||
received={})
|
||||
unit2 = _unit_mock(
|
||||
'unit2',
|
||||
received={})
|
||||
mock_rel = _relation_mock(units=[unit1, unit2])
|
||||
self.relation_obj._relations = [mock_rel]
|
||||
self.assertEqual(
|
||||
self.relation_obj.get_pacemaker_key(),
|
||||
None)
|
Loading…
Reference in New Issue