Add tests architecture
This commit is contained in:
parent
5ff6ab5518
commit
1056b8b6d7
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
||||
.DS_Store
|
||||
.coverage
|
||||
.tox
|
||||
.testrepository
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
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):
|
||||
"""Initialise the oslo_messaging layer."""
|
||||
global TRANSPORT, TRANSPORTS, NOTIFIER
|
||||
global TRANSPORT, NOTIFIER
|
||||
|
||||
if url and url.startswith("fake://"):
|
||||
# NOTE(sileht): oslo_messaging fake driver uses time.sleep
|
||||
@ -78,7 +78,6 @@ def setup(url=None, optional=False):
|
||||
if not NOTIFIER and TRANSPORT:
|
||||
serializer = RequestContextSerializer(JsonPayloadSerializer())
|
||||
NOTIFIER = oslo_messaging.Notifier(TRANSPORT, serializer=serializer)
|
||||
TRANSPORTS[url] = TRANSPORT
|
||||
|
||||
|
||||
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
|
||||
command from the top level of the senlin directory:
|
||||
To generate the sample bilean.conf file, run the following
|
||||
command from the top level of the bilean directory:
|
||||
|
||||
tox -egenconfig
|
||||
|
@ -2,16 +2,11 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# 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
|
||||
sqlalchemy-migrate>=0.9.6
|
||||
stevedore>=1.5.0
|
||||
six>=1.9.0
|
||||
cryptography>=1.0 # Apache-2.0
|
||||
eventlet>=0.17.4
|
||||
jsonpath-rw-ext>=0.1.9
|
||||
keystonemiddleware>=4.0.0
|
||||
oslo.config>=2.7.0 # Apache-2.0
|
||||
oslo.context>=0.2.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.service>=1.0.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