Make testing with different configuration values easier and less error-prone by using mock.patch to change the configuration.

This commit is contained in:
Jan Kaluza
2017-03-09 14:33:28 +01:00
parent d87010dba0
commit 7cf77d0fee
4 changed files with 68 additions and 63 deletions

View File

@@ -324,15 +324,26 @@ class Config(object):
self.set_item(key.lower(), getattr(conf_section_obj, key)) self.set_item(key.lower(), getattr(conf_section_obj, key))
def set_item(self, key, value): def set_item(self, key, value):
"""Set value for configuration item as self.key = value""" """
Set value for configuration item. Creates the self._key = value
attribute and self.key property to set/get/del the attribute.
"""
if key == 'set_item' or key.startswith('_'): if key == 'set_item' or key.startswith('_'):
raise Exception("Configuration item's name is not allowed: %s" % key) raise Exception("Configuration item's name is not allowed: %s" % key)
# customized check & set if there's a corresponding handler # Create the empty self._key attribute, so we can assign to it.
setattr(self, "_" + key, None)
# Create self.key property to access the self._key attribute.
# Use the setifok_func if available for the attribute.
setifok_func = '_setifok_{}'.format(key) setifok_func = '_setifok_{}'.format(key)
if hasattr(self, setifok_func): if hasattr(self, setifok_func):
getattr(self, setifok_func)(value) setx = lambda self, val: getattr(self, setifok_func)(val)
return else:
setx = lambda self, val: setattr(self, "_" + key, val)
getx = lambda self: getattr(self, "_" + key)
delx = lambda self: delattr(self, "_" + key)
setattr(Config, key, property(getx, setx, delx))
# managed/registered configuration items # managed/registered configuration items
if key in self._defaults: if key in self._defaults:
@@ -340,21 +351,17 @@ class Config(object):
convert = self._defaults[key]['type'] convert = self._defaults[key]['type']
if convert in [bool, int, list, str, set]: if convert in [bool, int, list, str, set]:
try: try:
setattr(self, key, convert(value)) # Do no try to convert None...
if value is not None:
value = convert(value)
except: except:
raise TypeError("Configuration value conversion failed for name: %s" % key) raise TypeError("Configuration value conversion failed for name: %s" % key)
# if type is None, do not perform any conversion
elif convert is None:
setattr(self, key, value)
# unknown type/unsupported conversion # unknown type/unsupported conversion
else: elif convert is not None:
raise TypeError("Unsupported type %s for configuration item name: %s" % (convert, key)) raise TypeError("Unsupported type %s for configuration item name: %s" % (convert, key))
# passthrough for unmanaged configuration items # Set the attribute to the correct value
else: setattr(self, key, value)
setattr(self, key, value)
return
# #
# Register your _setifok_* handlers here # Register your _setifok_* handlers here
@@ -364,62 +371,62 @@ class Config(object):
s = str(s) s = str(s)
if s not in ("koji", "copr", "mock"): if s not in ("koji", "copr", "mock"):
raise ValueError("Unsupported buildsystem: %s." % s) raise ValueError("Unsupported buildsystem: %s." % s)
self.system = s self._system = s
def _setifok_polling_interval(self, i): def _setifok_polling_interval(self, i):
if not isinstance(i, int): if not isinstance(i, int):
raise TypeError("polling_interval needs to be an int") raise TypeError("polling_interval needs to be an int")
if i < 0: if i < 0:
raise ValueError("polling_interval must be >= 0") raise ValueError("polling_interval must be >= 0")
self.polling_interval = i self._polling_interval = i
def _setifok_rpms_default_repository(self, s): def _setifok_rpms_default_repository(self, s):
rpm_repo = str(s) rpm_repo = str(s)
if rpm_repo[-1] != '/': if rpm_repo[-1] != '/':
rpm_repo = rpm_repo + '/' rpm_repo = rpm_repo + '/'
self.rpms_default_repository = rpm_repo self._rpms_default_repository = rpm_repo
def _setifok_rpms_default_cache(self, s): def _setifok_rpms_default_cache(self, s):
rpm_cache = str(s) rpm_cache = str(s)
if rpm_cache[-1] != '/': if rpm_cache[-1] != '/':
rpm_cache = rpm_cache + '/' rpm_cache = rpm_cache + '/'
self.rpms_default_cache = rpm_cache self._rpms_default_cache = rpm_cache
def _setifok_log_backend(self, s): def _setifok_log_backend(self, s):
if s is None: if s is None:
self.log_backend = "console" self._log_backend = "console"
elif s not in logger.supported_log_backends(): elif s not in logger.supported_log_backends():
raise ValueError("Unsupported log backend") raise ValueError("Unsupported log backend")
self.log_backend = str(s) self._log_backend = str(s)
def _setifok_log_file(self, s): def _setifok_log_file(self, s):
if s is None: if s is None:
self.log_file = "" self._log_file = ""
else: else:
self.log_file = str(s) self._log_file = str(s)
def _setifok_log_level(self, s): def _setifok_log_level(self, s):
level = str(s).lower() level = str(s).lower()
self.log_level = logger.str_to_log_level(level) self._log_level = logger.str_to_log_level(level)
def _setifok_messaging(self, s): def _setifok_messaging(self, s):
s = str(s) s = str(s)
if s not in ("fedmsg", "amq", "in_memory"): if s not in ("fedmsg", "amq", "in_memory"):
raise ValueError("Unsupported messaging system.") raise ValueError("Unsupported messaging system.")
self.messaging = s self._messaging = s
def _setifok_amq_recv_addresses(self, l): def _setifok_amq_recv_addresses(self, l):
assert isinstance(l, list) or isinstance(l, tuple) assert isinstance(l, list) or isinstance(l, tuple)
self.amq_recv_addresses = list(l) self._amq_recv_addresses = list(l)
def _setifok_scmurls(self, l): def _setifok_scmurls(self, l):
if not isinstance(l, list): if not isinstance(l, list):
raise TypeError("scmurls needs to be a list.") raise TypeError("scmurls needs to be a list.")
self.scmurls = [str(x) for x in l] self._scmurls = [str(x) for x in l]
def _setifok_num_consecutive_builds(self, i): def _setifok_num_consecutive_builds(self, i):
if not isinstance(i, int): if not isinstance(i, int):
raise TypeError('NUM_CONSECUTIVE_BUILDS needs to be an int') raise TypeError('NUM_CONSECUTIVE_BUILDS needs to be an int')
if i < 0: if i < 0:
raise ValueError('NUM_CONSECUTIVE_BUILDS must be >= 0') raise ValueError('NUM_CONSECUTIVE_BUILDS must be >= 0')
self.num_consecutive_builds = i self._num_consecutive_builds = i

