Merge lp:~adam-collard/charms/precise/reviewboard/trunk into lp:charms/reviewboard
- Precise Pangolin (12.04)
- trunk
- Merge into trunk
Proposed by
Adam Collard
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 31 | ||||
Proposed branch: | lp:~adam-collard/charms/precise/reviewboard/trunk | ||||
Merge into: | lp:charms/reviewboard | ||||
Diff against target: |
655 lines (+551/-18) 4 files modified
Makefile (+12/-0) README.md (+1/-2) hooks/hooks.py (+24/-16) hooks/tests/test_hooks.py (+514/-0) |
||||
To merge this branch: | bzr merge lp:~adam-collard/charms/precise/reviewboard/trunk | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Charles Butler (community) | Approve | ||
Review via email: mp+224041@code.launchpad.net |
Commit message
Description of the change
This branch adds unit test coverage to all of the implemented hooks and fixes a couple of bugs:
1. Lack of apt update before apt install meant (with out of date image files) units failed to install python-setuptools and therefore easy_install failed.
2. Ordering of relating and config setting (relating first and then setting config) meant a theoretical bug with failing to setup apache site.
To post a comment you must log in.
- 31. By Adam Collard
-
Add test dependencies and setup target
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'Makefile' | |||
2 | --- Makefile 1970-01-01 00:00:00 +0000 | |||
3 | +++ Makefile 2014-06-29 13:52:25 +0000 | |||
4 | @@ -0,0 +1,12 @@ | |||
5 | 1 | .PHONY: check test lint setup | ||
6 | 2 | |||
7 | 3 | test: setup | ||
8 | 4 | cd hooks; trial tests | ||
9 | 5 | |||
10 | 6 | lint: | ||
11 | 7 | flake8 --exclude=charmhelpers hooks/ | ||
12 | 8 | |||
13 | 9 | check: test lint | ||
14 | 10 | |||
15 | 11 | setup: | ||
16 | 12 | @sudo apt-get install python-twisted-core python-django python-mock | ||
17 | 0 | 13 | ||
18 | === modified file 'README.md' | |||
19 | --- README.md 2014-06-10 18:53:50 +0000 | |||
20 | +++ README.md 2014-06-29 13:52:25 +0000 | |||
21 | @@ -35,11 +35,10 @@ | |||
22 | 35 | 35 | ||
23 | 36 | ## Known Limitations and Issues | 36 | ## Known Limitations and Issues |
24 | 37 | 37 | ||
25 | 38 | * Add test coverage | ||
26 | 39 | * Add configuration for setting up initial repositories? | ||
27 | 40 | * Add support for MySQL as a database backend. | 38 | * Add support for MySQL as a database backend. |
28 | 41 | * Switch package installation between `python-psycopg2` and `python-mysqldb`. | 39 | * Switch package installation between `python-psycopg2` and `python-mysqldb`. |
29 | 42 | * Change configuration of rb-site command. | 40 | * Change configuration of rb-site command. |
30 | 41 | * Add configuration for setting up initial repositories? | ||
31 | 43 | * Optionally allow relation to separate | 42 | * Optionally allow relation to separate |
32 | 44 | [memcached](https://jujucharms.com/sidebar/search/precise/memcached/) | 43 | [memcached](https://jujucharms.com/sidebar/search/precise/memcached/) |
33 | 45 | charm instead of using local memcached. | 44 | charm instead of using local memcached. |
34 | 46 | 45 | ||
35 | === modified file 'hooks/hooks.py' | |||
36 | --- hooks/hooks.py 2014-06-11 18:52:18 +0000 | |||
37 | +++ hooks/hooks.py 2014-06-29 13:52:25 +0000 | |||
38 | @@ -7,7 +7,7 @@ | |||
39 | 7 | 7 | ||
40 | 8 | import charmhelpers.core.hookenv as hookenv | 8 | import charmhelpers.core.hookenv as hookenv |
41 | 9 | from charmhelpers.core.host import service_restart, service_stop | 9 | from charmhelpers.core.host import service_restart, service_stop |
43 | 10 | from charmhelpers.fetch import apt_install | 10 | from charmhelpers.fetch import apt_install, apt_update |
44 | 11 | 11 | ||
45 | 12 | 12 | ||
46 | 13 | hooks = hookenv.Hooks() | 13 | hooks = hookenv.Hooks() |
47 | @@ -42,7 +42,8 @@ | |||
48 | 42 | 42 | ||
49 | 43 | 43 | ||
50 | 44 | @contextmanager | 44 | @contextmanager |
52 | 45 | def django_environ(): | 45 | def _django_environ(): |
53 | 46 | """Context manager for setting up DJANGO_SETTINGS_MODULE.""" | ||
54 | 46 | cwd = os.getcwd() | 47 | cwd = os.getcwd() |
55 | 47 | config_dir = "/var/www/reviewboard/conf" | 48 | config_dir = "/var/www/reviewboard/conf" |
56 | 48 | os.chdir(config_dir) | 49 | os.chdir(config_dir) |
57 | @@ -119,8 +120,9 @@ | |||
58 | 119 | @hooks.hook('db-relation-changed') | 120 | @hooks.hook('db-relation-changed') |
59 | 120 | def db_relation_changed(): | 121 | def db_relation_changed(): |
60 | 121 | if not is_rb_site_installed(): | 122 | if not is_rb_site_installed(): |
63 | 122 | if install_rb_site(): | 123 | install_rb_site() |
64 | 123 | configure_apache2() | 124 | if is_rb_site_installed(): |
65 | 125 | configure_apache2() | ||
66 | 124 | 126 | ||
67 | 125 | 127 | ||
68 | 126 | @hooks.hook("website-relation-joined") | 128 | @hooks.hook("website-relation-joined") |
69 | @@ -134,23 +136,24 @@ | |||
70 | 134 | def config_changed(): | 136 | def config_changed(): |
71 | 135 | config = hookenv.config() | 137 | config = hookenv.config() |
72 | 136 | if not is_rb_site_installed(): | 138 | if not is_rb_site_installed(): |
80 | 137 | if install_rb_site(): | 139 | install_rb_site() |
81 | 138 | configure_apache2() | 140 | if is_rb_site_installed(): |
82 | 139 | else: | 141 | configure_apache2() |
83 | 140 | # Failed to install rb site for some reason, try again later. | 142 | else: |
84 | 141 | return | 143 | # Failed to install rb site for some reason, try again later. |
85 | 142 | if config["admin-password"]: | 144 | return |
86 | 143 | with django_environ(): | 145 | if config.get("admin-password"): |
87 | 146 | with _django_environ(): | ||
88 | 144 | from django.contrib.auth.management.commands import changepassword | 147 | from django.contrib.auth.management.commands import changepassword |
89 | 145 | command = changepassword.Command() | 148 | command = changepassword.Command() |
90 | 146 | command._get_pass = lambda *args: config["admin-password"] | 149 | command._get_pass = lambda *args: config["admin-password"] |
91 | 147 | command.execute("admin") | 150 | command.execute("admin") |
93 | 148 | if config["host"]: | 151 | if config.get("host"): |
94 | 149 | if config["host"] == "localhost": | 152 | if config["host"] == "localhost": |
95 | 150 | host = hookenv.unit_get("public-address") | 153 | host = hookenv.unit_get("public-address") |
96 | 151 | else: | 154 | else: |
97 | 152 | host = config["host"] | 155 | host = config["host"] |
99 | 153 | with django_environ(): | 156 | with _django_environ(): |
100 | 154 | from django.contrib.sites.models import Site | 157 | from django.contrib.sites.models import Site |
101 | 155 | cur_site = Site.objects.get_current() | 158 | cur_site = Site.objects.get_current() |
102 | 156 | cur_site.domain = host | 159 | cur_site.domain = host |
103 | @@ -159,6 +162,7 @@ | |||
104 | 159 | 162 | ||
105 | 160 | @hooks.hook("install") | 163 | @hooks.hook("install") |
106 | 161 | def install(): | 164 | def install(): |
107 | 165 | apt_update(fatal=True) | ||
108 | 162 | apt_install(["apache2-mpm-prefork", | 166 | apt_install(["apache2-mpm-prefork", |
109 | 163 | "libapache2-mod-wsgi", | 167 | "libapache2-mod-wsgi", |
110 | 164 | "python-setuptools", | 168 | "python-setuptools", |
111 | @@ -166,7 +170,7 @@ | |||
112 | 166 | "memcached", | 170 | "memcached", |
113 | 167 | "python-memcache", | 171 | "python-memcache", |
114 | 168 | "patch", | 172 | "patch", |
116 | 169 | "python-psycopg2"]) | 173 | "python-psycopg2"], fatal=True) |
117 | 170 | hookenv.log("Installing ReviewBoard") | 174 | hookenv.log("Installing ReviewBoard") |
118 | 171 | subprocess.check_call(["easy_install", "ReviewBoard"]) | 175 | subprocess.check_call(["easy_install", "ReviewBoard"]) |
119 | 172 | 176 | ||
120 | @@ -183,8 +187,12 @@ | |||
121 | 183 | service_stop("apache2") | 187 | service_stop("apache2") |
122 | 184 | 188 | ||
123 | 185 | 189 | ||
125 | 186 | if __name__ == '__main__': | 190 | def main(argv): |
126 | 187 | try: | 191 | try: |
128 | 188 | hooks.execute(sys.argv) | 192 | hooks.execute(argv) |
129 | 189 | except hookenv.UnregisteredHookError as e: | 193 | except hookenv.UnregisteredHookError as e: |
130 | 190 | hookenv.log('Unknown hook {} - skipping.'.format(e)) | 194 | hookenv.log('Unknown hook {} - skipping.'.format(e)) |
131 | 195 | |||
132 | 196 | |||
133 | 197 | if __name__ == '__main__': | ||
134 | 198 | main(sys.argv) | ||
135 | 191 | 199 | ||
136 | === added directory 'hooks/tests' | |||
137 | === added file 'hooks/tests/__init__.py' | |||
138 | === added file 'hooks/tests/test_hooks.py' | |||
139 | --- hooks/tests/test_hooks.py 1970-01-01 00:00:00 +0000 | |||
140 | +++ hooks/tests/test_hooks.py 2014-06-29 13:52:25 +0000 | |||
141 | @@ -0,0 +1,514 @@ | |||
142 | 1 | from contextlib import contextmanager | ||
143 | 2 | import unittest | ||
144 | 3 | |||
145 | 4 | from django.conf import settings | ||
146 | 5 | settings.configure() | ||
147 | 6 | |||
148 | 7 | from mock import patch | ||
149 | 8 | |||
150 | 9 | import hooks | ||
151 | 10 | |||
152 | 11 | |||
153 | 12 | class IsPostgresAvailableTest(unittest.TestCase): | ||
154 | 13 | |||
155 | 14 | def setUp(self): | ||
156 | 15 | super(IsPostgresAvailableTest, self).setUp() | ||
157 | 16 | log_patcher = patch("charmhelpers.core.hookenv.log") | ||
158 | 17 | self.mock_log = log_patcher.start() | ||
159 | 18 | self.addCleanup(log_patcher.stop) | ||
160 | 19 | |||
161 | 20 | @patch('charmhelpers.core.hookenv.is_relation_made') | ||
162 | 21 | def test_requires_relation_to_db(self, mock_is_relation_made): | ||
163 | 22 | """is_postgres_available depends on relation to PostgreSQL.""" | ||
164 | 23 | mock_is_relation_made.return_value = False | ||
165 | 24 | self.assertFalse(hooks.is_postgres_available()) | ||
166 | 25 | mock_is_relation_made.assert_called_with("db") | ||
167 | 26 | self.mock_log.assert_called_with("Missing relation to PostgreSQL") | ||
168 | 27 | |||
169 | 28 | @patch('charmhelpers.core.hookenv.is_relation_made') | ||
170 | 29 | @patch('charmhelpers.core.hookenv.local_unit') | ||
171 | 30 | @patch('charmhelpers.core.hookenv.relation_get') | ||
172 | 31 | def test_requires_unit_in_allowed_units( | ||
173 | 32 | self, mock_relation_get, mock_local_unit, mock_is_relation_made): | ||
174 | 33 | """is_postgres_available checks if unit is in allowed-units.""" | ||
175 | 34 | mock_is_relation_made.return_value = True | ||
176 | 35 | mock_local_unit.return_value = "test-unit/0" | ||
177 | 36 | mock_relation_get.return_value = "another-unit/0" | ||
178 | 37 | self.assertFalse(hooks.is_postgres_available()) | ||
179 | 38 | self.mock_log.assert_called_with( | ||
180 | 39 | "Unit is not yet allowed (another-unit/0)") | ||
181 | 40 | |||
182 | 41 | @patch('charmhelpers.core.hookenv.is_relation_made') | ||
183 | 42 | @patch('charmhelpers.core.hookenv.local_unit') | ||
184 | 43 | @patch('charmhelpers.core.hookenv.relation_get') | ||
185 | 44 | def test_checks_database_name( | ||
186 | 45 | self, mock_relation_get, mock_local_unit, mock_is_relation_made): | ||
187 | 46 | """If we are an allowed_unit then check the database name""" | ||
188 | 47 | |||
189 | 48 | def relation_get_side_effect(relation_key): | ||
190 | 49 | if relation_key == "allowed-units": | ||
191 | 50 | return "test-unit/0" | ||
192 | 51 | elif relation_key == "database": | ||
193 | 52 | return "not-reviewboard" | ||
194 | 53 | else: | ||
195 | 54 | assert False, "Unexpected relation key %s" % relation_key | ||
196 | 55 | |||
197 | 56 | mock_is_relation_made.return_value = True | ||
198 | 57 | mock_local_unit.return_value = "test-unit/0" | ||
199 | 58 | mock_relation_get.side_effect = relation_get_side_effect | ||
200 | 59 | self.assertFalse(hooks.is_postgres_available()) | ||
201 | 60 | self.mock_log.assert_called_with( | ||
202 | 61 | "Database provided by PostgreSQL is " | ||
203 | 62 | "not 'reviewboard': 'not-reviewboard'") | ||
204 | 63 | |||
205 | 64 | @patch('charmhelpers.core.hookenv.is_relation_made') | ||
206 | 65 | @patch('charmhelpers.core.hookenv.local_unit') | ||
207 | 66 | @patch('charmhelpers.core.hookenv.relation_get') | ||
208 | 67 | def test_everything_green( | ||
209 | 68 | self, mock_relation_get, mock_local_unit, mock_is_relation_made): | ||
210 | 69 | """When everything is OK, is_postgres_available returns True""" | ||
211 | 70 | def relation_get_side_effect(relation_key): | ||
212 | 71 | if relation_key == "allowed-units": | ||
213 | 72 | return "test-unit/0" | ||
214 | 73 | elif relation_key == "database": | ||
215 | 74 | return "reviewboard" | ||
216 | 75 | else: | ||
217 | 76 | assert False, "Unexpected relation key %s" % relation_key | ||
218 | 77 | |||
219 | 78 | mock_is_relation_made.return_value = True | ||
220 | 79 | mock_local_unit.return_value = "test-unit/0" | ||
221 | 80 | mock_relation_get.side_effect = relation_get_side_effect | ||
222 | 81 | self.assertTrue(hooks.is_postgres_available()) | ||
223 | 82 | self.assertFalse(self.mock_log.called) | ||
224 | 83 | |||
225 | 84 | |||
226 | 85 | class IsRbSiteInstalledTest(unittest.TestCase): | ||
227 | 86 | |||
228 | 87 | @patch("os.path.exists") | ||
229 | 88 | def test_looks_for_site_directory(self, mock_exists): | ||
230 | 89 | mock_exists.return_value = False | ||
231 | 90 | self.assertFalse(hooks.is_rb_site_installed()) | ||
232 | 91 | mock_exists.assert_called_with("/var/www/reviewboard") | ||
233 | 92 | mock_exists.return_value = True | ||
234 | 93 | self.assertTrue(hooks.is_rb_site_installed()) | ||
235 | 94 | |||
236 | 95 | |||
237 | 96 | class InstallRbSiteTest(unittest.TestCase): | ||
238 | 97 | |||
239 | 98 | def setUp(self): | ||
240 | 99 | super(InstallRbSiteTest, self).setUp() | ||
241 | 100 | config_patcher = patch("charmhelpers.core.hookenv.config") | ||
242 | 101 | self.mock_config = config_patcher.start() | ||
243 | 102 | self.addCleanup(config_patcher.stop) | ||
244 | 103 | |||
245 | 104 | log_patcher = patch("charmhelpers.core.hookenv.log") | ||
246 | 105 | self.mock_log = log_patcher.start() | ||
247 | 106 | self.addCleanup(log_patcher.stop) | ||
248 | 107 | |||
249 | 108 | available_patcher = patch("hooks.is_postgres_available") | ||
250 | 109 | self.mock_available = available_patcher.start() | ||
251 | 110 | self.mock_available.return_value = True | ||
252 | 111 | self.addCleanup(available_patcher.stop) | ||
253 | 112 | |||
254 | 113 | def test_guarded_on_is_postgres_available(self): | ||
255 | 114 | self.mock_available.return_value = False | ||
256 | 115 | self.assertFalse(hooks.install_rb_site()) | ||
257 | 116 | self.mock_available.assert_called_with() | ||
258 | 117 | |||
259 | 118 | def test_guarded_on_admin_password(self): | ||
260 | 119 | self.mock_config.return_value = {} | ||
261 | 120 | self.assertFalse(hooks.install_rb_site()) | ||
262 | 121 | self.mock_log.assert_called_with( | ||
263 | 122 | "admin-password is not set, refusing to configure site") | ||
264 | 123 | |||
265 | 124 | @patch('charmhelpers.core.hookenv.unit_get') | ||
266 | 125 | @patch('charmhelpers.core.hookenv.relation_get') | ||
267 | 126 | @patch('subprocess.check_call') | ||
268 | 127 | def test_host_info_from_config_defaults_to_public_address( | ||
269 | 128 | self, mock_call, mock_relation_get, mock_unit_get): | ||
270 | 129 | """ | ||
271 | 130 | The host information is a config value which defaults to | ||
272 | 131 | "localhost". When it is "localhost" that means use the public | ||
273 | 132 | address of the unit. | ||
274 | 133 | """ | ||
275 | 134 | self.mock_config.return_value = {"admin-password": "a-password"} | ||
276 | 135 | mock_unit_get.return_value = "8.8.8.8" | ||
277 | 136 | mock_relation_get.return_value = {"host": "psql.example.com", | ||
278 | 137 | "port": "5432", | ||
279 | 138 | "user": "rb-db-user", | ||
280 | 139 | "password": "hunter2"} | ||
281 | 140 | self.assertTrue(hooks.install_rb_site()) | ||
282 | 141 | args_list, kwargs_list = mock_call.call_args | ||
283 | 142 | self.assertIn("--domain-name=8.8.8.8", args_list[0]) | ||
284 | 143 | self.mock_log.assert_any_call( | ||
285 | 144 | "Installing rb-site for 8.8.8.8 in /var/www/reviewboard") | ||
286 | 145 | |||
287 | 146 | @patch('charmhelpers.core.hookenv.unit_get') | ||
288 | 147 | @patch('charmhelpers.core.hookenv.relation_get') | ||
289 | 148 | @patch('subprocess.check_call') | ||
290 | 149 | def test_db_host_and_port_used_from_relation( | ||
291 | 150 | self, mock_call, mock_relation_get, mock_unit_get): | ||
292 | 151 | self.mock_config.return_value = {"admin-password": "a-password", | ||
293 | 152 | "host": "rb.example.com"} | ||
294 | 153 | mock_relation_get.return_value = {"host": "psql.example.com", | ||
295 | 154 | "port": "5432", | ||
296 | 155 | "user": "rb-db-user", | ||
297 | 156 | "password": "hunter2"} | ||
298 | 157 | self.assertTrue(hooks.install_rb_site()) | ||
299 | 158 | args_list, kwargs_list = mock_call.call_args | ||
300 | 159 | self.assertIn("--db-host=psql.example.com:5432", args_list[0]) | ||
301 | 160 | |||
302 | 161 | @patch('charmhelpers.core.hookenv.unit_get') | ||
303 | 162 | @patch('charmhelpers.core.hookenv.relation_get') | ||
304 | 163 | @patch('subprocess.check_call') | ||
305 | 164 | def test_db_user_used_from_relation( | ||
306 | 165 | self, mock_call, mock_relation_get, mock_unit_get): | ||
307 | 166 | self.mock_config.return_value = {"admin-password": "a-password", | ||
308 | 167 | "host": "rb.example.com"} | ||
309 | 168 | mock_relation_get.return_value = {"host": "psql.example.com", | ||
310 | 169 | "port": "5432", | ||
311 | 170 | "user": "rb-db-user", | ||
312 | 171 | "password": "hunter2"} | ||
313 | 172 | self.assertTrue(hooks.install_rb_site()) | ||
314 | 173 | args_list, kwargs_list = mock_call.call_args | ||
315 | 174 | self.assertIn("--db-user=rb-db-user", args_list[0]) | ||
316 | 175 | |||
317 | 176 | @patch('charmhelpers.core.hookenv.unit_get') | ||
318 | 177 | @patch('charmhelpers.core.hookenv.relation_get') | ||
319 | 178 | @patch('subprocess.check_call') | ||
320 | 179 | def test_db_password_used_from_relation( | ||
321 | 180 | self, mock_call, mock_relation_get, mock_unit_get): | ||
322 | 181 | self.mock_config.return_value = {"admin-password": "a-password", | ||
323 | 182 | "host": "rb.example.com"} | ||
324 | 183 | mock_relation_get.return_value = {"host": "psql.example.com", | ||
325 | 184 | "port": "5432", | ||
326 | 185 | "user": "rb-db-user", | ||
327 | 186 | "password": "hunter2"} | ||
328 | 187 | self.assertTrue(hooks.install_rb_site()) | ||
329 | 188 | args_list, kwargs_list = mock_call.call_args | ||
330 | 189 | self.assertIn("--db-pass=hunter2", args_list[0]) | ||
331 | 190 | |||
332 | 191 | @patch('charmhelpers.core.hookenv.unit_get') | ||
333 | 192 | @patch('charmhelpers.core.hookenv.relation_get') | ||
334 | 193 | @patch('subprocess.check_call') | ||
335 | 194 | def test_admin_password_used_from_config( | ||
336 | 195 | self, mock_call, mock_relation_get, mock_unit_get): | ||
337 | 196 | self.mock_config.return_value = {"admin-password": "a-password", | ||
338 | 197 | "host": "rb.example.com"} | ||
339 | 198 | mock_relation_get.return_value = {"host": "psql.example.com", | ||
340 | 199 | "port": "5432", | ||
341 | 200 | "user": "rb-db-user", | ||
342 | 201 | "password": "hunter2"} | ||
343 | 202 | self.assertTrue(hooks.install_rb_site()) | ||
344 | 203 | args_list, kwargs_list = mock_call.call_args | ||
345 | 204 | self.assertIn("--admin-password=a-password", args_list[0]) | ||
346 | 205 | |||
347 | 206 | @patch('charmhelpers.core.hookenv.unit_get') | ||
348 | 207 | @patch('charmhelpers.core.hookenv.relation_get') | ||
349 | 208 | @patch('subprocess.check_call') | ||
350 | 209 | def test_log_confirmation( | ||
351 | 210 | self, mock_call, mock_relation_get, mock_unit_get): | ||
352 | 211 | self.mock_config.return_value = {"admin-password": "a-password", | ||
353 | 212 | "host": "rb.example.com"} | ||
354 | 213 | mock_relation_get.return_value = {"host": "psql.example.com", | ||
355 | 214 | "port": "5432", | ||
356 | 215 | "user": "rb-db-user", | ||
357 | 216 | "password": "hunter2"} | ||
358 | 217 | self.assertTrue(hooks.install_rb_site()) | ||
359 | 218 | self.mock_log.assert_called_with("Installed rb-site!") | ||
360 | 219 | |||
361 | 220 | @patch('charmhelpers.core.hookenv.unit_get') | ||
362 | 221 | @patch('charmhelpers.core.hookenv.relation_get') | ||
363 | 222 | @patch('subprocess.check_call') | ||
364 | 223 | def test_full_rb_site_install_command( | ||
365 | 224 | self, mock_call, mock_relation_get, mock_unit_get): | ||
366 | 225 | self.mock_config.return_value = {"admin-password": "a-password", | ||
367 | 226 | "host": "rb.example.com"} | ||
368 | 227 | mock_relation_get.return_value = {"host": "psql.example.com", | ||
369 | 228 | "port": "5432", | ||
370 | 229 | "user": "rb-db-user", | ||
371 | 230 | "password": "hunter2"} | ||
372 | 231 | self.assertTrue(hooks.install_rb_site()) | ||
373 | 232 | args_list, kwargs_list = mock_call.call_args | ||
374 | 233 | full_command_args = [ | ||
375 | 234 | "rb-site", | ||
376 | 235 | "install", | ||
377 | 236 | "--noinput", | ||
378 | 237 | "--domain-name=rb.example.com", | ||
379 | 238 | "--db-type=postgresql", | ||
380 | 239 | "--db-name=reviewboard", | ||
381 | 240 | "--db-host=psql.example.com:5432", | ||
382 | 241 | "--db-user=rb-db-user", | ||
383 | 242 | "--db-pass=hunter2", | ||
384 | 243 | "--cache-type=memcached", | ||
385 | 244 | "--web-server-type=apache", | ||
386 | 245 | "--web-server-port=80", | ||
387 | 246 | "--python-loader=wsgi", | ||
388 | 247 | "--admin-user=admin", | ||
389 | 248 | "--admin-password=a-password", | ||
390 | 249 | "/var/www/reviewboard"] | ||
391 | 250 | |||
392 | 251 | self.assertEqual(full_command_args, args_list[0]) | ||
393 | 252 | |||
394 | 253 | |||
395 | 254 | class ConfigureApache2Test(unittest.TestCase): | ||
396 | 255 | |||
397 | 256 | def setUp(self): | ||
398 | 257 | super(ConfigureApache2Test, self).setUp() | ||
399 | 258 | log_patcher = patch("charmhelpers.core.hookenv.log") | ||
400 | 259 | self.mock_log = log_patcher.start() | ||
401 | 260 | self.addCleanup(log_patcher.stop) | ||
402 | 261 | |||
403 | 262 | call_patcher = patch("subprocess.check_call") | ||
404 | 263 | self.mock_call = call_patcher.start() | ||
405 | 264 | self.addCleanup(call_patcher.stop) | ||
406 | 265 | |||
407 | 266 | restart_patcher = patch("hooks.service_restart") | ||
408 | 267 | self.mock_restart = restart_patcher.start() | ||
409 | 268 | self.addCleanup(restart_patcher.stop) | ||
410 | 269 | |||
411 | 270 | def test_copies_wsgi_conf(self): | ||
412 | 271 | hooks.configure_apache2() | ||
413 | 272 | self.mock_call.assert_any_call( | ||
414 | 273 | ["cp", "/var/www/reviewboard/conf/apache-wsgi.conf", | ||
415 | 274 | "/etc/apache2/sites-available/reviewboard.conf"]) | ||
416 | 275 | |||
417 | 276 | def test_disables_default_site(self): | ||
418 | 277 | hooks.configure_apache2() | ||
419 | 278 | self.mock_call.assert_any_call(["a2dissite", "000-default"]) | ||
420 | 279 | |||
421 | 280 | def test_enables_reviewboard_site(self): | ||
422 | 281 | hooks.configure_apache2() | ||
423 | 282 | self.mock_call.assert_any_call(["a2ensite", "reviewboard.conf"]) | ||
424 | 283 | |||
425 | 284 | def test_chowns_dirs(self): | ||
426 | 285 | hooks.configure_apache2() | ||
427 | 286 | for leaf in ["htdocs/media/uploaded", "htdocs/media/ext", | ||
428 | 287 | "htdocs/static/ext", "data"]: | ||
429 | 288 | self.mock_call.assert_any_call( | ||
430 | 289 | ["chown", "-R", "www-data", "/var/www/reviewboard/%s" % leaf]) | ||
431 | 290 | |||
432 | 291 | def test_restarts_apache2(self): | ||
433 | 292 | hooks.configure_apache2() | ||
434 | 293 | self.mock_restart.assert_called_with("apache2") | ||
435 | 294 | |||
436 | 295 | def test_log_confirmation(self): | ||
437 | 296 | hooks.configure_apache2() | ||
438 | 297 | self.mock_log.assert_called_with("Apache configured!") | ||
439 | 298 | self.mock_log.assert_any_call("Configuring Apache...") | ||
440 | 299 | |||
441 | 300 | |||
442 | 301 | class DbRelationJoinedTest(unittest.TestCase): | ||
443 | 302 | |||
444 | 303 | @patch('charmhelpers.core.hookenv.relation_set') | ||
445 | 304 | def test_requests_database(self, mock_relation_set): | ||
446 | 305 | hooks.db_relation_joined() | ||
447 | 306 | mock_relation_set.assert_called_with( | ||
448 | 307 | relation_settings={"database": "reviewboard"}) | ||
449 | 308 | |||
450 | 309 | |||
451 | 310 | class DbRelationChangedTest(unittest.TestCase): | ||
452 | 311 | |||
453 | 312 | @patch('hooks.is_rb_site_installed') | ||
454 | 313 | @patch('hooks.install_rb_site') | ||
455 | 314 | @patch('hooks.configure_apache2') | ||
456 | 315 | def test_configures_apache2_if_rb_needs_installing( | ||
457 | 316 | self, mock_configure, mock_install, mock_installed): | ||
458 | 317 | |||
459 | 318 | def mock_installed_side_effect(): | ||
460 | 319 | mock_installed.side_effect = lambda: True | ||
461 | 320 | return False | ||
462 | 321 | |||
463 | 322 | mock_installed.side_effect = mock_installed_side_effect | ||
464 | 323 | mock_install.return_value = True | ||
465 | 324 | hooks.db_relation_changed() | ||
466 | 325 | mock_install.assert_called_with() | ||
467 | 326 | mock_configure.assert_called_with() | ||
468 | 327 | |||
469 | 328 | @patch('hooks.is_rb_site_installed') | ||
470 | 329 | @patch('hooks.configure_apache2') | ||
471 | 330 | def test_configures_apache2_if_rb_already_installed( | ||
472 | 331 | self, mock_configure, mock_installed): | ||
473 | 332 | mock_installed.return_value = True | ||
474 | 333 | hooks.db_relation_changed() | ||
475 | 334 | mock_configure.assert_called_with() | ||
476 | 335 | |||
477 | 336 | |||
478 | 337 | class WebsiteRelationJoinedTest(unittest.TestCase): | ||
479 | 338 | |||
480 | 339 | @patch('charmhelpers.core.hookenv.unit_get') | ||
481 | 340 | @patch('charmhelpers.core.hookenv.relation_set') | ||
482 | 341 | def test_requests_database(self, mock_relation_set, mock_unit_get): | ||
483 | 342 | mock_unit_get.return_value = "127.0.0.1" | ||
484 | 343 | hooks.website_relation_joined() | ||
485 | 344 | mock_relation_set.assert_called_with( | ||
486 | 345 | relation_settings={"hostname": "127.0.0.1", "port": 80}) | ||
487 | 346 | |||
488 | 347 | |||
489 | 348 | class ConfigChangedTest(unittest.TestCase): | ||
490 | 349 | |||
491 | 350 | def setUp(self): | ||
492 | 351 | super(ConfigChangedTest, self).setUp() | ||
493 | 352 | config_patcher = patch("charmhelpers.core.hookenv.config") | ||
494 | 353 | self.mock_config = config_patcher.start() | ||
495 | 354 | self.mock_config.return_value = {} | ||
496 | 355 | self.addCleanup(config_patcher.stop) | ||
497 | 356 | |||
498 | 357 | @contextmanager | ||
499 | 358 | def _fake_django_environ(): | ||
500 | 359 | yield | ||
501 | 360 | |||
502 | 361 | @patch('hooks.is_rb_site_installed') | ||
503 | 362 | @patch('hooks.install_rb_site') | ||
504 | 363 | @patch('hooks.configure_apache2') | ||
505 | 364 | def test_configures_apache2_if_rb_needs_installing( | ||
506 | 365 | self, mock_configure, mock_install, mock_installed): | ||
507 | 366 | |||
508 | 367 | def mock_installed_side_effect(): | ||
509 | 368 | mock_installed.side_effect = lambda: True | ||
510 | 369 | return False | ||
511 | 370 | |||
512 | 371 | mock_installed.side_effect = mock_installed_side_effect | ||
513 | 372 | mock_install.return_value = True | ||
514 | 373 | hooks.config_changed() | ||
515 | 374 | mock_install.assert_called_with() | ||
516 | 375 | mock_configure.assert_called_with() | ||
517 | 376 | |||
518 | 377 | @patch('hooks.is_rb_site_installed') | ||
519 | 378 | @patch('hooks.configure_apache2') | ||
520 | 379 | def test_configures_apache2_if_rb_already_installed( | ||
521 | 380 | self, mock_configure, mock_installed): | ||
522 | 381 | mock_installed.return_value = True | ||
523 | 382 | hooks.config_changed() | ||
524 | 383 | mock_configure.assert_called_with() | ||
525 | 384 | |||
526 | 385 | @patch('hooks.is_rb_site_installed') | ||
527 | 386 | @patch('hooks.configure_apache2') | ||
528 | 387 | @patch('hooks._django_environ', _fake_django_environ) | ||
529 | 388 | @patch("django.contrib.auth.management.commands.changepassword.Command") | ||
530 | 389 | def test_changes_password_for_admin_user( | ||
531 | 390 | self, mock_command, mock_configure, mock_installed): | ||
532 | 391 | mock_installed.return_value = True | ||
533 | 392 | self.mock_config.return_value = {"admin-password": "a-password"} | ||
534 | 393 | instance = mock_command.return_value | ||
535 | 394 | hooks.config_changed() | ||
536 | 395 | instance.execute.assert_called_with("admin") | ||
537 | 396 | self.assertEqual(instance._get_pass(), "a-password") | ||
538 | 397 | |||
539 | 398 | @patch('hooks.is_rb_site_installed') | ||
540 | 399 | @patch('hooks.configure_apache2') | ||
541 | 400 | @patch('hooks._django_environ', _fake_django_environ) | ||
542 | 401 | @patch("django.contrib.sites.models.Site") | ||
543 | 402 | def test_changes_host(self, mock_site, mock_configure, mock_installed): | ||
544 | 403 | mock_installed.return_value = True | ||
545 | 404 | self.mock_config.return_value = {"host": "rb.example.com"} | ||
546 | 405 | instance = mock_site.objects.get_current.return_value | ||
547 | 406 | hooks.config_changed() | ||
548 | 407 | instance.save.assert_called_with() | ||
549 | 408 | self.assertEqual(instance.domain, "rb.example.com") | ||
550 | 409 | |||
551 | 410 | @patch('hooks.is_rb_site_installed') | ||
552 | 411 | @patch('hooks.configure_apache2') | ||
553 | 412 | @patch('charmhelpers.core.hookenv.unit_get') | ||
554 | 413 | @patch('hooks._django_environ', _fake_django_environ) | ||
555 | 414 | @patch("django.contrib.sites.models.Site") | ||
556 | 415 | def test_localhost_means_public_address( | ||
557 | 416 | self, mock_site, mock_unit_get, mock_configure, mock_installed): | ||
558 | 417 | mock_installed.return_value = True | ||
559 | 418 | self.mock_config.return_value = {"host": "localhost"} | ||
560 | 419 | mock_unit_get.return_value = "8.8.8.8" | ||
561 | 420 | instance = mock_site.objects.get_current.return_value | ||
562 | 421 | hooks.config_changed() | ||
563 | 422 | instance.save.assert_called_with() | ||
564 | 423 | self.assertEqual(instance.domain, "8.8.8.8") | ||
565 | 424 | |||
566 | 425 | |||
567 | 426 | class InstallTest(unittest.TestCase): | ||
568 | 427 | |||
569 | 428 | def setUp(self): | ||
570 | 429 | super(InstallTest, self).setUp() | ||
571 | 430 | log_patcher = patch("charmhelpers.core.hookenv.log") | ||
572 | 431 | self.mock_log = log_patcher.start() | ||
573 | 432 | self.addCleanup(log_patcher.stop) | ||
574 | 433 | |||
575 | 434 | update_patcher = patch("hooks.apt_update") | ||
576 | 435 | self.mock_update = update_patcher.start() | ||
577 | 436 | self.addCleanup(update_patcher.stop) | ||
578 | 437 | |||
579 | 438 | install_patcher = patch("hooks.apt_install") | ||
580 | 439 | self.mock_install = install_patcher.start() | ||
581 | 440 | self.addCleanup(install_patcher.stop) | ||
582 | 441 | |||
583 | 442 | call_patcher = patch("subprocess.check_call") | ||
584 | 443 | self.mock_call = call_patcher.start() | ||
585 | 444 | self.addCleanup(call_patcher.stop) | ||
586 | 445 | |||
587 | 446 | def test_apt_packages(self): | ||
588 | 447 | hooks.install() | ||
589 | 448 | self.mock_install.assert_called_with( | ||
590 | 449 | ["apache2-mpm-prefork", | ||
591 | 450 | "libapache2-mod-wsgi", | ||
592 | 451 | "python-setuptools", | ||
593 | 452 | "python-dev", | ||
594 | 453 | "memcached", | ||
595 | 454 | "python-memcache", | ||
596 | 455 | "patch", | ||
597 | 456 | "python-psycopg2"], fatal=True) | ||
598 | 457 | |||
599 | 458 | def test_apt_update(self): | ||
600 | 459 | hooks.install() | ||
601 | 460 | self.mock_update.assert_called_with(fatal=True) | ||
602 | 461 | |||
603 | 462 | def test_easy_install(self): | ||
604 | 463 | hooks.install() | ||
605 | 464 | self.mock_call.assert_called_with(["easy_install", "ReviewBoard"]) | ||
606 | 465 | |||
607 | 466 | def test_log(self): | ||
608 | 467 | hooks.install() | ||
609 | 468 | self.mock_log.assert_called_with("Installing ReviewBoard") | ||
610 | 469 | |||
611 | 470 | |||
612 | 471 | class StartTest(unittest.TestCase): | ||
613 | 472 | |||
614 | 473 | @patch("hooks.service_restart") | ||
615 | 474 | @patch('charmhelpers.core.hookenv.open_port') | ||
616 | 475 | def test_restarts_apache(self, mock_open_port, mock_restart): | ||
617 | 476 | hooks.start() | ||
618 | 477 | mock_restart.assert_called_with("apache2") | ||
619 | 478 | |||
620 | 479 | @patch("hooks.service_restart") | ||
621 | 480 | @patch('charmhelpers.core.hookenv.open_port') | ||
622 | 481 | def test_opens_port_80(self, mock_open_port, mock_restart): | ||
623 | 482 | hooks.start() | ||
624 | 483 | mock_open_port.assert_called_with(80) | ||
625 | 484 | |||
626 | 485 | |||
627 | 486 | class StopTest(unittest.TestCase): | ||
628 | 487 | |||
629 | 488 | @patch("hooks.service_stop") | ||
630 | 489 | @patch('charmhelpers.core.hookenv.close_port') | ||
631 | 490 | def test_stops_apache(self, mock_close_port, mock_stop): | ||
632 | 491 | hooks.stop() | ||
633 | 492 | mock_stop.assert_called_with("apache2") | ||
634 | 493 | |||
635 | 494 | @patch("hooks.service_stop") | ||
636 | 495 | @patch('charmhelpers.core.hookenv.close_port') | ||
637 | 496 | def test_closes_port_80(self, mock_close_port, mock_stop): | ||
638 | 497 | hooks.stop() | ||
639 | 498 | mock_close_port.assert_called_with(80) | ||
640 | 499 | |||
641 | 500 | |||
642 | 501 | class MainTest(unittest.TestCase): | ||
643 | 502 | |||
644 | 503 | @patch("hooks.hooks") | ||
645 | 504 | def test_executes_sys_argv(self, mock_hooks): | ||
646 | 505 | hooks.main(["arg", "v"]) | ||
647 | 506 | mock_hooks.execute.assert_called_with(["arg", "v"]) | ||
648 | 507 | |||
649 | 508 | @patch("hooks.hooks") | ||
650 | 509 | @patch("charmhelpers.core.hookenv.log") | ||
651 | 510 | def test_logs_about_unknown_hooks(self, mock_log, mock_hooks): | ||
652 | 511 | mock_hooks.execute.side_effect =\ | ||
653 | 512 | hooks.hookenv.UnregisteredHookError("missing-hook") | ||
654 | 513 | hooks.main(["arg", "v"]) | ||
655 | 514 | mock_log.assert_called_with("Unknown hook missing-hook - skipping.") |
Adam,
Thanks for the updated test suite and the make targets.
+1 LGTM