From 5b16aa9c55b850f67783ca4b77cb91347f7485cc Mon Sep 17 00:00:00 2001 From: Darragh Bailey Date: Fri, 8 May 2015 21:07:23 +0100 Subject: [PATCH] Test update command with --delete-old option Add simple test to ensure the update command results in update_job() being called as expected and a complex scenario to test that when only some jobs are updated, that passing --delete-old option to update will only remove those managed jobs no longer listed and cannot include jobs that were skipped previously. Additionally this has been tested with the problem commit for adding parallel thread usage and after adjusting the names of the functions mocked to align with the rename demonstrated the bug encountered by applying commit 7100a7f225e60936f23d24c757c1b14c9b136ec4. Change-Id: I9c8a0bf86be2e67e012a3cf2a46530fd2622712b --- tests/cmd/subcommands/test_update.py | 101 +++++++++++++++++++++++++++ tests/cmd/test_cmd.py | 11 ++- 2 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 tests/cmd/subcommands/test_update.py diff --git a/tests/cmd/subcommands/test_update.py b/tests/cmd/subcommands/test_update.py new file mode 100644 index 000000000..9e524678f --- /dev/null +++ b/tests/cmd/subcommands/test_update.py @@ -0,0 +1,101 @@ + +# Joint copyright: +# - Copyright 2015 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os + +from jenkins_jobs import cmd +from jenkins_jobs import builder +from tests.base import mock +from tests.cmd.test_cmd import CmdTestsBase + + +@mock.patch('jenkins_jobs.builder.Jenkins.get_plugins_info', mock.MagicMock) +class UpdateTests(CmdTestsBase): + + @mock.patch('jenkins_jobs.cmd.Builder.update_job') + def test_update_jobs(self, update_job_mock): + """ + Test update_job is called + """ + # don't care about the value returned here + update_job_mock.return_value = ([], 0) + + path = os.path.join(self.fixtures_path, 'cmd-002.yaml') + args = self.parser.parse_args(['update', path]) + + cmd.execute(args, self.config) + update_job_mock.assert_called_with([path], []) + + @mock.patch('jenkins_jobs.builder.Jenkins.is_job', return_value=True) + @mock.patch('jenkins_jobs.builder.Jenkins.get_jobs') + @mock.patch('jenkins_jobs.builder.Builder.delete_job') + @mock.patch('jenkins_jobs.cmd.Builder') + def test_update_jobs_and_delete_old(self, builder_mock, delete_job_mock, + get_jobs_mock, is_job_mock): + """ + Test update behaviour with --delete-old option + + Test update of jobs with the --delete-old option enabled, where only + some jobs result in has_changed() to limit the number of times + update_job is called, and have the get_jobs() method return additional + jobs not in the input yaml to test that the code in cmd will call + delete_job() after update_job() when '--delete-old' is set but only + for the extra jobs. + """ + # set up some test data + jobs = ['old_job001', 'old_job002'] + extra_jobs = [{'name': name} for name in jobs] + + builder_obj = builder.Builder('http://jenkins.example.com', + 'doesnot', 'matter', + plugins_list={}) + + # get the instance created by mock and redirect some of the method + # mocks to call real methods on a the above test object. + b_inst = builder_mock.return_value + b_inst.plugins_list = builder_obj.plugins_list + b_inst.update_job.side_effect = builder_obj.update_job + b_inst.delete_old_managed.side_effect = builder_obj.delete_old_managed + + def _get_jobs(): + return builder_obj.parser.jobs + extra_jobs + get_jobs_mock.side_effect = _get_jobs + + # override cache to ensure Jenkins.update_job called a limited number + # of times + self.cache_mock.return_value.has_changed.side_effect = ( + [True] * 2 + [False] * 2) + + path = os.path.join(self.fixtures_path, 'cmd-002.yaml') + args = self.parser.parse_args(['update', '--delete-old', path]) + + with mock.patch('jenkins_jobs.builder.Jenkins.update_job') as update: + with mock.patch('jenkins_jobs.builder.Jenkins.is_managed', + return_value=True): + cmd.execute(args, self.config) + self.assertEquals(2, update.call_count, + "Expected Jenkins.update_job to be called '%d' " + "times, got '%d' calls instead.\n" + "Called with: %s" % (2, update.call_count, + update.mock_calls)) + + calls = [mock.call(name) for name in jobs] + self.assertEquals(2, delete_job_mock.call_count, + "Expected Jenkins.delete_job to be called '%d' " + "times got '%d' calls instead.\n" + "Called with: %s" % (2, delete_job_mock.call_count, + delete_job_mock.mock_calls)) + delete_job_mock.assert_has_calls(calls, any_order=True) diff --git a/tests/cmd/test_cmd.py b/tests/cmd/test_cmd.py index a2248fe88..04846942b 100644 --- a/tests/cmd/test_cmd.py +++ b/tests/cmd/test_cmd.py @@ -18,17 +18,14 @@ class CmdTestsBase(testtools.TestCase): # are run in parallel. Stub out the CacheStorage to ensure that each # test can safely create the cache directory without risk of # interference. - self.cache_patch = mock.patch('jenkins_jobs.builder.CacheStorage', - autospec=True) - self.cache_patch.start() + cache_patch = mock.patch('jenkins_jobs.builder.CacheStorage', + autospec=True) + self.cache_mock = cache_patch.start() + self.addCleanup(cache_patch.stop) self.config = configparser.ConfigParser() self.config.readfp(StringIO(cmd.DEFAULT_CONF)) - def tearDown(self): - self.cache_patch.stop() - super(CmdTestsBase, self).tearDown() - class CmdTests(CmdTestsBase):