Adds Cloudbees folder plugin support
This patch adds cloudbees folder plugin support. Folder should be specified in job name : <folder>/<job> This update also extend job_builder (http://ci.openstack.org/jenkins-job-builder/) tool without any modification. Change-Id: I6d3957c217e1253e53152d90d5fcce5e69c77674 Co-Authored-By: Darragh Bailey <daragh.bailey@gmail.com>
This commit is contained in:
parent
f80e54b4bc
commit
7267eec454
@ -23,6 +23,7 @@ the things you can use it for:
|
||||
* Create/delete/reconfig views
|
||||
* Put server in shutdown mode (quiet down)
|
||||
* List running builds
|
||||
* Create/delete/update folders [#f1]_
|
||||
* and many more..
|
||||
|
||||
To install::
|
||||
@ -76,3 +77,11 @@ Then install the required python packages using pip_::
|
||||
.. _flake8: https://pypi.python.org/pypi/flake8
|
||||
.. _tox: https://testrun.org/tox
|
||||
.. _pip: https://pypi.python.org/pypi/pip
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] The free `Cloudbees Folders Plugin
|
||||
<https://wiki.jenkins-ci.org/display/JENKINS/CloudBees+Folders+Plugin>`_
|
||||
provides support for a subset of the full folders functionality. For the
|
||||
complete capabilities you will need the paid for version of the plugin.
|
||||
|
@ -116,3 +116,21 @@ This is an example showing how to retrieve information on the Jenkins queue.
|
||||
queue_info = server.get_queue_info()
|
||||
id = queue_info[0].get('id')
|
||||
server.cancel_queue(id)
|
||||
|
||||
|
||||
Example 7: Working with Jenkins Cloudbees Folders
|
||||
-------------------------------------------------
|
||||
|
||||
Requires the `Cloudbees Folders Plugin
|
||||
<https://wiki.jenkins-ci.org/display/JENKINS/CloudBees+Folders+Plugin>`_ for
|
||||
Jenkins.
|
||||
|
||||
This is an example showing how to create, configure and delete Jenkins folders.
|
||||
|
||||
::
|
||||
|
||||
server.create_job('folder', jenkins.EMPTY_FOLDER_XML)
|
||||
server.create_job('folder/empty', jenkins.EMPTY_FOLDER_XML)
|
||||
server.copy_job('folder/empty', 'folder/empty_copy')
|
||||
server.delete_job('folder/empty_copy')
|
||||
server.delete_job('folder')
|
||||
|
@ -74,22 +74,23 @@ DEFAULT_HEADERS = {'Content-Type': 'text/xml; charset=utf-8'}
|
||||
INFO = 'api/json'
|
||||
PLUGIN_INFO = 'pluginManager/api/json?depth=%(depth)s'
|
||||
CRUMB_URL = 'crumbIssuer/api/json'
|
||||
JOB_INFO = 'job/%(name)s/api/json?depth=%(depth)s'
|
||||
JOB_NAME = 'job/%(name)s/api/json?tree=name'
|
||||
JOBS_QUERY = '?tree=jobs[url,color,name,jobs]'
|
||||
JOB_INFO = '%(folder_url)sjob/%(short_name)s/api/json?depth=%(depth)s'
|
||||
JOB_NAME = '%(folder_url)sjob/%(short_name)s/api/json?tree=name'
|
||||
Q_INFO = 'queue/api/json?depth=0'
|
||||
CANCEL_QUEUE = 'queue/cancelItem?id=%(id)s'
|
||||
CREATE_JOB = 'createItem?name=%(name)s' # also post config.xml
|
||||
CONFIG_JOB = 'job/%(name)s/config.xml'
|
||||
DELETE_JOB = 'job/%(name)s/doDelete'
|
||||
ENABLE_JOB = 'job/%(name)s/enable'
|
||||
DISABLE_JOB = 'job/%(name)s/disable'
|
||||
COPY_JOB = 'createItem?name=%(to_name)s&mode=copy&from=%(from_name)s'
|
||||
RENAME_JOB = 'job/%(from_name)s/doRename?newName=%(to_name)s'
|
||||
BUILD_JOB = 'job/%(name)s/build'
|
||||
STOP_BUILD = 'job/%(name)s/%(number)s/stop'
|
||||
BUILD_WITH_PARAMS_JOB = 'job/%(name)s/buildWithParameters'
|
||||
BUILD_INFO = 'job/%(name)s/%(number)d/api/json?depth=%(depth)s'
|
||||
BUILD_CONSOLE_OUTPUT = 'job/%(name)s/%(number)d/consoleText'
|
||||
CREATE_JOB = '%(folder_url)screateItem?name=%(short_name)s' # also post config.xml
|
||||
CONFIG_JOB = '%(folder_url)sjob/%(short_name)s/config.xml'
|
||||
DELETE_JOB = '%(folder_url)sjob/%(short_name)s/doDelete'
|
||||
ENABLE_JOB = '%(folder_url)sjob/%(short_name)s/enable'
|
||||
DISABLE_JOB = '%(folder_url)sjob/%(short_name)s/disable'
|
||||
COPY_JOB = '%(from_folder_url)screateItem?name=%(to_short_name)s&mode=copy&from=%(from_short_name)s'
|
||||
RENAME_JOB = '%(from_folder_url)sjob/%(from_short_name)s/doRename?newName=%(to_short_name)s'
|
||||
BUILD_JOB = '%(folder_url)sjob/%(short_name)s/build'
|
||||
STOP_BUILD = '%(folder_url)sjob/%(short_name)s/%(number)s/stop'
|
||||
BUILD_WITH_PARAMS_JOB = '%(folder_url)sjob/%(short_name)s/buildWithParameters'
|
||||
BUILD_INFO = '%(folder_url)sjob/%(short_name)s/%(number)d/api/json?depth=%(depth)s'
|
||||
BUILD_CONSOLE_OUTPUT = '%(folder_url)sjob/%(short_name)s/%(number)d/consoleText'
|
||||
NODE_LIST = 'computer/api/json'
|
||||
CREATE_NODE = 'computer/doCreateItem?%s'
|
||||
DELETE_NODE = 'computer/%(name)s/doDelete'
|
||||
@ -224,7 +225,8 @@ class Jenkins(object):
|
||||
|
||||
def _get_encoded_params(self, params):
|
||||
for k, v in params.items():
|
||||
if k in ["name", "to_name", "from_name", "msg"]:
|
||||
if k in ["name", "msg", "short_name", "from_short_name",
|
||||
"to_short_name", "folder_url", "from_folder_url", "to_folder_url"]:
|
||||
params[k] = quote(v)
|
||||
return params
|
||||
|
||||
@ -257,6 +259,7 @@ class Jenkins(object):
|
||||
:param depth: JSON depth, ``int``
|
||||
:returns: dictionary of job information
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
try:
|
||||
response = self.jenkins_open(Request(
|
||||
self._build_url(JOB_INFO, locals())
|
||||
@ -271,16 +274,17 @@ class Jenkins(object):
|
||||
raise JenkinsException(
|
||||
"Could not parse JSON info for job[%s]" % name)
|
||||
|
||||
def get_job_info_regex(self, pattern, depth=0):
|
||||
def get_job_info_regex(self, pattern, depth=0, folder_depth=0):
|
||||
'''Get a list of jobs information that contain names which match the
|
||||
regex pattern.
|
||||
|
||||
:param pattern: regex pattern, ``str``
|
||||
:param depth: JSON depth, ``int``
|
||||
:param folder_depth: folder level depth to search ``int``
|
||||
:returns: List of jobs info, ``list``
|
||||
'''
|
||||
result = []
|
||||
jobs = self.get_jobs()
|
||||
jobs = self.get_all_jobs(folder_depth)
|
||||
for job in jobs:
|
||||
if re.search(pattern, job['name']):
|
||||
result.append(self.get_job_info(job['name'], depth=depth))
|
||||
@ -297,6 +301,7 @@ class Jenkins(object):
|
||||
:param name: Job name, ``str``
|
||||
:returns: Name of job or None
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
try:
|
||||
response = self.jenkins_open(Request(
|
||||
self._build_url(JOB_NAME, locals())
|
||||
@ -305,7 +310,7 @@ class Jenkins(object):
|
||||
return None
|
||||
else:
|
||||
actual = json.loads(response)['name']
|
||||
if actual != name:
|
||||
if actual != short_name:
|
||||
raise JenkinsException(
|
||||
'Jenkins returned an unexpected job name %s '
|
||||
'(expected: %s)' % (actual, name))
|
||||
@ -375,6 +380,7 @@ class Jenkins(object):
|
||||
>>> print(build_info)
|
||||
{u'building': False, u'changeSet': {u'items': [{u'date': u'2011-12-19T18:01:52.540557Z', u'msg': u'test', u'revision': 66, u'user': u'unknown', u'paths': [{u'editType': u'edit', u'file': u'/branches/demo/index.html'}]}], u'kind': u'svn', u'revisions': [{u'module': u'http://eaas-svn01.i3.level3.com/eaas', u'revision': 66}]}, u'builtOn': u'', u'description': None, u'artifacts': [{u'relativePath': u'dist/eaas-87-2011-12-19_18-01-57.war', u'displayPath': u'eaas-87-2011-12-19_18-01-57.war', u'fileName': u'eaas-87-2011-12-19_18-01-57.war'}, {u'relativePath': u'dist/eaas-87-2011-12-19_18-01-57.war.zip', u'displayPath': u'eaas-87-2011-12-19_18-01-57.war.zip', u'fileName': u'eaas-87-2011-12-19_18-01-57.war.zip'}], u'timestamp': 1324317717000, u'number': 87, u'actions': [{u'parameters': [{u'name': u'SERVICE_NAME', u'value': u'eaas'}, {u'name': u'PROJECT_NAME', u'value': u'demo'}]}, {u'causes': [{u'userName': u'anonymous', u'shortDescription': u'Started by user anonymous'}]}, {}, {}, {}], u'id': u'2011-12-19_18-01-57', u'keepLog': False, u'url': u'http://eaas-jenkins01.i3.level3.com:9080/job/build_war/87/', u'culprits': [{u'absoluteUrl': u'http://eaas-jenkins01.i3.level3.com:9080/user/unknown', u'fullName': u'unknown'}], u'result': u'SUCCESS', u'duration': 8826, u'fullDisplayName': u'build_war #87'}
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
try:
|
||||
response = self.jenkins_open(Request(
|
||||
self._build_url(BUILD_INFO, locals())
|
||||
@ -421,12 +427,15 @@ class Jenkins(object):
|
||||
# mechanism, so ignore it
|
||||
pass
|
||||
|
||||
def get_info(self):
|
||||
"""Get information on this Master.
|
||||
def get_info(self, item="", query=None):
|
||||
"""Get information on this Master or item on Master.
|
||||
|
||||
This information includes job list and view information.
|
||||
This information includes job list and view information and can be
|
||||
used to retreive information on items such as job folders.
|
||||
|
||||
:returns: dictionary of information about Master, ``dict``
|
||||
:param item: item to get information about on this Master
|
||||
:param query: xpath to extract information about on this Master
|
||||
:returns: dictionary of information about Master or item, ``dict``
|
||||
|
||||
Example::
|
||||
|
||||
@ -437,9 +446,12 @@ class Jenkins(object):
|
||||
u'name': u'my_job'}
|
||||
|
||||
"""
|
||||
url = "/".join((item, INFO))
|
||||
if query:
|
||||
url += query
|
||||
try:
|
||||
return json.loads(self.jenkins_open(
|
||||
Request(self._build_url(INFO))
|
||||
Request(self._build_url(url))
|
||||
))
|
||||
except (HTTPError, BadStatusLine):
|
||||
raise BadHTTPException("Error communicating with server[%s]"
|
||||
@ -549,21 +561,106 @@ class Jenkins(object):
|
||||
raise JenkinsException("Could not parse JSON info for server[%s]"
|
||||
% self.server)
|
||||
|
||||
def get_jobs(self):
|
||||
"""Get list of jobs running.
|
||||
def get_jobs(self, folder_depth=0):
|
||||
"""Get list of jobs.
|
||||
|
||||
Each job is a dictionary with 'name', 'url', and 'color' keys.
|
||||
Each job is a dictionary with 'name', 'url', 'color' and 'fullname'
|
||||
keys.
|
||||
|
||||
:param folder_depth: Number of levels to search, ``int``. By default
|
||||
0, which will limit search to toplevel. None disables the limit.
|
||||
:returns: list of jobs, ``[ { str: str} ]``
|
||||
"""
|
||||
return self.get_info()['jobs']
|
||||
|
||||
return self.get_all_jobs(folder_depth=folder_depth)
|
||||
|
||||
def get_all_jobs(self, folder_depth=None):
|
||||
"""Get list of all jobs recursively to the given folder depth.
|
||||
|
||||
Each job is a dictionary with 'name', 'url', 'color' and 'fullname'
|
||||
keys.
|
||||
|
||||
:param folder_depth: Number of levels to search, ``int``. By default
|
||||
None, which will search all levels. 0 limits to toplevel.
|
||||
:returns: list of jobs, ``[ { str: str} ]``
|
||||
|
||||
.. note::
|
||||
|
||||
On instances with many folders it may be more efficient to use the
|
||||
run_script method to retrieve all jobs instead.
|
||||
|
||||
Example::
|
||||
|
||||
server.run_script(\"\"\"
|
||||
import groovy.json.JsonBuilder;
|
||||
|
||||
// get all projects excluding matrix configuration
|
||||
// as they are simply part of a matrix project.
|
||||
// there may be better ways to get just jobs
|
||||
items = Jenkins.instance.getAllItems(AbstractProject);
|
||||
items.removeAll {
|
||||
it instanceof hudson.matrix.MatrixConfiguration
|
||||
};
|
||||
|
||||
def json = new JsonBuilder()
|
||||
def root = json {
|
||||
jobs items.collect {
|
||||
[
|
||||
name: it.name,
|
||||
url: Jenkins.instance.getRootUrl() + it.getUrl(),
|
||||
color: it.getIconColor().toString(),
|
||||
fullname: it.getFullName()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// use json.toPrettyString() if viewing
|
||||
println json.toString()
|
||||
\"\"\")
|
||||
|
||||
"""
|
||||
jobs_list = []
|
||||
|
||||
jobs = [(0, "", self.get_info(query=JOBS_QUERY)['jobs'])]
|
||||
for lvl, root, lvl_jobs in jobs:
|
||||
if not isinstance(lvl_jobs, list):
|
||||
lvl_jobs = [lvl_jobs]
|
||||
for job in lvl_jobs:
|
||||
if 'jobs' in job: # folder
|
||||
if folder_depth is None or lvl < folder_depth:
|
||||
path = '/job/'.join((root, job[u'name']))
|
||||
jobs.append(
|
||||
(lvl + 1, path,
|
||||
self.get_info(path,
|
||||
query=JOBS_QUERY)['jobs']))
|
||||
else:
|
||||
# insert fullname info if it doesn't exist to
|
||||
# allow callers to easily reference unambiguously
|
||||
if u'fullname' not in job:
|
||||
job[u'fullname'] = '/'.join(
|
||||
[p for p in root.split('/')
|
||||
if p and p != 'job'] +
|
||||
[job[u'name']])
|
||||
jobs_list.append(job)
|
||||
return jobs_list
|
||||
|
||||
def copy_job(self, from_name, to_name):
|
||||
'''Copy a Jenkins job
|
||||
'''Copy a Jenkins job.
|
||||
|
||||
Will raise an exception whenever the source and destination folder
|
||||
for this jobs won't be the same.
|
||||
|
||||
:param from_name: Name of Jenkins job to copy from, ``str``
|
||||
:param to_name: Name of Jenkins job to copy to, ``str``
|
||||
:throws: :class:`JenkinsException` whenever the source and destination
|
||||
folder are not the same
|
||||
'''
|
||||
from_folder_url, from_short_name = self._get_job_folder(from_name)
|
||||
to_folder_url, to_short_name = self._get_job_folder(to_name)
|
||||
if from_folder_url != to_folder_url:
|
||||
raise JenkinsException('copy[%s to %s] failed, source and destination '
|
||||
'folder must be the same' % (from_name, to_name))
|
||||
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(COPY_JOB, locals()), b''))
|
||||
self.assert_job_exists(to_name, 'create[%s] failed')
|
||||
@ -571,9 +668,19 @@ class Jenkins(object):
|
||||
def rename_job(self, from_name, to_name):
|
||||
'''Rename an existing Jenkins job
|
||||
|
||||
Will raise an exception whenever the source and destination folder
|
||||
for this jobs won't be the same.
|
||||
|
||||
:param from_name: Name of Jenkins job to rename, ``str``
|
||||
:param to_name: New Jenkins job name, ``str``
|
||||
:throws: :class:`JenkinsException` whenever the source and destination
|
||||
folder are not the same
|
||||
'''
|
||||
from_folder_url, from_short_name = self._get_job_folder(from_name)
|
||||
to_folder_url, to_short_name = self._get_job_folder(to_name)
|
||||
if from_folder_url != to_folder_url:
|
||||
raise JenkinsException('rename[%s to %s] failed, source and destination folder '
|
||||
'must be the same' % (from_name, to_name))
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(RENAME_JOB, locals()), b''))
|
||||
self.assert_job_exists(to_name, 'rename[%s] failed')
|
||||
@ -583,6 +690,7 @@ class Jenkins(object):
|
||||
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(DELETE_JOB, locals()), b''))
|
||||
if self.job_exists(name):
|
||||
@ -593,6 +701,7 @@ class Jenkins(object):
|
||||
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(ENABLE_JOB, locals()), b''))
|
||||
|
||||
@ -603,6 +712,7 @@ class Jenkins(object):
|
||||
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(DISABLE_JOB, locals()), b''))
|
||||
|
||||
@ -612,15 +722,32 @@ class Jenkins(object):
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
:returns: ``True`` if Jenkins job exists
|
||||
'''
|
||||
if self.get_job_name(name) == name:
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
if self.get_job_name(name) == short_name:
|
||||
return True
|
||||
|
||||
def jobs_count(self):
|
||||
'''Get the number of jobs on the Jenkins server
|
||||
|
||||
:returns: Total number of jobs, ``int``
|
||||
|
||||
.. note::
|
||||
|
||||
On instances with many folders it may be more efficient to use the
|
||||
run_script method to retrieve the total number of jobs instead.
|
||||
|
||||
Example::
|
||||
|
||||
# get all projects excluding matrix configuration
|
||||
# as they are simply part of a matrix project.
|
||||
server.run_script(
|
||||
"print(Hudson.instance.getAllItems("
|
||||
" hudson.model.AbstractProject).count{"
|
||||
" !(it instanceof hudson.matrix.MatrixConfiguration)"
|
||||
" })")
|
||||
|
||||
'''
|
||||
return len(self.get_jobs())
|
||||
return len(self.get_all_jobs())
|
||||
|
||||
def assert_job_exists(self, name,
|
||||
exception_message='job[%s] does not exist'):
|
||||
@ -640,12 +767,17 @@ class Jenkins(object):
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
:param config_xml: config file text, ``str``
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
if self.job_exists(name):
|
||||
raise JenkinsException('job[%s] already exists' % (name))
|
||||
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(CREATE_JOB, locals()),
|
||||
config_xml.encode('utf-8'), DEFAULT_HEADERS))
|
||||
try:
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(CREATE_JOB, locals()),
|
||||
config_xml.encode('utf-8'), DEFAULT_HEADERS))
|
||||
except NotFoundException:
|
||||
raise JenkinsException('Cannot create job[%s] because folder '
|
||||
'for the job does not exist' % (name))
|
||||
self.assert_job_exists(name, 'create[%s] failed')
|
||||
|
||||
def get_job_config(self, name):
|
||||
@ -654,6 +786,7 @@ class Jenkins(object):
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
:returns: job configuration (XML format)
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
request = Request(self._build_url(CONFIG_JOB, locals()))
|
||||
return self.jenkins_open(request)
|
||||
|
||||
@ -665,6 +798,7 @@ class Jenkins(object):
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
:param config_xml: New XML configuration, ``str``
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
reconfig_url = self._build_url(CONFIG_JOB, locals())
|
||||
self.jenkins_open(Request(reconfig_url, config_xml.encode('utf-8'),
|
||||
DEFAULT_HEADERS))
|
||||
@ -679,6 +813,7 @@ class Jenkins(object):
|
||||
:param token: (optional) token for building job, ``str``
|
||||
:returns: URL for building job
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
if parameters:
|
||||
if token:
|
||||
parameters['token'] = token
|
||||
@ -723,6 +858,7 @@ class Jenkins(object):
|
||||
:param name: Name of Jenkins job, ``str``
|
||||
:param number: Jenkins build number for the job, ``int``
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
self.jenkins_open(Request(
|
||||
self._build_url(STOP_BUILD, locals()), b''))
|
||||
|
||||
@ -937,6 +1073,7 @@ class Jenkins(object):
|
||||
:param name: Build number, ``int``
|
||||
:returns: Build console output, ``str``
|
||||
'''
|
||||
folder_url, short_name = self._get_job_folder(name)
|
||||
try:
|
||||
response = self.jenkins_open(Request(
|
||||
self._build_url(BUILD_CONSOLE_OUTPUT, locals())
|
||||
@ -950,6 +1087,24 @@ class Jenkins(object):
|
||||
raise JenkinsException('job[%s] number[%d] does not exist'
|
||||
% (name, number))
|
||||
|
||||
def _get_job_folder(self, name):
|
||||
'''Return the name and folder (see cloudbees plugin).
|
||||
|
||||
This is a method to support cloudbees folder plugin.
|
||||
Url request should take into account folder path when the job name specify it
|
||||
(ex.: 'folder/job')
|
||||
|
||||
:param name: Job name, ``str``
|
||||
:returns: Tuple [ 'folder path for Request', 'Name of job without folder path' ]
|
||||
'''
|
||||
|
||||
a_path = name.split('/')
|
||||
short_name = a_path[-1]
|
||||
folder_url = (('job/' + '/job/'.join(a_path[:-1]) + '/')
|
||||
if len(a_path) > 1 else '')
|
||||
|
||||
return folder_url, short_name
|
||||
|
||||
def get_view_name(self, name):
|
||||
'''Return the name of a view using the API.
|
||||
|
||||
|
@ -4,6 +4,6 @@ hacking>=0.5.6,<0.11
|
||||
mock<1.1
|
||||
unittest2
|
||||
python-subunit
|
||||
sphinx>=1.1.2,<1.2
|
||||
sphinx>=1.2,<1.3.0
|
||||
testrepository
|
||||
testtools
|
||||
|
@ -1,3 +1,6 @@
|
||||
import copy
|
||||
import json
|
||||
|
||||
from tests.base import JenkinsTestBase
|
||||
|
||||
|
||||
@ -8,3 +11,49 @@ class JenkinsJobsTestBase(JenkinsTestBase):
|
||||
<actions/>
|
||||
<description>Foo</description>
|
||||
</matrix-project>"""
|
||||
|
||||
|
||||
class JenkinsGetJobsTestBase(JenkinsJobsTestBase):
|
||||
|
||||
jobs_in_folder = [
|
||||
[
|
||||
{'name': 'my_job1'},
|
||||
{'name': 'my_folder1', 'jobs': None},
|
||||
{'name': 'my_job2'}
|
||||
],
|
||||
# my_folder1 jobs
|
||||
[
|
||||
{'name': 'my_job3'},
|
||||
{'name': 'my_job4'}
|
||||
]
|
||||
]
|
||||
|
||||
jobs_in_multiple_folders = copy.deepcopy(jobs_in_folder)
|
||||
jobs_in_multiple_folders[1].insert(
|
||||
0, {'name': 'my_folder2', 'jobs': None})
|
||||
jobs_in_multiple_folders.append(
|
||||
# my_folder1/my_folder2 jobs
|
||||
[
|
||||
{'name': 'my_job1'},
|
||||
{'name': 'my_job2'}
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def build_jobs_list_responses(jobs_list, server_url):
|
||||
responses = []
|
||||
for jobs in jobs_list:
|
||||
get_jobs_response = []
|
||||
for job in jobs:
|
||||
job_json = {
|
||||
u'url': u'%s/job/%s' % (server_url.rstrip('/'), job['name']),
|
||||
u'name': job['name'],
|
||||
u'color': u'blue'
|
||||
}
|
||||
if 'jobs' in job:
|
||||
job_json[u'jobs'] = "null"
|
||||
get_jobs_response.append(job_json)
|
||||
|
||||
responses.append(json.dumps({u'jobs': get_jobs_response}))
|
||||
|
||||
return responses
|
||||
|
@ -18,6 +18,17 @@ class JenkinsAssertJobExistsTest(JenkinsJobsTestBase):
|
||||
'job[NonExistent] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_job_missing_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = jenkins.NotFoundException()
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.assert_job_exists('a Folder/NonExistent')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'job[a Folder/NonExistent] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_job_exists(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
@ -25,3 +36,11 @@ class JenkinsAssertJobExistsTest(JenkinsJobsTestBase):
|
||||
]
|
||||
self.j.assert_job_exists('ExistingJob')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_job_exists_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'ExistingJob'}),
|
||||
]
|
||||
self.j.assert_job_exists('a Folder/ExistingJob')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -19,6 +19,19 @@ class JenkinsBuildJobTest(JenkinsJobsTestBase):
|
||||
self.assertEqual(build_info, {'foo': 'bar'})
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
{'foo': 'bar'},
|
||||
]
|
||||
|
||||
build_info = self.j.build_job(u'a Folder/Test Job')
|
||||
|
||||
self.assertEqual(jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/build')
|
||||
self.assertEqual(build_info, {'foo': 'bar'})
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_with_token(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
@ -32,6 +45,19 @@ class JenkinsBuildJobTest(JenkinsJobsTestBase):
|
||||
self.assertEqual(build_info, {'foo': 'bar'})
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_with_token(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
{'foo': 'bar'},
|
||||
]
|
||||
|
||||
build_info = self.j.build_job(u'a Folder/TestJob', token='some_token')
|
||||
|
||||
self.assertEqual(jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/TestJob/build?token=some_token')
|
||||
self.assertEqual(build_info, {'foo': 'bar'})
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_with_parameters_and_token(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
|
@ -14,3 +14,12 @@ class JenkinsGetJobConfigTest(JenkinsJobsTestBase):
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/Test%20Job/config.xml')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_encodes_job_name_in_folder(self, jenkins_mock):
|
||||
self.j.get_job_config(u'a folder/Test Job')
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20folder/job/Test%20Job/config.xml')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -24,6 +24,23 @@ class JenkinsCopyJobTest(JenkinsJobsTestBase):
|
||||
self.assertTrue(self.j.job_exists('Test Job_2'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'Test Job_2'}),
|
||||
json.dumps({'name': 'Test Job_2'}),
|
||||
json.dumps({'name': 'Test Job_2'}),
|
||||
]
|
||||
|
||||
self.j.copy_job(u'a Folder/Test Job', u'a Folder/Test Job_2')
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/createItem'
|
||||
'?name=Test%20Job_2&mode=copy&from=Test%20Job')
|
||||
self.assertTrue(self.j.job_exists('a Folder/Test Job_2'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
@ -41,3 +58,35 @@ class JenkinsCopyJobTest(JenkinsJobsTestBase):
|
||||
str(context_manager.exception),
|
||||
'create[TestJob_2] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
None,
|
||||
jenkins.NotFoundException(),
|
||||
]
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.copy_job(u'a Folder/TestJob', u'a Folder/TestJob_2')
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/createItem'
|
||||
'?name=TestJob_2&mode=copy&from=TestJob')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'create[a Folder/TestJob_2] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_another_folder_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
jenkins.JenkinsException()
|
||||
]
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.copy_job(u'a Folder/TestJob', u'another Folder/TestJob_2')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
('copy[a Folder/TestJob to another Folder/TestJob_2] failed, '
|
||||
'source and destination folder must be the same'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -22,6 +22,21 @@ class JenkinsCreateJobTest(JenkinsJobsTestBase):
|
||||
'http://example.com/createItem?name=Test%20Job')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
jenkins.NotFoundException(),
|
||||
None,
|
||||
json.dumps({'name': 'Test Job'}),
|
||||
]
|
||||
|
||||
self.j.create_job(u'a Folder/Test Job', self.config_xml)
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[1][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/createItem?name=Test%20Job')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_already_exists(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
@ -39,6 +54,23 @@ class JenkinsCreateJobTest(JenkinsJobsTestBase):
|
||||
'job[TestJob] already exists')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_already_exists_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'TestJob'}),
|
||||
None,
|
||||
]
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.create_job(u'a Folder/TestJob', self.config_xml)
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/TestJob/api/json?tree=name')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'job[a Folder/TestJob] already exists')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
@ -59,3 +91,24 @@ class JenkinsCreateJobTest(JenkinsJobsTestBase):
|
||||
str(context_manager.exception),
|
||||
'create[TestJob] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_failed_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
jenkins.NotFoundException(),
|
||||
None,
|
||||
jenkins.NotFoundException(),
|
||||
]
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.create_job(u'a Folder/TestJob', self.config_xml)
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/TestJob/api/json?tree=name')
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[1][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/createItem?name=TestJob')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'create[a Folder/TestJob] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -23,3 +23,20 @@ class JenkinsDebugJobInfoTest(JenkinsJobsTestBase):
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/Test%20Job/api/json?depth=0')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
job_info_to_return = {
|
||||
u'building': False,
|
||||
u'msg': u'test',
|
||||
u'revision': 66,
|
||||
u'user': u'unknown'
|
||||
}
|
||||
jenkins_mock.return_value = json.dumps(job_info_to_return)
|
||||
|
||||
self.j.debug_job_info(u'a Folder/Test Job')
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/api/json?depth=0')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -21,6 +21,20 @@ class JenkinsDeleteJobTest(JenkinsJobsTestBase):
|
||||
'http://example.com/job/Test%20Job/doDelete')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
None,
|
||||
jenkins.NotFoundException(),
|
||||
]
|
||||
|
||||
self.j.delete_job(u'a Folder/Test Job')
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/Test%20Job/doDelete')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
@ -38,3 +52,21 @@ class JenkinsDeleteJobTest(JenkinsJobsTestBase):
|
||||
str(context_manager.exception),
|
||||
'delete[TestJob] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'TestJob'}),
|
||||
json.dumps({'name': 'TestJob'}),
|
||||
json.dumps({'name': 'TestJob'}),
|
||||
]
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.delete_job(u'a Folder/TestJob')
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/TestJob/doDelete')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'delete[a Folder/TestJob] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -21,3 +21,18 @@ class JenkinsDisableJobTest(JenkinsJobsTestBase):
|
||||
'http://example.com/job/Test%20Job/disable')
|
||||
self.assertTrue(self.j.job_exists('Test Job'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'Test Job'}),
|
||||
json.dumps({'name': 'Test Job'}),
|
||||
]
|
||||
|
||||
self.j.disable_job(u'a Folder/Test Job')
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/Test%20Job/disable')
|
||||
self.assertTrue(self.j.job_exists('a Folder/Test Job'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -21,3 +21,18 @@ class JenkinsEnableJobTest(JenkinsJobsTestBase):
|
||||
'http://example.com/job/TestJob/enable')
|
||||
self.assertTrue(self.j.job_exists('TestJob'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'TestJob'}),
|
||||
json.dumps({'name': 'TestJob'}),
|
||||
]
|
||||
|
||||
self.j.enable_job(u'a Folder/TestJob')
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/TestJob/enable')
|
||||
self.assertTrue(self.j.job_exists('a Folder/TestJob'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -2,10 +2,11 @@ import json
|
||||
from mock import patch
|
||||
|
||||
import jenkins
|
||||
from tests.jobs.base import JenkinsJobsTestBase
|
||||
from tests.jobs.base import build_jobs_list_responses
|
||||
from tests.jobs.base import JenkinsGetJobsTestBase
|
||||
|
||||
|
||||
class JenkinsGetJobsTest(JenkinsJobsTestBase):
|
||||
class JenkinsGetJobsTest(JenkinsGetJobsTestBase):
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_simple(self, jenkins_mock):
|
||||
@ -19,8 +20,40 @@ class JenkinsGetJobsTest(JenkinsJobsTestBase):
|
||||
|
||||
job_info = self.j.get_jobs()
|
||||
|
||||
self.assertEqual(job_info, jobs)
|
||||
jobs[u'fullname'] = jobs[u'name']
|
||||
self.assertEqual(job_info, [jobs])
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/api/json')
|
||||
u'http://example.com/api/json?tree=jobs[url,color,name,jobs]')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_folders_simple(self, jenkins_mock):
|
||||
response = build_jobs_list_responses(
|
||||
self.jobs_in_folder, 'http://example.com/')
|
||||
jenkins_mock.side_effect = iter(response)
|
||||
|
||||
jobs_info = self.j.get_jobs()
|
||||
|
||||
expected_fullnames = [
|
||||
u"my_job1", u"my_job2"
|
||||
]
|
||||
self.assertEqual(len(expected_fullnames), len(jobs_info))
|
||||
got_fullnames = [job[u"fullname"] for job in jobs_info]
|
||||
self.assertEqual(expected_fullnames, got_fullnames)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_folders_additional_level(self, jenkins_mock):
|
||||
response = build_jobs_list_responses(
|
||||
self.jobs_in_folder, 'http://example.com/')
|
||||
jenkins_mock.side_effect = iter(response)
|
||||
|
||||
jobs_info = self.j.get_jobs(folder_depth=1)
|
||||
|
||||
expected_fullnames = [
|
||||
u"my_job1", u"my_job2",
|
||||
u"my_folder1/my_job3", u"my_folder1/my_job4"
|
||||
]
|
||||
self.assertEqual(len(expected_fullnames), len(jobs_info))
|
||||
got_fullnames = [job[u"fullname"] for job in jobs_info]
|
||||
self.assertEqual(expected_fullnames, got_fullnames)
|
||||
|
61
tests/jobs/test_getall.py
Normal file
61
tests/jobs/test_getall.py
Normal file
@ -0,0 +1,61 @@
|
||||
from mock import patch
|
||||
|
||||
import jenkins
|
||||
from tests.jobs.base import build_jobs_list_responses
|
||||
from tests.jobs.base import JenkinsGetJobsTestBase
|
||||
|
||||
|
||||
class JenkinsGetAllJobsTest(JenkinsGetJobsTestBase):
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_simple(self, jenkins_mock):
|
||||
response = build_jobs_list_responses(
|
||||
self.jobs_in_folder, 'http://example.com/')
|
||||
jenkins_mock.side_effect = iter(response)
|
||||
|
||||
jobs_info = self.j.get_all_jobs()
|
||||
|
||||
expected_fullnames = [
|
||||
u"my_job1", u"my_job2",
|
||||
u"my_folder1/my_job3", u"my_folder1/my_job4"
|
||||
]
|
||||
self.assertEqual(len(expected_fullnames), len(jobs_info))
|
||||
got_fullnames = [job[u"fullname"] for job in jobs_info]
|
||||
self.assertEqual(expected_fullnames, got_fullnames)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_multi_level(self, jenkins_mock):
|
||||
response = build_jobs_list_responses(
|
||||
self.jobs_in_multiple_folders, 'http://example.com/')
|
||||
jenkins_mock.side_effect = iter(response)
|
||||
|
||||
jobs_info = self.j.get_all_jobs()
|
||||
|
||||
expected_fullnames = [
|
||||
u"my_job1", u"my_job2",
|
||||
u"my_folder1/my_job3", u"my_folder1/my_job4",
|
||||
u"my_folder1/my_folder2/my_job1", u"my_folder1/my_folder2/my_job2"
|
||||
]
|
||||
self.assertEqual(len(expected_fullnames), len(jobs_info))
|
||||
got_fullnames = [job[u"fullname"] for job in jobs_info]
|
||||
self.assertEqual(expected_fullnames, got_fullnames)
|
||||
# multiple jobs with same name
|
||||
self.assertEqual(2, len([True
|
||||
for job in jobs_info
|
||||
if job['name'] == u"my_job1"]))
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_folders_depth(self, jenkins_mock):
|
||||
response = build_jobs_list_responses(
|
||||
self.jobs_in_multiple_folders, 'http://example.com/')
|
||||
jenkins_mock.side_effect = iter(response)
|
||||
|
||||
jobs_info = self.j.get_all_jobs(folder_depth=1)
|
||||
|
||||
expected_fullnames = [
|
||||
u"my_job1", u"my_job2",
|
||||
u"my_folder1/my_job3", u"my_folder1/my_job4"
|
||||
]
|
||||
self.assertEqual(len(expected_fullnames), len(jobs_info))
|
||||
got_fullnames = [job[u"fullname"] for job in jobs_info]
|
||||
self.assertEqual(expected_fullnames, got_fullnames)
|
@ -25,6 +25,24 @@ class JenkinsGetJobInfoTest(JenkinsJobsTestBase):
|
||||
u'http://example.com/job/Test%20Job/api/json?depth=0')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
job_info_to_return = {
|
||||
u'building': False,
|
||||
u'msg': u'test',
|
||||
u'revision': 66,
|
||||
u'user': u'unknown'
|
||||
}
|
||||
jenkins_mock.return_value = json.dumps(job_info_to_return)
|
||||
|
||||
job_info = self.j.get_job_info(u'a Folder/Test Job')
|
||||
|
||||
self.assertEqual(job_info, job_info_to_return)
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/api/json?depth=0')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_regex(self, jenkins_mock):
|
||||
jobs = [
|
||||
@ -91,3 +109,22 @@ class JenkinsGetJobInfoTest(JenkinsJobsTestBase):
|
||||
str(context_manager.exception),
|
||||
'job[TestJob] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_raise_HTTPError(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = jenkins.HTTPError(
|
||||
'http://example.com/job/a%20Folder/job/TestJob/api/json?depth=0',
|
||||
code=401,
|
||||
msg="basic auth failed",
|
||||
hdrs=[],
|
||||
fp=None)
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.get_job_info(u'a Folder/TestJob')
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/TestJob/api/json?depth=0')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'job[a Folder/TestJob] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -20,6 +20,19 @@ class JenkinsGetJobNameTest(JenkinsJobsTestBase):
|
||||
u'http://example.com/job/Test%20Job/api/json?tree=name')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
job_name_to_return = {u'name': 'Test Job'}
|
||||
jenkins_mock.return_value = json.dumps(job_name_to_return)
|
||||
|
||||
job_name = self.j.get_job_name(u'a Folder/Test Job')
|
||||
|
||||
self.assertEqual(job_name, 'Test Job')
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/api/json?tree=name')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_return_none(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = jenkins.NotFoundException()
|
||||
@ -32,6 +45,18 @@ class JenkinsGetJobNameTest(JenkinsJobsTestBase):
|
||||
u'http://example.com/job/TestJob/api/json?tree=name')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_return_none(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = jenkins.NotFoundException()
|
||||
|
||||
job_name = self.j.get_job_name(u'a Folder/TestJob')
|
||||
|
||||
self.assertEqual(job_name, None)
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/TestJob/api/json?tree=name')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_unexpected_job_name(self, jenkins_mock):
|
||||
job_name_to_return = {u'name': 'not the right name'}
|
||||
@ -47,3 +72,19 @@ class JenkinsGetJobNameTest(JenkinsJobsTestBase):
|
||||
'Jenkins returned an unexpected job name {0} '
|
||||
'(expected: {1})'.format(job_name_to_return['name'], 'TestJob'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_unexpected_job_name(self, jenkins_mock):
|
||||
job_name_to_return = {u'name': 'not the right name'}
|
||||
jenkins_mock.return_value = json.dumps(job_name_to_return)
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.get_job_name(u'a Folder/TestJob')
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/TestJob/api/json?tree=name')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'Jenkins returned an unexpected job name {0} (expected: '
|
||||
'{1})'.format(job_name_to_return['name'], 'a Folder/TestJob'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -19,3 +19,16 @@ class JenkinsReconfigJobTest(JenkinsJobsTestBase):
|
||||
self.assertEqual(jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/Test%20Job/config.xml')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'Test Job'}),
|
||||
None,
|
||||
]
|
||||
|
||||
self.j.reconfig_job(u'a Folder/Test Job', self.config_xml)
|
||||
|
||||
self.assertEqual(jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/config.xml')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -23,6 +23,22 @@ class JenkinsRenameJobTest(JenkinsJobsTestBase):
|
||||
self.assertTrue(self.j.job_exists('Test Job_2'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
json.dumps({'name': 'Test Job_2'}),
|
||||
json.dumps({'name': 'Test Job_2'}),
|
||||
json.dumps({'name': 'Test Job_2'}),
|
||||
]
|
||||
|
||||
self.j.rename_job(u'a Folder/Test Job', u'a Folder/Test Job_2')
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/Test%20Job/doRename?newName=Test%20Job_2')
|
||||
self.assertTrue(self.j.job_exists('Test Job_2'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
@ -39,3 +55,34 @@ class JenkinsRenameJobTest(JenkinsJobsTestBase):
|
||||
str(context_manager.exception),
|
||||
'rename[TestJob_2] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
None,
|
||||
jenkins.NotFoundException(),
|
||||
]
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.rename_job(u'a Folder/TestJob', u'a Folder/TestJob_2')
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args_list[0][0][0].get_full_url(),
|
||||
'http://example.com/job/a%20Folder/job/TestJob/doRename?newName=TestJob_2')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'rename[a Folder/TestJob_2] failed')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_another_folder_failed(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = [
|
||||
jenkins.JenkinsException()
|
||||
]
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.rename_job(u'a Folder/TestJob', u'another Folder/TestJob_2')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
('rename[a Folder/TestJob to another Folder/TestJob_2] failed, '
|
||||
'source and destination folder must be the same'))
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
@ -19,6 +19,18 @@ class JenkinsBuildConsoleTest(JenkinsTestBase):
|
||||
u'http://example.com/job/Test%20Job/52/consoleText')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
jenkins_mock.return_value = "build console output..."
|
||||
|
||||
build_info = self.j.get_build_console_output(u'a Folder/Test Job', number=52)
|
||||
|
||||
self.assertEqual(build_info, jenkins_mock.return_value)
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/52/consoleText')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_return_none(self, jenkins_mock):
|
||||
jenkins_mock.return_value = None
|
||||
@ -30,6 +42,17 @@ class JenkinsBuildConsoleTest(JenkinsTestBase):
|
||||
'job[TestJob] number[52] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_return_none(self, jenkins_mock):
|
||||
jenkins_mock.return_value = None
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.get_build_console_output(u'A Folder/TestJob', number=52)
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'job[A Folder/TestJob] number[52] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_return_invalid_json(self, jenkins_mock):
|
||||
jenkins_mock.return_value = 'Invalid JSON'
|
||||
@ -57,6 +80,25 @@ class JenkinsBuildConsoleTest(JenkinsTestBase):
|
||||
'job[TestJob] number[52] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_raise_HTTPError(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = jenkins.HTTPError(
|
||||
'http://example.com/job/a%20Folder/job/TestJob/52/consoleText',
|
||||
code=401,
|
||||
msg="basic auth failed",
|
||||
hdrs=[],
|
||||
fp=None)
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.get_build_console_output(u'a Folder/TestJob', number=52)
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/TestJob/52/consoleText')
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'job[a Folder/TestJob] number[52] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
|
||||
class JenkinsBuildInfoTest(JenkinsTestBase):
|
||||
|
||||
@ -78,6 +120,24 @@ class JenkinsBuildInfoTest(JenkinsTestBase):
|
||||
u'http://example.com/job/Test%20Job/52/api/json?depth=0')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
build_info_to_return = {
|
||||
u'building': False,
|
||||
u'msg': u'test',
|
||||
u'revision': 66,
|
||||
u'user': u'unknown'
|
||||
}
|
||||
jenkins_mock.return_value = json.dumps(build_info_to_return)
|
||||
|
||||
build_info = self.j.get_build_info(u'a Folder/Test Job', number=52)
|
||||
|
||||
self.assertEqual(build_info, build_info_to_return)
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/52/api/json?depth=0')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_return_none(self, jenkins_mock):
|
||||
jenkins_mock.return_value = None
|
||||
@ -116,6 +176,22 @@ class JenkinsBuildInfoTest(JenkinsTestBase):
|
||||
'job[TestJob] number[52] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder_raise_HTTPError(self, jenkins_mock):
|
||||
jenkins_mock.side_effect = jenkins.HTTPError(
|
||||
'http://example.com/job/a%20Folder/job/TestJob/api/json?depth=0',
|
||||
code=401,
|
||||
msg="basic auth failed",
|
||||
hdrs=[],
|
||||
fp=None)
|
||||
|
||||
with self.assertRaises(jenkins.JenkinsException) as context_manager:
|
||||
self.j.get_build_info(u'a Folder/TestJob', number=52)
|
||||
self.assertEqual(
|
||||
str(context_manager.exception),
|
||||
'job[a Folder/TestJob] number[52] does not exist')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
|
||||
class JenkinsStopBuildTest(JenkinsTestBase):
|
||||
|
||||
@ -128,6 +204,16 @@ class JenkinsStopBuildTest(JenkinsTestBase):
|
||||
u'http://example.com/job/Test%20Job/52/stop')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_in_folder(self, jenkins_mock):
|
||||
|
||||
self.j.stop_build(u'a Folder/Test Job', number=52)
|
||||
|
||||
self.assertEqual(
|
||||
jenkins_mock.call_args[0][0].get_full_url(),
|
||||
u'http://example.com/job/a%20Folder/job/Test%20Job/52/stop')
|
||||
self._check_requests(jenkins_mock.call_args_list)
|
||||
|
||||
|
||||
class JenkinsListRunningBuildsTest(JenkinsTestBase):
|
||||
@patch.object(jenkins.Jenkins, 'get_node_info')
|
||||
|
25
tests/test_job_folder.py
Normal file
25
tests/test_job_folder.py
Normal file
@ -0,0 +1,25 @@
|
||||
from mock import patch
|
||||
|
||||
import jenkins
|
||||
from tests.base import JenkinsTestBase
|
||||
|
||||
|
||||
class JenkinsGetJobFolderTest(JenkinsTestBase):
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_simple(self, jenkins_mock):
|
||||
folder, name = self.j._get_job_folder('my job')
|
||||
self.assertEqual(folder, '')
|
||||
self.assertEqual(name, 'my job')
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_single_level(self, jenkins_mock):
|
||||
folder, name = self.j._get_job_folder('my folder/my job')
|
||||
self.assertEqual(folder, 'job/my folder/')
|
||||
self.assertEqual(name, 'my job')
|
||||
|
||||
@patch.object(jenkins.Jenkins, 'jenkins_open')
|
||||
def test_multi_level(self, jenkins_mock):
|
||||
folder, name = self.j._get_job_folder('folder1/folder2/my job')
|
||||
self.assertEqual(folder, 'job/folder1/job/folder2/')
|
||||
self.assertEqual(name, 'my job')
|
Loading…
Reference in New Issue
Block a user