1
from unittest import TestCase
9
def load_config_defaults():
10
with open("config.yaml") as conf:
11
config_schema = yaml.safe_load(conf)
13
for key, value in config_schema['options'].items():
14
defaults[key] = value['default']
17
DEFAULTS = load_config_defaults()
20
class HookTestCase(TestCase):
23
SERVICE_NAME = 'some_juju_service'
24
WORKING_DIR = '/some_path'
29
def apply_patch(self, name, value=_object, return_value=_object):
30
if value is not self._object:
31
patcher = patch(name, value)
35
mock_obj = patcher.start()
36
self.addCleanup(patcher.stop)
38
if value is self._object and return_value is not self._object:
39
mock_obj.return_value = return_value
41
self.mocks[name] = mock_obj
45
super(HookTestCase, self).setUp()
46
# There's quite a bit of mocking here, due to the large amounts of
47
# environment context to juju hooks
49
self.config = DEFAULTS.copy()
50
self.relation_data = {'working_dir': self.WORKING_DIR}
52
# intercept all charmsupport usage
53
self.hookenv = self.apply_patch('hooks.hookenv')
54
self.fetch = self.apply_patch('hooks.fetch')
55
self.host = self.apply_patch('hooks.host')
57
self.hookenv.config.return_value = self.config
58
self.hookenv.relations_of_type.return_value = [self.relation_data]
60
# mocking utilities that touch the host/environment
61
self.process_template = self.apply_patch('hooks.process_template')
63
'hooks.sanitized_service_name', return_value=self.SERVICE_NAME)
64
self.apply_patch('hooks.cpu_count', return_value=1)
66
def assert_wsgi_config_applied(self, expected):
67
tmpl, config, path = self.process_template.call_args[0]
68
self.assertEqual(tmpl, 'upstart.tmpl')
69
self.assertEqual(path, '/etc/init/%s.conf' % self.SERVICE_NAME)
70
self.assertEqual(config, expected)
71
self.host.service_restart.assert_called_once_with(self.SERVICE_NAME)
73
def get_default_context(self):
74
expected = DEFAULTS.copy()
75
expected['unit_name'] = self.SERVICE_NAME
76
expected['working_dir'] = self.WORKING_DIR
77
expected['project_name'] = self.SERVICE_NAME
78
expected['wsgi_workers'] = 2
79
expected['env_extra'] = []
80
fmt = expected['wsgi_access_logformat'].replace('"', '\\"')
81
expected['wsgi_access_logformat'] = fmt
84
def test_python_install_hook(self):
86
self.assertTrue(self.fetch.apt_update.called)
87
self.fetch.apt_install.assert_called_once_with(
88
['gunicorn', 'python-jinja2'])
90
def test_default_configure_gunicorn(self):
91
hooks.configure_gunicorn()
92
expected = self.get_default_context()
93
self.assert_wsgi_config_applied(expected)
95
def test_configure_gunicorn_no_working_dir(self):
96
del self.relation_data['working_dir']
97
hooks.configure_gunicorn()
98
self.assertFalse(self.process_template.called)
99
self.assertFalse(self.host.service_restart.called)
101
def test_configure_gunicorn_relation_data(self):
102
self.relation_data['port'] = 9999
103
self.relation_data['wsgi_workers'] = 1
104
self.relation_data['unknown'] = 'value'
106
hooks.configure_gunicorn()
108
self.assertFalse(self.fetch.apt_install.called)
110
expected = self.get_default_context()
111
expected['wsgi_workers'] = 1
112
expected['port'] = 9999
114
self.assert_wsgi_config_applied(expected)
116
def test_env_extra_parsing(self):
117
self.relation_data['env_extra'] = 'A=1 B="2" C="3 4" D= E'
119
hooks.configure_gunicorn()
121
expected = self.get_default_context()
122
expected['env_extra'] = [
130
self.assert_wsgi_config_applied(expected)
132
def do_worker_class(self, worker_class):
133
self.relation_data['wsgi_worker_class'] = worker_class
134
hooks.configure_gunicorn()
135
self.fetch.apt_install.assert_called_once_with(
136
'python-%s' % worker_class)
137
expected = self.get_default_context()
138
expected['wsgi_worker_class'] = worker_class
139
self.assert_wsgi_config_applied(expected)
141
def test_configure_worker_class_eventlet(self):
142
self.do_worker_class('eventlet')
144
def test_configure_worker_class_tornado(self):
145
self.do_worker_class('tornado')
147
def test_configure_worker_class_gevent(self):
148
self.do_worker_class('gevent')
152
@patch('hooks.os.remove')
153
def test_wsgi_file_relation_broken(self, remove):
154
hooks.wsgi_file_relation_broken()
155
self.host.service_stop.assert_called_once_with(self.SERVICE_NAME)
156
remove.assert_called_once_with(
157
'/etc/init/%s.conf' % self.SERVICE_NAME)