View File

@@ -34,7 +34,7 @@ import module_build_service.scheduler.handlers.repos
import module_build_service.utils import module_build_service.utils
from module_build_service import db, models, conf from module_build_service import db, models, conf
from mock import patch from mock import patch, PropertyMock
from tests import app, init_data from tests import app, init_data
import os import os
@@ -203,15 +203,13 @@ class TestModuleBuilder(GenericBuilder):
pass pass
@patch("module_build_service.config.Config.system",
new_callable=PropertyMock, return_value = "mock")
class TestBuild(unittest.TestCase): class TestBuild(unittest.TestCase):
def setUp(self): def setUp(self):
GenericBuilder.register_backend_class(TestModuleBuilder) GenericBuilder.register_backend_class(TestModuleBuilder)
self.client = app.test_client() self.client = app.test_client()
self._prev_system = conf.system
self._prev_num_consecutive_builds = conf.num_consecutive_builds
conf.set_item("system", "mock")
init_data() init_data()
models.ModuleBuild.query.delete() models.ModuleBuild.query.delete()
@@ -222,8 +220,6 @@ class TestBuild(unittest.TestCase):
self.vcr.__enter__() self.vcr.__enter__()
def tearDown(self): def tearDown(self):
conf.set_item("system", self._prev_system)
conf.set_item("num_consecutive_builds", self._prev_num_consecutive_builds)
TestModuleBuilder.reset() TestModuleBuilder.reset()
# Necessary to restart the twisted reactor for the next test. # Necessary to restart the twisted reactor for the next test.
@@ -237,7 +233,7 @@ class TestBuild(unittest.TestCase):
@timed(30) @timed(30)
@patch('module_build_service.auth.get_user', return_value=user) @patch('module_build_service.auth.get_user', return_value=user)
@patch('module_build_service.scm.SCM') @patch('module_build_service.scm.SCM')
def test_submit_build(self, mocked_scm, mocked_get_user): def test_submit_build(self, mocked_scm, mocked_get_user, conf_system):
""" """
Tests the build of testmodule.yaml using TestModuleBuilder which Tests the build of testmodule.yaml using TestModuleBuilder which
succeeds everytime. succeeds everytime.
@@ -292,7 +288,7 @@ class TestBuild(unittest.TestCase):
@timed(30) @timed(30)
@patch('module_build_service.auth.get_user', return_value=user) @patch('module_build_service.auth.get_user', return_value=user)
@patch('module_build_service.scm.SCM') @patch('module_build_service.scm.SCM')
def test_submit_build_from_yaml(self, mocked_scm, mocked_get_user): def test_submit_build_from_yaml(self, mocked_scm, mocked_get_user, conf_system):
MockedSCM(mocked_scm, "testmodule", "testmodule.yaml") MockedSCM(mocked_scm, "testmodule", "testmodule.yaml")
testmodule = os.path.join(base_dir, 'staged_data', 'testmodule.yaml') testmodule = os.path.join(base_dir, 'staged_data', 'testmodule.yaml')
@@ -305,18 +301,21 @@ class TestBuild(unittest.TestCase):
data={'yaml': (testmodule, yaml)}) data={'yaml': (testmodule, yaml)})
return json.loads(rv.data) return json.loads(rv.data)
conf.set_item("yaml_submit_allowed", True) with patch("module_build_service.config.Config.yaml_submit_allowed",
data = submit() new_callable=PropertyMock, return_value = True):
self.assertEqual(data['id'], 1) conf.set_item("yaml_submit_allowed", True)
data = submit()
self.assertEqual(data['id'], 1)
conf.set_item("yaml_submit_allowed", False) with patch("module_build_service.config.Config.yaml_submit_allowed",
data = submit() new_callable=PropertyMock, return_value = False):
self.assertEqual(data['status'], 401) data = submit()
self.assertEqual(data['message'], 'YAML submission is not enabled') self.assertEqual(data['status'], 401)
self.assertEqual(data['message'], 'YAML submission is not enabled')
@timed(30) @timed(30)
@patch('module_build_service.auth.get_user', return_value=user) @patch('module_build_service.auth.get_user', return_value=user)
def test_submit_build_with_optional_params(self, mocked_get_user): def test_submit_build_with_optional_params(self, mocked_get_user, conf_system):
params = {'branch': 'master', 'scmurl': 'git://pkgs.stg.fedoraproject.org/modules/' params = {'branch': 'master', 'scmurl': 'git://pkgs.stg.fedoraproject.org/modules/'
'testmodule.git?#68932c90de214d9d13feefbd35246a81b6cb8d49'} 'testmodule.git?#68932c90de214d9d13feefbd35246a81b6cb8d49'}
@@ -335,7 +334,7 @@ class TestBuild(unittest.TestCase):
@timed(30) @timed(30)
@patch('module_build_service.auth.get_user', return_value=user) @patch('module_build_service.auth.get_user', return_value=user)
@patch('module_build_service.scm.SCM') @patch('module_build_service.scm.SCM')
def test_submit_build_cancel(self, mocked_scm, mocked_get_user): def test_submit_build_cancel(self, mocked_scm, mocked_get_user, conf_system):
""" """
Submit all builds for a module and cancel the module build later. Submit all builds for a module and cancel the module build later.
""" """
@@ -387,7 +386,7 @@ class TestBuild(unittest.TestCase):
@timed(30) @timed(30)
@patch('module_build_service.auth.get_user', return_value=user) @patch('module_build_service.auth.get_user', return_value=user)
@patch('module_build_service.scm.SCM') @patch('module_build_service.scm.SCM')
def test_submit_build_instant_complete(self, mocked_scm, mocked_get_user): def test_submit_build_instant_complete(self, mocked_scm, mocked_get_user, conf_system):
""" """
Tests the build of testmodule.yaml using TestModuleBuilder which Tests the build of testmodule.yaml using TestModuleBuilder which
succeeds everytime. succeeds everytime.
@@ -418,7 +417,11 @@ class TestBuild(unittest.TestCase):
@timed(30) @timed(30)
@patch('module_build_service.auth.get_user', return_value=user) @patch('module_build_service.auth.get_user', return_value=user)
@patch('module_build_service.scm.SCM') @patch('module_build_service.scm.SCM')
def test_submit_build_concurrent_threshold(self, mocked_scm, mocked_get_user): @patch("module_build_service.config.Config.num_consecutive_builds",
new_callable=PropertyMock, return_value = 1)
def test_submit_build_concurrent_threshold(self, conf_num_consecutive_builds,
mocked_scm, mocked_get_user,
conf_system):
""" """
Tests the build of testmodule.yaml using TestModuleBuilder with Tests the build of testmodule.yaml using TestModuleBuilder with
num_consecutive_builds set to 1. num_consecutive_builds set to 1.
@@ -426,8 +429,6 @@ class TestBuild(unittest.TestCase):
MockedSCM(mocked_scm, 'testmodule', 'testmodule.yaml', MockedSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
'620ec77321b2ea7b0d67d82992dda3e1d67055b4') '620ec77321b2ea7b0d67d82992dda3e1d67055b4')
conf.set_item("num_consecutive_builds", 1)
rv = self.client.post('/module-build-service/1/module-builds/', data=json.dumps( rv = self.client.post('/module-build-service/1/module-builds/', data=json.dumps(
{'branch': 'master', 'scmurl': 'git://pkgs.stg.fedoraproject.org/modules/' {'branch': 'master', 'scmurl': 'git://pkgs.stg.fedoraproject.org/modules/'
'testmodule.git?#68932c90de214d9d13feefbd35246a81b6cb8d49'})) 'testmodule.git?#68932c90de214d9d13feefbd35246a81b6cb8d49'}))

