Merge ~hloeung/jenkins-agent-charm:master into jenkins-agent-charm:master
- Git
- lp:~hloeung/jenkins-agent-charm
- master
- Merge into master
Proposed by
Haw Loeung
Status: | Merged |
---|---|
Approved by: | Haw Loeung |
Approved revision: | b1a4abd7e54cb8bfc20d167bc6c57f17e1b5f0ab |
Merged at revision: | 98c0339c7227b49a74cd9583da140bd00b6ffd8b |
Proposed branch: | ~hloeung/jenkins-agent-charm:master |
Merge into: | jenkins-agent-charm:master |
Diff against target: |
353 lines (+207/-24) 3 files modified
pytest.ini (+6/-0) tests/unit/test_jenkins_slave.py (+200/-22) tox.ini (+1/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stuart Bishop (community) | Approve | ||
Haw Loeung | Needs Resubmitting | ||
Joel Sing (community) | Needs Information | ||
Review via email: mp+364302@code.launchpad.net |
Commit message
Unit test all the things.
Description of the change
It's still missing unit tests for configure_nagios() which I'll work through next.
To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Revision history for this message
Joel Sing (jsing) wrote : | # |
The layer status change is a functional change, which I think should be split out and handled in a separate merge proposal to the testing additions (unless I'm missing the reason this cannot be done).
review:
Needs Information
Revision history for this message
Haw Loeung (hloeung) wrote : | # |
Okay, I've split out the change to layer-status to a separate MP - https:/
review:
Needs Resubmitting
Revision history for this message
Stuart Bishop (stub) wrote : | # |
This all looks great.
The module mocks need to be reset in the TestCase's setUp method, rather than in the tests themselves.
review:
Approve
Revision history for this message
Haw Loeung (hloeung) : | # |
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision 98c0339c7227b49
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/pytest.ini b/pytest.ini |
2 | new file mode 100644 |
3 | index 0000000..39da980 |
4 | --- /dev/null |
5 | +++ b/pytest.ini |
6 | @@ -0,0 +1,6 @@ |
7 | +[pytest] |
8 | +filterwarnings = |
9 | + # https://github.com/juju/charm-helpers/issues/294 |
10 | + ignore:.*inspect.getargspec\(\) is deprecated.*:DeprecationWarning |
11 | + # https://github.com/juju/charm-helpers/issues/293 |
12 | + ignore:.*dist\(\) and linux_distribution\(\) functions are deprecated:PendingDeprecationWarning |
13 | diff --git a/tests/unit/test_jenkins_slave.py b/tests/unit/test_jenkins_slave.py |
14 | index 7bd3ead..53e0e51 100644 |
15 | --- a/tests/unit/test_jenkins_slave.py |
16 | +++ b/tests/unit/test_jenkins_slave.py |
17 | @@ -2,21 +2,33 @@ import grp |
18 | import os |
19 | import pwd |
20 | import shutil |
21 | +import stat |
22 | import sys |
23 | import tempfile |
24 | import unittest |
25 | from unittest import mock |
26 | |
27 | + |
28 | +# We also need to mock up charms.apt and charms.layer so we can run |
29 | +# unit tests without having to build the charm and pull in layers such |
30 | +# as layer-status. |
31 | sys.modules['charms.apt'] = mock.MagicMock() |
32 | -from charms import apt # NOQA: E402 |
33 | sys.modules['charms.layer'] = mock.MagicMock() |
34 | +from charms import apt # NOQA: E402 |
35 | +from charms.layer import status # NOQA: E402 |
36 | |
37 | # Add path to where our reactive layer lives and import. |
38 | sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))) |
39 | from reactive.jenkins_slave import ( |
40 | + upgrade_charm, |
41 | config_changed, |
42 | install, |
43 | configure_jenkins_slave, |
44 | + blocked_on_jenkins_url, |
45 | + set_active, |
46 | + slave_relation_changed, |
47 | + slave_relation_removed, |
48 | + slave_relation, |
49 | file_to_units, |
50 | write_default_conf, |
51 | ) # NOQA: E402 |
52 | @@ -27,7 +39,9 @@ INITIAL_CONF = 'tests/unit/files/jenkins-slave-default' |
53 | |
54 | class TestSetDefaultConf(unittest.TestCase): |
55 | def setUp(self): |
56 | + self.maxDiff = None |
57 | self.tmpdir = tempfile.mkdtemp(prefix='charm-unittests-') |
58 | + self.addCleanup(shutil.rmtree, self.tmpdir) |
59 | temp_file = tempfile.NamedTemporaryFile(delete=False, dir=self.tmpdir) |
60 | with open(INITIAL_CONF, 'rb') as f: |
61 | conf = f.read().decode('utf-8') |
62 | @@ -56,46 +70,103 @@ class TestSetDefaultConf(unittest.TestCase): |
63 | self.addCleanup(patcher.stop) |
64 | self.mock_local_unit.return_value = 'mock-jenkins-slave/0' |
65 | |
66 | + patcher = mock.patch('charmhelpers.core.hookenv.unit_private_ip') |
67 | + self.mock_local_unit = patcher.start() |
68 | + self.addCleanup(patcher.stop) |
69 | + self.mock_local_unit.return_value = '10.1.2.3' |
70 | + |
71 | patcher = mock.patch('charmhelpers.core.hookenv.log') |
72 | self.mock_log = patcher.start() |
73 | self.addCleanup(patcher.stop) |
74 | self.mock_log.return_value = '' |
75 | |
76 | - def tearDown(self): |
77 | - shutil.rmtree(self.tmpdir) |
78 | + patcher = mock.patch('os.uname') |
79 | + self.mock_log = patcher.start() |
80 | + self.addCleanup(patcher.stop) |
81 | + self.mock_log.return_value = ('Linux', 'mock-jenkins-slave', '4.15.0-46-generic', |
82 | + '#49-Ubuntu SMP Wed Feb 6 09:33:07 UTC 2019', 'x86_64') |
83 | + |
84 | + apt.reset_mock() |
85 | + status.active.reset_mock() |
86 | + status.maintenance.reset_mock() |
87 | + |
88 | + @mock.patch('charms.reactive.clear_flag') |
89 | + def test_hook_upgrade_charm_flags(self, clear_flag): |
90 | + '''Test correct flags set via upgrade-charm hook''' |
91 | + upgrade_charm() |
92 | + self.assertFalse(status.maintenance.assert_called()) |
93 | + expected = [mock.call('jenkins-slave.active'), mock.call('jenkins-slave.installed')] |
94 | + self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True)) |
95 | |
96 | @mock.patch('charms.reactive.clear_flag') |
97 | def test_hook_config_changed(self, clear_flag): |
98 | + '''Test correct flags are set via config-changed charm hook''' |
99 | config_changed() |
100 | + self.assertFalse(status.maintenance.assert_called()) |
101 | expected = [mock.call('jenkins-slave.blocked'), |
102 | mock.call('jenkins-slave.configured'), |
103 | mock.call('nagios-nrpe.configured')] |
104 | - self.assertEqual(clear_flag.call_args_list, expected) |
105 | + self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True)) |
106 | |
107 | + @mock.patch('charmhelpers.core.hookenv.config') |
108 | @mock.patch('charmhelpers.core.host.adduser') |
109 | @mock.patch('charmhelpers.core.host.mkdir') |
110 | @mock.patch('charmhelpers.core.host.service') |
111 | + @mock.patch('charmhelpers.core.host.user_exists') |
112 | @mock.patch('reactive.jenkins_slave.apt_purge') |
113 | @mock.patch('reactive.jenkins_slave.file_to_units') |
114 | @mock.patch('reactive.jenkins_slave.write_default_conf') |
115 | - def test_hook_install(self, write_default_conf, file_to_units, apt_purge, service, mkdir, adduser): |
116 | + def test_hook_install( |
117 | + self, write_default_conf, file_to_units, apt_purge, user_exists, service, |
118 | + mkdir, adduser, config): |
119 | + config.return_value = {'tools': 'some-tools-package'} |
120 | + apt.install_queued.return_value = True |
121 | + user_exists.return_value = False |
122 | install() |
123 | + self.assertFalse(status.maintenance.assert_called()) |
124 | + |
125 | expected = [mock.call(home_dir='/var/lib/jenkins', system_user=True, username='jenkins')] |
126 | - self.assertEqual(adduser.call_args_list, expected) |
127 | + self.assertFalse(adduser.assert_has_calls(expected, any_order=True)) |
128 | expected = [mock.call('/var/lib/jenkins', group='jenkins', owner='jenkins'), |
129 | mock.call('/var/log/jenkins', group='jenkins', owner='jenkins')] |
130 | - self.assertEqual(mkdir.call_args_list, expected) |
131 | - self.assertEqual(service.call_args_list, [mock.call('enable', 'jenkins-slave')]) |
132 | - self.assertEqual(apt_purge.call_args_list, [mock.call(['jenkins-slave'])]) |
133 | + self.assertFalse(mkdir.assert_has_calls(expected, any_order=True)) |
134 | + self.assertFalse(service.assert_has_calls([mock.call('enable', 'jenkins-slave')], any_order=True)) |
135 | + self.assertFalse(apt_purge.assert_has_calls([mock.call(['jenkins-slave'])], any_order=True)) |
136 | expected = [mock.call('files/download-slave.sh', '/usr/local/sbin/download-slave.sh'), |
137 | mock.call('files/jenkins-slave-logrotate-config', '/etc/logrotate.d/jenkins-slave'), |
138 | mock.call('files/jenkins-slave-systemd-config', '/lib/systemd/system/jenkins-slave.service')] |
139 | - self.assertEqual(file_to_units.call_args_list, expected) |
140 | - self.assertEqual(write_default_conf.call_args_list, [mock.call()]) |
141 | - expected = [mock.call.queue_install(['wget', 'default-jre-headless']), |
142 | + self.assertFalse(file_to_units.assert_has_calls(expected, any_order=True)) |
143 | + self.assertFalse(write_default_conf.assert_called()) |
144 | + expected = [mock.call.queue_install(['wget', 'default-jre-headless', 'some-tools-package']), |
145 | mock.call.install_queued()] |
146 | self.assertEqual(apt.method_calls, expected) |
147 | |
148 | + apt.install_queued.return_value = False |
149 | + apt_purge.reset_mock() |
150 | + install() |
151 | + self.assertFalse(apt_purge.assert_not_called()) |
152 | + |
153 | + apt.install_queued.return_value = True |
154 | + user_exists.return_value = True |
155 | + adduser.reset_mock() |
156 | + install() |
157 | + self.assertFalse(adduser.assert_not_called()) |
158 | + |
159 | + @mock.patch('charmhelpers.core.host.lsb_release') |
160 | + @mock.patch('charmhelpers.core.host.adduser') |
161 | + @mock.patch('charmhelpers.core.host.mkdir') |
162 | + @mock.patch('reactive.jenkins_slave.file_to_units') |
163 | + @mock.patch('reactive.jenkins_slave.write_default_conf') |
164 | + def test_hook_install_trusty(self, write_default_conf, file_to_units, mkdir, adduser, lsb_release): |
165 | + apt.install_queued.return_value = True |
166 | + lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'} |
167 | + install() |
168 | + self.assertFalse(status.maintenance.assert_called()) |
169 | + |
170 | + # file_to_units('files/jenkins-slave-upstart-config', '/etc/init/jenkins-slave.conf') |
171 | + expected = [mock.call('files/jenkins-slave-upstart-config', '/etc/init/jenkins-slave.conf')] |
172 | + self.assertFalse(file_to_units.assert_has_calls(expected, any_order=True)) |
173 | + |
174 | @mock.patch('charms.reactive.clear_flag') |
175 | @mock.patch('charms.reactive.set_flag') |
176 | @mock.patch('charmhelpers.core.hookenv.config') |
177 | @@ -107,9 +178,9 @@ class TestSetDefaultConf(unittest.TestCase): |
178 | config.return_value = {} |
179 | unitdata_kv.return_value = {} |
180 | configure_jenkins_slave() |
181 | - self.assertEqual(write_default_conf.call_args_list, []) |
182 | - self.assertEqual(set_flag.call_args_list, [mock.call('jenkins-slave.blocked')]) |
183 | - self.assertEqual(clear_flag.call_args_list, [mock.call('jenkins-slave.active')]) |
184 | + self.assertFalse(write_default_conf.assert_not_called()) |
185 | + self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.blocked')], any_order=True)) |
186 | + self.assertFalse(clear_flag.assert_has_calls([mock.call('jenkins-slave.active')], any_order=True)) |
187 | |
188 | @mock.patch('charms.reactive.clear_flag') |
189 | @mock.patch('charms.reactive.set_flag') |
190 | @@ -122,10 +193,10 @@ class TestSetDefaultConf(unittest.TestCase): |
191 | config.return_value = {'master_url': 'http://10.1.1.1:8080'} |
192 | unitdata_kv.return_value = {} |
193 | configure_jenkins_slave() |
194 | - self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.1.1.1:8080')]) |
195 | - self.assertEqual(set_flag.call_args_list, [mock.call('jenkins-slave.configured')]) |
196 | + self.assertFalse(write_default_conf.assert_called_once_with('http://10.1.1.1:8080')) |
197 | + self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True)) |
198 | expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')] |
199 | - self.assertEqual(clear_flag.call_args_list, expected) |
200 | + self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True)) |
201 | |
202 | @mock.patch('charms.reactive.clear_flag') |
203 | @mock.patch('charms.reactive.set_flag') |
204 | @@ -138,11 +209,107 @@ class TestSetDefaultConf(unittest.TestCase): |
205 | config.return_value = {} |
206 | unitdata_kv.return_value = {'url': 'http://10.22.22.22:8080'} |
207 | configure_jenkins_slave() |
208 | - print(write_default_conf.call_args_list) |
209 | - self.assertEqual(write_default_conf.call_args_list, [mock.call('http://10.22.22.22:8080')]) |
210 | - self.assertEqual(set_flag.call_args_list, [mock.call('jenkins-slave.configured')]) |
211 | + self.assertTrue(write_default_conf.called_once_with('http://10.22.22.22:8080')) |
212 | + self.assertFalse(set_flag.assert_has_calls([mock.call('jenkins-slave.configured')], any_order=True)) |
213 | expected = [mock.call('jenkins-slave.active'), mock.call('nagios-nrpe.configured')] |
214 | - self.assertEqual(clear_flag.call_args_list, expected) |
215 | + self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True)) |
216 | + |
217 | + @mock.patch('charms.reactive.clear_flag') |
218 | + def test_blocked_on_jenkins_url(self, clear_flag): |
219 | + blocked_on_jenkins_url() |
220 | + self.assertFalse(clear_flag.assert_has_calls([mock.call('jenkins-slave.active')], any_order=True)) |
221 | + |
222 | + @mock.patch('charmhelpers.core.host.service_running') |
223 | + @mock.patch('charmhelpers.core.host.service_restart') |
224 | + @mock.patch('charmhelpers.core.host.service_start') |
225 | + def test_set_active_running(self, service_start, service_restart, service_running): |
226 | + '''Test service restarted when already running''' |
227 | + service_running.return_value = True |
228 | + set_active() |
229 | + self.assertFalse(status.maintenance.assert_called()) |
230 | + self.assertFalse(service_start.assert_not_called()) |
231 | + self.assertFalse(service_restart.assert_called_once_with('jenkins-slave')) |
232 | + |
233 | + @mock.patch('charmhelpers.core.host.service_running') |
234 | + @mock.patch('charmhelpers.core.host.service_restart') |
235 | + @mock.patch('charmhelpers.core.host.service_start') |
236 | + def test_set_active_not_running(self, service_start, service_restart, service_running): |
237 | + '''Test service restarted when not running''' |
238 | + service_running.return_value = False |
239 | + set_active() |
240 | + self.assertFalse(status.maintenance.assert_called()) |
241 | + self.assertFalse(service_start.assert_called_once_with('jenkins-slave')) |
242 | + self.assertFalse(service_restart.assert_not_called()) |
243 | + |
244 | + @mock.patch('charms.reactive.clear_flag') |
245 | + @mock.patch('charms.reactive.set_flag') |
246 | + def test_hook_slave_relation_changed_flags(self, set_flag, clear_flag): |
247 | + slave_relation_changed() |
248 | + expected = [ |
249 | + mock.call('jenkins-slave.blocked'), |
250 | + mock.call('jenkins-slave.configured'), |
251 | + mock.call('slave-relation.configured'), |
252 | + ] |
253 | + self.assertFalse(clear_flag.assert_has_calls(expected, any_order=True)) |
254 | + self.assertFalse(set_flag.assert_has_calls([mock.call('slave-relation.available')], any_order=True)) |
255 | + |
256 | + @mock.patch('charms.reactive.clear_flag') |
257 | + def test_hook_slave_relation_removed_flags(self, clear_flag): |
258 | + slave_relation_removed() |
259 | + self.assertFalse(clear_flag.assert_has_calls([mock.call('slave-relation.available')], any_order=True)) |
260 | + |
261 | + @mock.patch('charms.reactive.clear_flag') |
262 | + @mock.patch('charms.reactive.set_flag') |
263 | + @mock.patch('charmhelpers.core.hookenv.config') |
264 | + @mock.patch('charmhelpers.core.hookenv.relation_get') |
265 | + @mock.patch('charmhelpers.core.hookenv.relation_set') |
266 | + @mock.patch('charmhelpers.core.unitdata.kv') |
267 | + @mock.patch('reactive.jenkins_slave.write_default_conf') |
268 | + def test_hook_slave_relation( |
269 | + self, write_default_conf, unitdata_kv, relation_set, relation_get, |
270 | + config, set_flag, clear_flag): |
271 | + config.return_value = {'master_url': ''} |
272 | + relation_get.return_value = 'http://10.1.1.1:8080' |
273 | + slave_relation() |
274 | + self.assertFalse(relation_get.assert_called_once_with('url')) |
275 | + self.assertFalse(clear_flag.assert_called_once_with('jenkins-slave.active')) |
276 | + self.assertFalse(set_flag.assert_called_once_with('slave-relation.configured')) |
277 | + expected = [mock.call(executors=2), |
278 | + mock.call(labels='x86_64'), |
279 | + mock.call(slavehost='mock-jenkins-slave-0'), |
280 | + mock.call(slaveaddress='10.1.2.3')] |
281 | + self.assertFalse(relation_set.assert_has_calls(expected, any_order=True)) |
282 | + |
283 | + config.return_value = {'master_url': '', 'labels': 'label1 label2'} |
284 | + relation_get.return_value = 'http://10.1.1.1:8080' |
285 | + relation_set.reset_mock() |
286 | + slave_relation() |
287 | + expected = [mock.call(executors=2), |
288 | + mock.call(labels='label1 label2'), |
289 | + mock.call(slavehost='mock-jenkins-slave-0'), |
290 | + mock.call(slaveaddress='10.1.2.3')] |
291 | + self.assertFalse(relation_set.assert_has_calls(expected, any_order=True)) |
292 | + |
293 | + @mock.patch('charmhelpers.core.hookenv.config') |
294 | + @mock.patch('charmhelpers.core.hookenv.relation_get') |
295 | + @mock.patch('charms.reactive.set_flag') |
296 | + def test_hook_slave_relation_master_url_set(self, set_flag, relation_get, config): |
297 | + config.return_value = {'master_url': 'http://10.1.1.1:8080'} |
298 | + relation_get.return_value = '' |
299 | + slave_relation() |
300 | + self.assertFalse(relation_get.assert_not_called()) |
301 | + self.assertFalse(set_flag.assert_called_once_with('slave-relation.configured')) |
302 | + |
303 | + @mock.patch('charms.reactive.clear_flag') |
304 | + @mock.patch('charms.reactive.set_flag') |
305 | + @mock.patch('charmhelpers.core.hookenv.config') |
306 | + @mock.patch('charmhelpers.core.hookenv.relation_get') |
307 | + def test_hook_slave_relation_relation_url_not_set( |
308 | + self, relation_get, config, set_flag, clear_flag): |
309 | + config.return_value = {'master_url': ''} |
310 | + relation_get.return_value = '' |
311 | + slave_relation() |
312 | + self.assertFalse(clear_flag.assert_not_called()) |
313 | |
314 | def test_write_default_conf_update(self): |
315 | write_default_conf('http://10.1.1.1:8080', self.user, self.group, self.conf_file) |
316 | @@ -196,6 +363,17 @@ class TestSetDefaultConf(unittest.TestCase): |
317 | self.assertEqual(grp.getgrgid(os.stat(dest).st_gid).gr_name, self.group) |
318 | self.assertFalse(os.access(dest, os.X_OK)) |
319 | |
320 | + def test_file_to_units_with_perms(self): |
321 | + source = os.path.join(self.charm_dir, 'tests/unit/files/somefile') |
322 | + dest = os.path.join(self.tmpdir, os.path.basename(source)) |
323 | + file_to_units(source, dest, owner=self.user, group=self.group, perms=0o640) |
324 | + with open(dest, 'rb') as fh: |
325 | + want = fh.read().decode('utf-8') |
326 | + self.assertTrue(conf_equals(source, want)) |
327 | + self.assertEqual(pwd.getpwuid(os.stat(dest).st_uid).pw_name, self.user) |
328 | + self.assertEqual(grp.getgrgid(os.stat(dest).st_gid).gr_name, self.group) |
329 | + self.assertEqual(os.stat(dest)[stat.ST_MODE], 0o100640) |
330 | + |
331 | |
332 | def conf_equals(conf_file, want): |
333 | with open(conf_file, 'rb') as conf: |
334 | diff --git a/tox.ini b/tox.ini |
335 | index ee5dbeb..d6b7675 100644 |
336 | --- a/tox.ini |
337 | +++ b/tox.ini |
338 | @@ -9,7 +9,7 @@ setenv = |
339 | PYTHONPATH = . |
340 | |
341 | [testenv:unit] |
342 | -commands = pytest -v --ignore {toxinidir}/tests/functional --cov=lib --cov=reactive --cov=actions --cov-report=term |
343 | +commands = pytest -v --ignore {toxinidir}/tests/functional --cov=lib --cov=reactive --cov=actions --cov-report=term-missing --cov-branch |
344 | deps = -r{toxinidir}/tests/unit/requirements.txt |
345 | -r{toxinidir}/requirements.txt |
346 | setenv = PYTHONPATH={toxinidir}/lib |
347 | @@ -32,6 +32,5 @@ exclude = |
348 | .git, |
349 | __pycache__, |
350 | .tox, |
351 | - hooks/install.d/, |
352 | max-line-length = 120 |
353 | max-complexity = 10 |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.