~thedac/charms/precise/gunicorn/fix-config-changed

« back to all changes in this revision

Viewing changes to hooks/tests/test_hooks.py

  • Committer: Marco Ceppi
  • Date: 2014-03-04 16:20:11 UTC
  • mfrom: (28.1.7 gunicorn)
  • Revision ID: marco@ceppi.net-20140304162011-kpulghowq5j4kpyj
[bloodearnest] Clean up of gunicorn charm, adding charmhelpers, tests and re-add env_extra param

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from unittest import TestCase
 
2
from mock import patch
 
3
 
 
4
import yaml
 
5
 
 
6
import hooks
 
7
 
 
8
 
 
9
def load_config_defaults():
 
10
    with open("config.yaml") as conf:
 
11
        config_schema = yaml.safe_load(conf)
 
12
    defaults = {}
 
13
    for key, value in config_schema['options'].items():
 
14
        defaults[key] = value['default']
 
15
    return defaults
 
16
 
 
17
DEFAULTS = load_config_defaults()
 
18
 
 
19
 
 
20
class HookTestCase(TestCase):
 
21
    maxDiff = None
 
22
 
 
23
    SERVICE_NAME = 'some_juju_service'
 
24
    WORKING_DIR = '/some_path'
 
25
 
 
26
    _object = object()
 
27
    mocks = {}
 
28
 
 
29
    def apply_patch(self, name, value=_object, return_value=_object):
 
30
        if value is not self._object:
 
31
            patcher = patch(name, value)
 
32
        else:
 
33
            patcher = patch(name)
 
34
 
 
35
        mock_obj = patcher.start()
 
36
        self.addCleanup(patcher.stop)
 
37
 
 
38
        if value is self._object and return_value is not self._object:
 
39
            mock_obj.return_value = return_value
 
40
 
 
41
        self.mocks[name] = mock_obj
 
42
        return mock_obj
 
43
 
 
44
    def setUp(self):
 
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
 
48
 
 
49
        self.config = DEFAULTS.copy()
 
50
        self.relation_data = {'working_dir': self.WORKING_DIR}
 
51
 
 
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')
 
56
 
 
57
        self.hookenv.config.return_value = self.config
 
58
        self.hookenv.relations_of_type.return_value = [self.relation_data]
 
59
 
 
60
        # mocking utilities that touch the host/environment
 
61
        self.process_template = self.apply_patch('hooks.process_template')
 
62
        self.apply_patch(
 
63
            'hooks.sanitized_service_name', return_value=self.SERVICE_NAME)
 
64
        self.apply_patch('hooks.cpu_count', return_value=1)
 
65
 
 
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)
 
72
 
 
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
 
82
        return expected
 
83
 
 
84
    def test_python_install_hook(self):
 
85
        hooks.install()
 
86
        self.assertTrue(self.fetch.apt_update.called)
 
87
        self.fetch.apt_install.assert_called_once_with(
 
88
            ['gunicorn', 'python-jinja2'])
 
89
 
 
90
    def test_default_configure_gunicorn(self):
 
91
        hooks.configure_gunicorn()
 
92
        expected = self.get_default_context()
 
93
        self.assert_wsgi_config_applied(expected)
 
94
 
 
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)
 
100
 
 
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'
 
105
 
 
106
        hooks.configure_gunicorn()
 
107
 
 
108
        self.assertFalse(self.fetch.apt_install.called)
 
109
 
 
110
        expected = self.get_default_context()
 
111
        expected['wsgi_workers'] = 1
 
112
        expected['port'] = 9999
 
113
 
 
114
        self.assert_wsgi_config_applied(expected)
 
115
 
 
116
    def test_env_extra_parsing(self):
 
117
        self.relation_data['env_extra'] = 'A=1 B="2" C="3 4" D= E'
 
118
 
 
119
        hooks.configure_gunicorn()
 
120
 
 
121
        expected = self.get_default_context()
 
122
        expected['env_extra'] = [
 
123
            ['A', '1'],
 
124
            ['B', '2'],
 
125
            ['C', '3 4'],
 
126
            ['D', ''],
 
127
            # no E
 
128
        ]
 
129
 
 
130
        self.assert_wsgi_config_applied(expected)
 
131
 
 
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)
 
140
 
 
141
    def test_configure_worker_class_eventlet(self):
 
142
        self.do_worker_class('eventlet')
 
143
 
 
144
    def test_configure_worker_class_tornado(self):
 
145
        self.do_worker_class('tornado')
 
146
 
 
147
    def test_configure_worker_class_gevent(self):
 
148
        self.do_worker_class('gevent')
 
149
 
 
150
 
 
151
 
 
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)