Add tests architecture
This commit is contained in:
parent
5ff6ab5518
commit
1056b8b6d7
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.coverage
|
.coverage
|
||||||
.tox
|
.tox
|
||||||
|
.testrepository
|
||||||
AUTHORS
|
AUTHORS
|
||||||
ChangeLog
|
ChangeLog
|
||||||
bilean.egg-info/
|
bilean.egg-info/
|
||||||
|
6
.testr.conf
Normal file
6
.testr.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
test_command=
|
||||||
|
PYTHON=$(echo ${PYTHON:-python} | sed 's/--source bilean//g')
|
||||||
|
${PYTHON} -m subunit.run discover ${OS_TEST_PATH:-./bilean/tests} -t . $LISTOPT $IDOPTION
|
||||||
|
test_id_option=--load-list $IDFILE
|
||||||
|
test_list_option=--list
|
@ -55,7 +55,7 @@ class JsonPayloadSerializer(oslo_messaging.NoOpSerializer):
|
|||||||
|
|
||||||
def setup(url=None, optional=False):
|
def setup(url=None, optional=False):
|
||||||
"""Initialise the oslo_messaging layer."""
|
"""Initialise the oslo_messaging layer."""
|
||||||
global TRANSPORT, TRANSPORTS, NOTIFIER
|
global TRANSPORT, NOTIFIER
|
||||||
|
|
||||||
if url and url.startswith("fake://"):
|
if url and url.startswith("fake://"):
|
||||||
# NOTE(sileht): oslo_messaging fake driver uses time.sleep
|
# NOTE(sileht): oslo_messaging fake driver uses time.sleep
|
||||||
@ -78,7 +78,6 @@ def setup(url=None, optional=False):
|
|||||||
if not NOTIFIER and TRANSPORT:
|
if not NOTIFIER and TRANSPORT:
|
||||||
serializer = RequestContextSerializer(JsonPayloadSerializer())
|
serializer = RequestContextSerializer(JsonPayloadSerializer())
|
||||||
NOTIFIER = oslo_messaging.Notifier(TRANSPORT, serializer=serializer)
|
NOTIFIER = oslo_messaging.Notifier(TRANSPORT, serializer=serializer)
|
||||||
TRANSPORTS[url] = TRANSPORT
|
|
||||||
|
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
|
24
bilean/tests/__init__.py
Normal file
24
bilean/tests/__init__.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import oslo_i18n
|
||||||
|
|
||||||
|
|
||||||
|
def fake_translate_msgid(msgid, domain, desired_locale=None):
|
||||||
|
return msgid
|
||||||
|
|
||||||
|
oslo_i18n.enable_lazy()
|
||||||
|
|
||||||
|
# To ensure messages don't really get translated while running tests.
|
||||||
|
# As there are lots of places where matching is expected when comparing
|
||||||
|
# exception message(translated) with raw message.
|
||||||
|
oslo_i18n._translate_msgid = fake_translate_msgid
|
0
bilean/tests/common/__init__.py
Normal file
0
bilean/tests/common/__init__.py
Normal file
78
bilean/tests/common/base.py
Normal file
78
bilean/tests/common/base.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import fixtures
|
||||||
|
from oslotest import mockpatch
|
||||||
|
import testscenarios
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
from bilean.common import messaging
|
||||||
|
from bilean.tests.common import utils
|
||||||
|
|
||||||
|
|
||||||
|
TEST_DEFAULT_LOGLEVELS = {'migrate': logging.WARN,
|
||||||
|
'sqlalchemy': logging.WARN}
|
||||||
|
_LOG_FORMAT = "%(levelname)8s [%(name)s] %(message)s"
|
||||||
|
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
||||||
|
|
||||||
|
|
||||||
|
class FakeLogMixin(object):
|
||||||
|
def setup_logging(self):
|
||||||
|
# Assign default logs to self.LOG so we can still
|
||||||
|
# assert on bilean logs.
|
||||||
|
default_level = logging.INFO
|
||||||
|
if os.environ.get('OS_DEBUG') in _TRUE_VALUES:
|
||||||
|
default_level = logging.DEBUG
|
||||||
|
|
||||||
|
self.LOG = self.useFixture(
|
||||||
|
fixtures.FakeLogger(level=default_level, format=_LOG_FORMAT))
|
||||||
|
base_list = set([nlog.split('.')[0]
|
||||||
|
for nlog in logging.Logger.manager.loggerDict])
|
||||||
|
for base in base_list:
|
||||||
|
if base in TEST_DEFAULT_LOGLEVELS:
|
||||||
|
self.useFixture(fixtures.FakeLogger(
|
||||||
|
level=TEST_DEFAULT_LOGLEVELS[base],
|
||||||
|
name=base, format=_LOG_FORMAT))
|
||||||
|
elif base != 'bilean':
|
||||||
|
self.useFixture(fixtures.FakeLogger(
|
||||||
|
name=base, format=_LOG_FORMAT))
|
||||||
|
|
||||||
|
|
||||||
|
class BileanTestCase(testscenarios.WithScenarios,
|
||||||
|
testtools.TestCase, FakeLogMixin):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(BileanTestCase, self).setUp()
|
||||||
|
self.setup_logging()
|
||||||
|
self.useFixture(fixtures.MonkeyPatch(
|
||||||
|
'bilean.common.exception._FATAL_EXCEPTION_FORMAT_ERRORS',
|
||||||
|
True))
|
||||||
|
|
||||||
|
messaging.setup("fake://", optional=True)
|
||||||
|
self.addCleanup(messaging.cleanup)
|
||||||
|
|
||||||
|
utils.setup_dummy_db()
|
||||||
|
self.addCleanup(utils.reset_dummy_db)
|
||||||
|
|
||||||
|
def patchobject(self, obj, attr, **kwargs):
|
||||||
|
mockfixture = self.useFixture(mockpatch.PatchObject(obj, attr,
|
||||||
|
**kwargs))
|
||||||
|
return mockfixture.mock
|
||||||
|
|
||||||
|
# NOTE(pshchelo): this overrides the testtools.TestCase.patch method
|
||||||
|
# that does simple monkey-patching in favor of mock's patching
|
||||||
|
def patch(self, target, **kwargs):
|
||||||
|
mockfixture = self.useFixture(mockpatch.Patch(target, **kwargs))
|
||||||
|
return mockfixture.mock
|
85
bilean/tests/common/fakes.py
Normal file
85
bilean/tests/common/fakes.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
A fake server that "responds" to API methods with pre-canned responses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class FakeClient(object):
|
||||||
|
def assert_called(self, method, url, body=None, pos=-1):
|
||||||
|
# Assert than an API method was just called.
|
||||||
|
expected = (method, url)
|
||||||
|
called = self.client.callstack[pos][0:2]
|
||||||
|
|
||||||
|
assert self.client.callstack, \
|
||||||
|
"Expected %s %s but no calls were made." % expected
|
||||||
|
|
||||||
|
assert expected == called, 'Expected %s %s; got %s %s' % \
|
||||||
|
(expected + called)
|
||||||
|
|
||||||
|
if body is not None:
|
||||||
|
assert self.client.callstack[pos][2] == body
|
||||||
|
|
||||||
|
def assert_called_anytime(self, method, url, body=None):
|
||||||
|
# Assert than an API method was called anytime in the test.
|
||||||
|
expected = (method, url)
|
||||||
|
|
||||||
|
assert self.client.callstack, \
|
||||||
|
"Expected %s %s but no calls were made." % expected
|
||||||
|
|
||||||
|
found = False
|
||||||
|
for entry in self.client.callstack:
|
||||||
|
if expected == entry[0:2]:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
assert found, 'Expected %s %s; got %s' % \
|
||||||
|
(expected, self.client.callstack)
|
||||||
|
if body is not None:
|
||||||
|
try:
|
||||||
|
assert entry[2] == body
|
||||||
|
except AssertionError:
|
||||||
|
print(entry[2])
|
||||||
|
print("!=")
|
||||||
|
print(body)
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.client.callstack = []
|
||||||
|
|
||||||
|
def clear_callstack(self):
|
||||||
|
self.client.callstack = []
|
||||||
|
|
||||||
|
def authenticate(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FakeKeystoneClient(object):
|
||||||
|
def __init__(self, username='test_user', password='apassword',
|
||||||
|
user_id='1234', access='4567', secret='8901',
|
||||||
|
credential_id='abcdxyz', auth_token='abcd1234',
|
||||||
|
only_services=None):
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.user_id = user_id
|
||||||
|
self.access = access
|
||||||
|
self.secret = secret
|
||||||
|
self.credential_id = credential_id
|
||||||
|
self.only_services = only_services
|
||||||
|
|
||||||
|
class FakeCred(object):
|
||||||
|
id = self.credential_id
|
||||||
|
access = self.access
|
||||||
|
secret = self.secret
|
||||||
|
self.creds = FakeCred()
|
||||||
|
|
||||||
|
self.auth_token = auth_token
|
82
bilean/tests/common/utils.py
Normal file
82
bilean/tests/common/utils.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_db import options
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
from bilean.common import context
|
||||||
|
from bilean.db import api as db_api
|
||||||
|
|
||||||
|
get_engine = db_api.get_engine
|
||||||
|
|
||||||
|
|
||||||
|
class UUIDStub(object):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.uuid4 = uuid.uuid4
|
||||||
|
uuid_stub = lambda: self.value
|
||||||
|
uuid.uuid4 = uuid_stub
|
||||||
|
|
||||||
|
def __exit__(self, *exc_info):
|
||||||
|
uuid.uuid4 = self.uuid4
|
||||||
|
|
||||||
|
|
||||||
|
def random_name():
|
||||||
|
return ''.join(random.choice(string.ascii_uppercase)
|
||||||
|
for x in range(10))
|
||||||
|
|
||||||
|
|
||||||
|
def setup_dummy_db():
|
||||||
|
options.cfg.set_defaults(options.database_opts, sqlite_synchronous=False)
|
||||||
|
options.set_defaults(cfg.CONF,
|
||||||
|
connection="sqlite://",
|
||||||
|
sqlite_db='bilean.db')
|
||||||
|
engine = get_engine()
|
||||||
|
db_api.db_sync(engine)
|
||||||
|
engine.connect()
|
||||||
|
|
||||||
|
|
||||||
|
def reset_dummy_db():
|
||||||
|
engine = get_engine()
|
||||||
|
meta = sqlalchemy.MetaData()
|
||||||
|
meta.reflect(bind=engine)
|
||||||
|
|
||||||
|
for table in reversed(meta.sorted_tables):
|
||||||
|
if table.name == 'migrate_version':
|
||||||
|
continue
|
||||||
|
engine.execute(table.delete())
|
||||||
|
|
||||||
|
|
||||||
|
def dummy_context(user='test_username', project='test_project_id',
|
||||||
|
password='password', roles=None, user_id='test_user_id',
|
||||||
|
trust_id=None, region_name=None, domain=None):
|
||||||
|
roles = roles or []
|
||||||
|
return context.RequestContext.from_dict({
|
||||||
|
'project': project,
|
||||||
|
'user': user_id,
|
||||||
|
'user_name': user,
|
||||||
|
'password': password,
|
||||||
|
'roles': roles,
|
||||||
|
'is_admin': False,
|
||||||
|
'auth_url': 'http://server.test:5000/v2.0',
|
||||||
|
'auth_token': 'abcd1234',
|
||||||
|
'trust_id': trust_id,
|
||||||
|
'region_name': region_name,
|
||||||
|
'domain': domain
|
||||||
|
})
|
0
bilean/tests/rules/__init__.py
Normal file
0
bilean/tests/rules/__init__.py
Normal file
108
bilean/tests/rules/test_rule_base.py
Normal file
108
bilean/tests/rules/test_rule_base.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslo_utils import encodeutils
|
||||||
|
import six
|
||||||
|
|
||||||
|
from bilean.common import exception
|
||||||
|
from bilean.common.i18n import _
|
||||||
|
from bilean.common import schema
|
||||||
|
from bilean.db import api as db_api
|
||||||
|
from bilean.engine import environment
|
||||||
|
from bilean.rules import base as rule_base
|
||||||
|
from bilean.tests.common import base
|
||||||
|
from bilean.tests.common import utils
|
||||||
|
|
||||||
|
|
||||||
|
class DummyRule(rule_base.Rule):
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
properties_schema = {
|
||||||
|
'key1': schema.String(
|
||||||
|
'First key',
|
||||||
|
default='value1'
|
||||||
|
),
|
||||||
|
'key2': schema.Integer(
|
||||||
|
'Second key',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, name, spec, **kwargs):
|
||||||
|
super(DummyRule, self).__init__(name, spec, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRuleBase(base.BileanTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestRuleBase, self).setUp()
|
||||||
|
|
||||||
|
self.context = utils.dummy_context()
|
||||||
|
environment.global_env().register_rule('bilean.rule.dummy', DummyRule)
|
||||||
|
self.spec = {
|
||||||
|
'type': 'bilean.rule.dummy',
|
||||||
|
'version': '1.0',
|
||||||
|
'properties': {
|
||||||
|
'key1': 'value1',
|
||||||
|
'key2': 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _create_rule(self, rule_name, rule_id=None):
|
||||||
|
rule = rule_base.Rule(rule_name, self.spec)
|
||||||
|
if rule_id:
|
||||||
|
rule.id = rule_id
|
||||||
|
|
||||||
|
return rule
|
||||||
|
|
||||||
|
def _create_db_rule(self, **kwargs):
|
||||||
|
values = {
|
||||||
|
'name': 'test-rule',
|
||||||
|
'type': 'bilean.rule.dummy-1.0',
|
||||||
|
'spec': self.spec,
|
||||||
|
'metadata': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
values.update(kwargs)
|
||||||
|
return db_api.rule_create(self.context, values)
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
name = utils.random_name()
|
||||||
|
rule = self._create_rule(name)
|
||||||
|
|
||||||
|
self.assertIsNone(rule.id)
|
||||||
|
self.assertEqual(name, rule.name)
|
||||||
|
self.assertEqual('bilean.rule.dummy-1.0', rule.type)
|
||||||
|
self.assertEqual(self.spec, rule.spec)
|
||||||
|
self.assertEqual({}, rule.metadata)
|
||||||
|
self.assertIsNone(rule.created_at)
|
||||||
|
self.assertIsNone(rule.updated_at)
|
||||||
|
self.assertIsNone(rule.deleted_at)
|
||||||
|
|
||||||
|
spec_data = rule.spec_data
|
||||||
|
self.assertEqual('bilean.rule.dummy', spec_data['type'])
|
||||||
|
self.assertEqual('1.0', spec_data['version'])
|
||||||
|
self.assertEqual({'key1': 'value1', 'key2': 2},
|
||||||
|
spec_data['properties'])
|
||||||
|
self.assertEqual({'key1': 'value1', 'key2': 2}, rule.properties)
|
||||||
|
|
||||||
|
def test_rule_type_not_found(self):
|
||||||
|
bad_spec = {
|
||||||
|
'type': 'bad-type',
|
||||||
|
'version': '1.0',
|
||||||
|
'properties': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertRaises(exception.RuleTypeNotFound,
|
||||||
|
rule_base.Rule,
|
||||||
|
'test-rule', bad_spec)
|
@ -1,4 +1,4 @@
|
|||||||
To generate the sample senlin.conf file, run the following
|
To generate the sample bilean.conf file, run the following
|
||||||
command from the top level of the senlin directory:
|
command from the top level of the bilean directory:
|
||||||
|
|
||||||
tox -egenconfig
|
tox -egenconfig
|
||||||
|
@ -2,16 +2,11 @@
|
|||||||
# of appearance. Changing the order has an impact on the overall integration
|
# of appearance. Changing the order has an impact on the overall integration
|
||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
pbr>=1.6
|
|
||||||
SQLAlchemy<1.1.0,>=0.9.9
|
|
||||||
eventlet>=0.17.4
|
|
||||||
keystonemiddleware>=4.0.0
|
|
||||||
jsonpath-rw-ext>=0.1.9
|
|
||||||
WebOb>=1.2.3
|
|
||||||
apscheduler>=3.0.1
|
apscheduler>=3.0.1
|
||||||
sqlalchemy-migrate>=0.9.6
|
cryptography>=1.0 # Apache-2.0
|
||||||
stevedore>=1.5.0
|
eventlet>=0.17.4
|
||||||
six>=1.9.0
|
jsonpath-rw-ext>=0.1.9
|
||||||
|
keystonemiddleware>=4.0.0
|
||||||
oslo.config>=2.7.0 # Apache-2.0
|
oslo.config>=2.7.0 # Apache-2.0
|
||||||
oslo.context>=0.2.0 # Apache-2.0
|
oslo.context>=0.2.0 # Apache-2.0
|
||||||
oslo.db>=4.1.0 # Apache-2.0
|
oslo.db>=4.1.0 # Apache-2.0
|
||||||
@ -23,3 +18,9 @@ oslo.policy>=0.5.0 # Apache-2.0
|
|||||||
oslo.serialization>=1.10.0 # Apache-2.0
|
oslo.serialization>=1.10.0 # Apache-2.0
|
||||||
oslo.service>=1.0.0 # Apache-2.0
|
oslo.service>=1.0.0 # Apache-2.0
|
||||||
oslo.utils!=3.1.0,>=2.8.0 # Apache-2.0
|
oslo.utils!=3.1.0,>=2.8.0 # Apache-2.0
|
||||||
|
pbr>=1.6
|
||||||
|
six>=1.9.0
|
||||||
|
SQLAlchemy<1.1.0,>=0.9.9
|
||||||
|
sqlalchemy-migrate>=0.9.6
|
||||||
|
stevedore>=1.5.0
|
||||||
|
WebOb>=1.2.3
|
||||||
|
Loading…
Reference in New Issue
Block a user