View File

@@ -23,6 +23,7 @@
import unittest import unittest
from module_build_service import messaging, conf from module_build_service import messaging, conf
from mock import patch, PropertyMock
class TestFedmsgMessaging(unittest.TestCase): class TestFedmsgMessaging(unittest.TestCase):
@@ -53,7 +54,9 @@ class TestFedmsgMessaging(unittest.TestCase):
self.assertEqual(msg.build_id, 614503) self.assertEqual(msg.build_id, 614503)
self.assertEqual(msg.build_new_state, 1) self.assertEqual(msg.build_new_state, 1)
def test_copr_build_end(self): @patch("module_build_service.config.Config.system",
new_callable=PropertyMock, return_value = "copr")
def test_copr_build_end(self, conf_system):
# http://fedora-fedmsg.readthedocs.io/en/latest/topics.html#copr-build-end # http://fedora-fedmsg.readthedocs.io/en/latest/topics.html#copr-build-end
copr_build_end_msg = { copr_build_end_msg = {
'msg': { 'msg': {
@@ -75,7 +78,6 @@ class TestFedmsgMessaging(unittest.TestCase):
'username': 'copr' 'username': 'copr'
} }
conf.set_item("system", "copr")
topic = 'org.fedoraproject.prod.copr.build.end' topic = 'org.fedoraproject.prod.copr.build.end'
msg = messaging.BaseMessage.from_fedmsg(topic, copr_build_end_msg) msg = messaging.BaseMessage.from_fedmsg(topic, copr_build_end_msg)
self.assertIsInstance(msg, messaging.KojiBuildChange) self.assertIsInstance(msg, messaging.KojiBuildChange)

View File

@@ -24,7 +24,7 @@ import unittest
import json import json
import time import time
import vcr import vcr
from mock import patch, Mock from mock import patch, Mock, PropertyMock
from shutil import copyfile from shutil import copyfile
from os import path, mkdir from os import path, mkdir
from os.path import dirname from os.path import dirname
@@ -475,19 +475,14 @@ class TestViews(unittest.TestCase):
@patch('module_build_service.auth.get_user', return_value=user) @patch('module_build_service.auth.get_user', return_value=user)
@patch('module_build_service.scm.SCM') @patch('module_build_service.scm.SCM')
def test_submit_build_includedmodule(self, mocked_scm, mocked_get_user): @patch("module_build_service.config.Config.modules_allow_repository",
_prev_modules_allow_repository = conf.modules_allow_repository new_callable=PropertyMock, return_value = True)
conf.set_item("modules_allow_repository", True) def test_submit_build_includedmodule(self, conf, mocked_scm, mocked_get_user):
mocked_scm_obj = MockedSCM(mocked_scm, "includedmodules", mocked_scm_obj = MockedSCM(mocked_scm, "includedmodules",
["includedmodules.yaml", "testmodule.yaml"]) ["includedmodules.yaml", "testmodule.yaml"])
try: rv = self.client.post('/module-build-service/1/module-builds/', data=json.dumps(
rv = self.client.post('/module-build-service/1/module-builds/', data=json.dumps( {'branch': 'master', 'scmurl': 'git://pkgs.stg.fedoraproject.org/modules/'
{'branch': 'master', 'scmurl': 'git://pkgs.stg.fedoraproject.org/modules/' 'testmodule.git?#68931c90de214d9d13feefbd35246a81b6cb8d49'}))
'testmodule.git?#68931c90de214d9d13feefbd35246a81b6cb8d49'}))
except e:
raise
finally:
conf.set_item("modules_allow_repository", _prev_modules_allow_repository)
data = json.loads(rv.data) data = json.loads(rv.data)
assert 'component_builds' in data, data assert 'component_builds' in data, data