From faacbdcba411a114a118c76dd00136ef81906b97 Mon Sep 17 00:00:00 2001 From: Joe Lee Date: Thu, 31 Dec 2020 13:29:35 +0800 Subject: [PATCH] Add more test cases (#13) --- api4jenkins/build.py | 4 ++-- api4jenkins/node.py | 14 ++++------- api4jenkins/system.py | 5 ++-- api4jenkins/view.py | 7 +++--- tests/unit/conftest.py | 7 ++++++ tests/unit/test_build.py | 9 ++++++- tests/unit/test_jenkins.py | 18 ++++++++++++-- tests/unit/test_job.py | 10 ++++++++ tests/unit/test_system.py | 12 ++++++++++ tests/unit/test_view.py | 31 ++++++++++++++++++++++++- tests/unit/tests_data/job/pipeline.json | 24 ++++++++++++------- tests/unit/tests_data/view/allview.json | 20 ++++++++++++++++ 12 files changed, 132 insertions(+), 29 deletions(-) create mode 100644 tests/unit/test_system.py create mode 100644 tests/unit/tests_data/view/allview.json diff --git a/api4jenkins/build.py b/api4jenkins/build.py index d55921f..1647d6f 100644 --- a/api4jenkins/build.py +++ b/api4jenkins/build.py @@ -36,8 +36,8 @@ def term(self): def kill(self): self.handle_req('POST', 'kill', allow_redirects=False) - def retrigger(self): - pass + # def retrigger(self): + # pass def get_next_build(self): item = self.api_json(tree='nextBuild[url]')['nextBuild'] diff --git a/api4jenkins/node.py b/api4jenkins/node.py index b76a9f9..ffd106e 100644 --- a/api4jenkins/node.py +++ b/api4jenkins/node.py @@ -1,6 +1,7 @@ # encoding: utf-8 import json +import re from .exceptions import ItemNotFoundError from .item import Item @@ -36,10 +37,7 @@ def create(self, name, **kwargs): def get(self, name): for item in self.api_json(tree='computer[displayName]')['computer']: if name == item['displayName']: - if name == 'master': - item['url'] = f'{self.url}(master)/' - else: - item['url'] = f"{self.url}{item['displayName']}/" + item['url'] = f"{self.url}{item['displayName']}/" return self._new_instance_by_item(__name__, item) return None @@ -52,10 +50,7 @@ def iter_builds(self): def __iter__(self): for item in self.api_json(tree='computer[displayName]')['computer']: - if item['displayName'] == 'master': - item['url'] = f'{self.url}(master)/' - else: - item['url'] = f"{self.url}{item['displayName']}/" + item['url'] = f"{self.url}{item['displayName']}/" yield self._new_instance_by_item(__name__, item) @@ -79,7 +74,8 @@ def iter_builds(self): class MasterComputer(Node): - pass + def __init__(self, jenkins, url): + super().__init__(jenkins, re.sub(r'/master/$', '/(master)/', url)) class SlaveComputer(Node): diff --git a/api4jenkins/system.py b/api4jenkins/system.py index 5ab356a..75a0807 100644 --- a/api4jenkins/system.py +++ b/api4jenkins/system.py @@ -17,7 +17,6 @@ def quiet_down(self): def cancel_quiet_down(self): self.handle_req('POST', 'cancelQuietDown', allow_redirects=False) - def show_credential(self): - pass - # TODO add groovy to print credential + # def show_credential(self): + # pass diff --git a/api4jenkins/view.py b/api4jenkins/view.py index 23aa4f8..67e194a 100644 --- a/api4jenkins/view.py +++ b/api4jenkins/view.py @@ -1,7 +1,7 @@ # encoding: utf-8 from .item import Item -from .mix import ConfigrationMix, DeletionMix +from .mix import ConfigrationMix, DeletionMix, DescriptionMix class Views(Item): @@ -31,7 +31,7 @@ def __iter__(self): yield self._new_instance_by_item(__name__, item) -class View(Item, ConfigrationMix, DeletionMix): +class View(Item, ConfigrationMix, DescriptionMix, DeletionMix): def get_job(self, name): for item in self.api_json(tree='jobs[name,url]')['jobs']: @@ -51,7 +51,8 @@ def exclude(self, name): class AllView(View): - pass + def __init__(self, jenkins, url): + super().__init__(jenkins, url + 'view/all/') class MyView(View): diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 1f3d154..3e40de4 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -12,6 +12,7 @@ from api4jenkins import PluginsManager from api4jenkins import Queue from api4jenkins.node import Nodes +from api4jenkins.view import AllView DATA = Path(__file__).with_name('tests_data') @@ -35,6 +36,8 @@ def _api_json(self, tree='', depth=0): return load_json('queue/queue.json') elif isinstance(self, Nodes): return load_json('node/computer.json') + elif isinstance(self, AllView): + return load_json('view/allview.json') raise TypeError(f'unknow item: {type(self)}') @@ -80,6 +83,10 @@ def credential(jenkins): return Credential(jenkins, f'{jenkins.url}credentials/store/system/domain/_/test-user/') +@pytest.fixture(scope='module') +def view(jenkins): + return AllView(jenkins, jenkins.url) + @pytest.fixture def mock_resp(): with responses.RequestsMock() as r: diff --git a/tests/unit/test_build.py b/tests/unit/test_build.py index 779e604..14d0bb5 100644 --- a/tests/unit/test_build.py +++ b/tests/unit/test_build.py @@ -1,5 +1,5 @@ # encoding: utf-8 - +import pytest from api4jenkins.build import WorkflowRun @@ -26,3 +26,10 @@ def test_get_previous_build(self, workflowrun): def test_get_job(self, workflowrun, workflow): assert workflow == workflowrun.get_job() + + @pytest.mark.parametrize('action', ['stop', 'term', 'kill']) + def test_stop_term_kill(self, workflowrun, mock_resp, action): + req_url = f'{workflowrun.url}{action}' + mock_resp.add('POST', req_url) + getattr(workflowrun, action)() + assert mock_resp.calls[0].request.url == req_url diff --git a/tests/unit/test_jenkins.py b/tests/unit/test_jenkins.py index 4b7325f..ac1e54f 100644 --- a/tests/unit/test_jenkins.py +++ b/tests/unit/test_jenkins.py @@ -64,8 +64,22 @@ def test_create_job_succ(self, jenkins, mock_resp): assert mock_resp.calls[0].response.status_code == 200 assert mock_resp.calls[0].request.url == req_url - def test_copy_job(self, jenkins, mock_resp): - pass + @pytest.mark.parametrize('headers', [{'X-Error': "A job already exists " + "with the name 'Level2_Folder1'"}, + {'X-Error': 'No such job: xxxx'}], + ids=['job exist', 'no source job']) + def test_copy_fail(self, jenkins, mock_resp, headers): + mock_resp.add('POST', f'{jenkins.url}createItem', + headers=headers, status=400) + with pytest.raises(BadRequestError): + jenkins.copy_job('not exist', 'Level2_Folder1') + + def test_copy_succ(self, jenkins, mock_resp): + req_url = f'{jenkins.url}createItem?name=new_job&mode=copy&from=src_job' + mock_resp.add('POST', req_url) + jenkins.copy_job('src_job', 'new_job') + assert mock_resp.calls[0].response.status_code == 200 + assert mock_resp.calls[0].request.url == req_url def test_delete_job(self, jenkins, mock_resp): req_url = f'{jenkins.url}job/Level1_Folder1/doDelete' diff --git a/tests/unit/test_job.py b/tests/unit/test_job.py index 100917c..593faa3 100644 --- a/tests/unit/test_job.py +++ b/tests/unit/test_job.py @@ -143,3 +143,13 @@ def test_get_(self, workflow, key): def test_iter_builds(self, workflow): builds = list(workflow.iter_builds()) assert len(builds) == 8 + + @pytest.mark.parametrize('action', ['enable', 'disable']) + def test_enable_disable(self, workflow, mock_resp, action): + req_url = f'{workflow.url}{action}' + mock_resp.add('POST', req_url) + getattr(workflow, action)() + assert mock_resp.calls[0].request.url == req_url + + def test_building(self, workflow): + assert workflow.building is False diff --git a/tests/unit/test_system.py b/tests/unit/test_system.py new file mode 100644 index 0000000..7ac55ab --- /dev/null +++ b/tests/unit/test_system.py @@ -0,0 +1,12 @@ +import pytest +from api4jenkins.item import snake + + +class TestSystem: + + @pytest.mark.parametrize('action', ['restart', 'safeRestart', 'quietDown', 'cancelQuietDown']) + def test_enable_disable(self, jenkins, mock_resp, action): + req_url = f'{jenkins.system.url}{action}' + mock_resp.add('POST', req_url) + getattr(jenkins.system, snake(action))() + assert mock_resp.calls[0].request.url == req_url diff --git a/tests/unit/test_view.py b/tests/unit/test_view.py index 3312575..61f749d 100644 --- a/tests/unit/test_view.py +++ b/tests/unit/test_view.py @@ -1,3 +1,32 @@ # encoding: utf-8 +import pytest +from api4jenkins.view import AllView class TestViews: - pass + + def test_get(self, jenkins): + view = jenkins.views.get('all') + assert isinstance(view, AllView) + assert view.url == jenkins.url + 'view/all/' + assert jenkins.views.get('not exist') is None + + def test_views(self, jenkins): + assert len(list(jenkins.views)) == 1 + + +class TestView: + + def test_get_job(self, view): + job_in_view = view.get_job('Level1_Folder1') + job = view.jenkins.get_job('Level1_Folder1') + assert job_in_view == job + assert view.get_job('not exist') is None + + def test_iter(self, view): + assert len(list(view)) == 2 + + @pytest.mark.parametrize('action, entry', [('include', 'addJobToView'), ('exclude', 'removeJobFromView')]) + def test_include_exclude(self, view, mock_resp, action, entry): + req_url = f'{view.url}{entry}?name=folder1' + mock_resp.add('POST', req_url, json={'name': 'folder1'}) + getattr(view, action)('folder1') + assert mock_resp.calls[0].request.url == req_url diff --git a/tests/unit/tests_data/job/pipeline.json b/tests/unit/tests_data/job/pipeline.json index ed002f9..dbc11aa 100644 --- a/tests/unit/tests_data/job/pipeline.json +++ b/tests/unit/tests_data/job/pipeline.json @@ -36,42 +36,50 @@ { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 52, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/52/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/52/", + "building": false }, { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 51, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/51/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/51/", + "building": false }, { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 50, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/50/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/50/", + "building": false }, { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 49, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/49/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/49/", + "building": false }, { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 48, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/48/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/48/", + "building": false }, { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 47, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/47/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/47/", + "building": false }, { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 46, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/46/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/46/", + "building": false }, { "_class": "org.jenkinsci.plugins.workflow.job.WorkflowRun", "number": 45, - "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/45/" + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob/45/", + "building": false } ], "color": "red", diff --git a/tests/unit/tests_data/view/allview.json b/tests/unit/tests_data/view/allview.json new file mode 100644 index 0000000..813f826 --- /dev/null +++ b/tests/unit/tests_data/view/allview.json @@ -0,0 +1,20 @@ +{ + "_class": "hudson.model.AllView", + "description": null, + "jobs": [ + { + "_class": "org.jenkinsci.plugins.workflow.job.WorkflowJob", + "name": "Level1_WorkflowJob1", + "url": "http://0.0.0.0:8080/job/Level1_WorkflowJob1/", + "color": "red" + }, + { + "_class": "com.cloudbees.hudson.plugins.folder.Folder", + "name": "Level1_Folder1", + "url": "http://0.0.0.0:8080/job/Level1_Folder1/" + } + ], + "name": "all", + "property": [], + "url": "http://http://0.0.0.0:8080/" +} \ No newline at end of file