@ -18,295 +18,294 @@
# of actions by the JJB library, usually through interaction with the
# python-jenkins library.
import difflib
import filecmp
import io
import difflib
import os
import shutil
import tempfile
import yaml
from unittest import mock
import jenkins
from six . moves import StringIO
import testtools
import pytest
from testtools . assertions import assert_that
from jenkins_jobs . cli import entry
from tests . base import mock
from tests . cmd . test_cmd import CmdTestsBase
@mock.patch ( " jenkins_jobs.builder.JenkinsManager.get_plugins_info " , mock . MagicMock )
class TestTests ( CmdTestsBase ) :
def test_non_existing_job ( self ) :
"""
Run test mode and pass a non - existing job name
( probably better to fail here )
"""
args = [
" --conf " ,
self . default_config_file ,
" test " ,
os . path . join ( self . fixtures_path , " cmd-001.yaml " ) ,
" invalid " ,
]
self . execute_jenkins_jobs_with_args ( args )
def test_non_existing_job ( fixtures_dir , default_config_file , execute_jenkins_jobs ) :
"""
Run test mode and pass a non - existing job name
( probably better to fail here )
"""
args = [
" --conf " ,
default_config_file ,
" test " ,
str ( fixtures_dir / " cmd-001.yaml " ) ,
" invalid " ,
]
execute_jenkins_jobs ( args )
def test_valid_job ( self ) :
"""
Run test mode and pass a valid job name
"""
args = [
" --conf " ,
self . default_config_file ,
" test " ,
os . path . join ( self . fixtures_path , " cmd-001.yaml " ) ,
" foo-job " ,
]
console_out = io . BytesIO ( )
with mock . patch ( " sys.stdout " , console_out ) :
self . execute_jenkins_jobs_with_args ( args )
def test_console_output ( self ) :
"""
Run test mode and verify that resulting XML gets sent to the console .
"""
console_out = io . BytesIO ( )
with mock . patch ( " sys.stdout " , console_out ) :
args = [
" --conf " ,
self . default_config_file ,
" test " ,
os . path . join ( self . fixtures_path , " cmd-001.yaml " ) ,
]
self . execute_jenkins_jobs_with_args ( args )
xml_content = io . open (
os . path . join ( self . fixtures_path , " cmd-001.xml " ) , " r " , encoding = " utf-8 "
) . read ( )
self . assertEqual ( console_out . getvalue ( ) . decode ( " utf-8 " ) , xml_content )
def test_output_dir ( self ) :
"""
Run test mode with output to directory and verify that output files are
generated .
"""
tmpdir = tempfile . mkdtemp ( )
self . addCleanup ( shutil . rmtree , tmpdir )
args = [ " test " , os . path . join ( self . fixtures_path , " cmd-001.yaml " ) , " -o " , tmpdir ]
self . execute_jenkins_jobs_with_args ( args )
self . expectThat (
os . path . join ( tmpdir , " foo-job " ) , testtools . matchers . FileExists ( )
)
def test_output_dir_config_xml ( self ) :
"""
Run test mode with output to directory in " config.xml " mode and verify
that output files are generated .
"""
tmpdir = tempfile . mkdtemp ( )
self . addCleanup ( shutil . rmtree , tmpdir )
args = [
" test " ,
os . path . join ( self . fixtures_path , " cmd-001.yaml " ) ,
" -o " ,
tmpdir ,
" --config-xml " ,
]
self . execute_jenkins_jobs_with_args ( args )
self . expectThat (
os . path . join ( tmpdir , " foo-job " , " config.xml " ) ,
testtools . matchers . FileExists ( ) ,
)
def test_valid_job ( fixtures_dir , default_config_file , execute_jenkins_jobs ) :
"""
Run test mode and pass a valid job name
"""
args = [
" --conf " ,
default_config_file ,
" test " ,
str ( fixtures_dir / " cmd-001.yaml " ) ,
" foo-job " ,
]
execute_jenkins_jobs ( args )
def test_console_output (
capsys , fixtures_dir , default_config_file , execute_jenkins_jobs
) :
"""
Run test mode and verify that resulting XML gets sent to the console .
"""
def test_stream_input_output_no_encoding_exceed_recursion ( self ) :
"""
Test that we don ' t have issues processing large number of jobs and
outputting the result if the encoding is not set .
"""
console_out = io . BytesIO ( )
input_file = os . path . join ( self . fixtures_path , " large-number-of-jobs-001.yaml " )
with io . open ( input_file , " r " ) as f :
with mock . patch ( " sys.stdout " , console_out ) :
console_out . encoding = None
with mock . patch ( " sys.stdin " , f ) :
args = [ " test " ]
self . execute_jenkins_jobs_with_args ( args )
def test_stream_input_output_utf8_encoding ( self ) :
"""
Run test mode simulating using pipes for input and output using
utf - 8 encoding
"""
console_out = io . BytesIO ( )
input_file = os . path . join ( self . fixtures_path , " cmd-001.yaml " )
with io . open ( input_file , " r " ) as f :
with mock . patch ( " sys.stdout " , console_out ) :
with mock . patch ( " sys.stdin " , f ) :
args = [ " --conf " , self . default_config_file , " test " ]
self . execute_jenkins_jobs_with_args ( args )
xml_content = io . open (
os . path . join ( self . fixtures_path , " cmd-001.xml " ) , " r " , encoding = " utf-8 "
) . read ( )
value = console_out . getvalue ( ) . decode ( " utf-8 " )
self . assertEqual ( value , xml_content )
def test_stream_input_output_ascii_encoding ( self ) :
"""
Run test mode simulating using pipes for input and output using
ascii encoding with unicode input
"""
console_out = io . BytesIO ( )
console_out . encoding = " ascii "
input_file = os . path . join ( self . fixtures_path , " cmd-001.yaml " )
with io . open ( input_file , " r " ) as f :
with mock . patch ( " sys.stdout " , console_out ) :
with mock . patch ( " sys.stdin " , f ) :
args = [ " --conf " , self . default_config_file , " test " ]
self . execute_jenkins_jobs_with_args ( args )
xml_content = io . open (
os . path . join ( self . fixtures_path , " cmd-001.xml " ) , " r " , encoding = " utf-8 "
) . read ( )
value = console_out . getvalue ( ) . decode ( " ascii " )
self . assertEqual ( value , xml_content )
def test_stream_output_ascii_encoding_invalid_char ( self ) :
"""
Run test mode simulating using pipes for input and output using
ascii encoding for output with include containing a character
that cannot be converted .
"""
console_out = io . BytesIO ( )
console_out . encoding = " ascii "
input_file = os . path . join ( self . fixtures_path , " unicode001.yaml " )
with io . open ( input_file , " r " , encoding = " utf-8 " ) as f :
with mock . patch ( " sys.stdout " , console_out ) :
with mock . patch ( " sys.stdin " , f ) :
args = [ " --conf " , self . default_config_file , " test " ]
jenkins_jobs = entry . JenkinsJobs ( args )
e = self . assertRaises ( UnicodeError , jenkins_jobs . execute )
self . assertIn ( " ' ascii ' codec can ' t encode character " , str ( e ) )
@mock.patch ( " jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML " )
@mock.patch ( " jenkins_jobs.cli.subcommand.update.ModuleRegistry " )
def test_plugins_info_stub_option ( self , registry_mock , generateXML_mock ) :
"""
Test handling of plugins_info stub option .
"""
plugins_info_stub_yaml_file = os . path . join (
self . fixtures_path , " plugins-info.yaml "
)
args = [
" --conf " ,
os . path . join ( self . fixtures_path , " cmd-001.conf " ) ,
" test " ,
" -p " ,
plugins_info_stub_yaml_file ,
os . path . join ( self . fixtures_path , " cmd-001.yaml " ) ,
]
args = [
" --conf " ,
default_config_file ,
" test " ,
str ( fixtures_dir / " cmd-001.yaml " ) ,
]
execute_jenkins_jobs ( args )
self . execute_jenkins_jobs_with_args ( args )
expected_output = fixtures_dir . joinpath ( " cmd-001.xml " ) . read_text ( )
captured = capsys . readouterr ( )
assert captured . out == expected_output
with io . open ( plugins_info_stub_yaml_file , " r " , encoding = " utf-8 " ) as yaml_file :
plugins_info_list = yaml . safe_load ( yaml_file )
registry_mock . assert_called_with ( mock . ANY , plugins_info_list )
def test_output_dir ( tmp_path , fixtures_dir , default_config_file , execute_jenkins_jobs ) :
"""
Run test mode with output to directory and verify that output files are
generated .
"""
args = [ " test " , str ( fixtures_dir / " cmd-001.yaml " ) , " -o " , str ( tmp_path ) ]
execute_jenkins_jobs ( args )
assert tmp_path . joinpath ( " foo-job " ) . exists ( )
@mock.patch ( " jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML " )
@mock.patch ( " jenkins_jobs.cli.subcommand.update.ModuleRegistry " )
def test_bogus_plugins_info_stub_option ( self , registry_mock , generateXML_mock ) :
"""
Verify that a JenkinsJobException is raised if the plugins_info stub
file does not yield a list as its top - level object .
"""
plugins_info_stub_yaml_file = os . path . join (
self . fixtures_path , " bogus-plugins-info.yaml "
)
def test_output_dir_config_xml ( tmp_path , fixtures_dir , execute_jenkins_jobs ) :
"""
Run test mode with output to directory in " config.xml " mode and verify
that output files are generated .
"""
args = [
" test " ,
str ( fixtures_dir / " cmd-001.yaml " ) ,
" -o " ,
str ( tmp_path ) ,
" --config-xml " ,
]
execute_jenkins_jobs ( args )
assert tmp_path . joinpath ( " foo-job " , " config.xml " ) . exists ( )
def test_stream_input_output_no_encoding_exceed_recursion (
mocker , fixtures_dir , execute_jenkins_jobs
) :
"""
Test that we don ' t have issues processing large number of jobs and
outputting the result if the encoding is not set .
"""
console_out = io . BytesIO ( )
console_out . encoding = None
mocker . patch ( " sys.stdout " , console_out )
input = fixtures_dir . joinpath ( " large-number-of-jobs-001.yaml " ) . read_bytes ( )
mocker . patch ( " sys.stdin " , io . BytesIO ( input ) )
args = [ " test " ]
execute_jenkins_jobs ( args )
def test_stream_input_output_utf8_encoding (
capsys , mocker , fixtures_dir , default_config_file , execute_jenkins_jobs
) :
"""
Run test mode simulating using pipes for input and output using
utf - 8 encoding
"""
input = fixtures_dir . joinpath ( " cmd-001.yaml " ) . read_bytes ( )
mocker . patch ( " sys.stdin " , io . BytesIO ( input ) )
args = [ " --conf " , default_config_file , " test " ]
execute_jenkins_jobs ( args )
expected_output = fixtures_dir . joinpath ( " cmd-001.xml " ) . read_text ( )
captured = capsys . readouterr ( )
assert captured . out == expected_output
def test_stream_input_output_ascii_encoding (
mocker , fixtures_dir , default_config_file , execute_jenkins_jobs
) :
"""
Run test mode simulating using pipes for input and output using
ascii encoding with unicode input
"""
console_out = io . BytesIO ( )
console_out . encoding = " ascii "
mocker . patch ( " sys.stdout " , console_out )
input = fixtures_dir . joinpath ( " cmd-001.yaml " ) . read_bytes ( )
mocker . patch ( " sys.stdin " , io . BytesIO ( input ) )
args = [ " --conf " , default_config_file , " test " ]
execute_jenkins_jobs ( args )
expected_output = fixtures_dir . joinpath ( " cmd-001.xml " ) . read_text ( )
output = console_out . getvalue ( ) . decode ( " ascii " )
assert output == expected_output
def test_stream_output_ascii_encoding_invalid_char (
mocker , fixtures_dir , default_config_file
) :
"""
Run test mode simulating using pipes for input and output using
ascii encoding for output with include containing a character
that cannot be converted .
"""
console_out = io . BytesIO ( )
console_out . encoding = " ascii "
mocker . patch ( " sys.stdout " , console_out )
input = fixtures_dir . joinpath ( " unicode001.yaml " ) . read_bytes ( )
mocker . patch ( " sys.stdin " , io . BytesIO ( input ) )
args = [ " --conf " , default_config_file , " test " ]
jenkins_jobs = entry . JenkinsJobs ( args )
with pytest . raises ( UnicodeError ) as excinfo :
jenkins_jobs . execute ( )
assert " ' ascii ' codec can ' t encode character " in str ( excinfo . value )
def test_plugins_info_stub_option ( mocker , fixtures_dir , execute_jenkins_jobs ) :
"""
Test handling of plugins_info stub option .
"""
mocker . patch ( " jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML " )
registry_mock = mocker . patch ( " jenkins_jobs.cli.subcommand.update.ModuleRegistry " )
plugins_info_stub_yaml_file = fixtures_dir / " plugins-info.yaml "
args = [
" --conf " ,
str ( fixtures_dir / " cmd-001.conf " ) ,
" test " ,
" -p " ,
str ( plugins_info_stub_yaml_file ) ,
str ( fixtures_dir / " cmd-001.yaml " ) ,
]
execute_jenkins_jobs ( args )
plugins_info_list = yaml . safe_load ( plugins_info_stub_yaml_file . read_text ( ) )
registry_mock . assert_called_with ( mock . ANY , plugins_info_list )
def test_bogus_plugins_info_stub_option (
capsys , mocker , fixtures_dir , default_config_file
) :
"""
Verify that a JenkinsJobException is raised if the plugins_info stub
file does not yield a list as its top - level object .
"""
mocker . patch ( " jenkins_jobs.cli.subcommand.update.XmlJobGenerator.generateXML " )
mocker . patch ( " jenkins_jobs.cli.subcommand.update.ModuleRegistry " )
plugins_info_stub_yaml_file = fixtures_dir / " bogus-plugins-info.yaml "
args = [
" --conf " ,
str ( fixtures_dir / " cmd-001.conf " ) ,
" test " ,
" -p " ,
str ( plugins_info_stub_yaml_file ) ,
str ( fixtures_dir / " cmd-001.yaml " ) ,
]
with pytest . raises ( SystemExit ) :
entry . JenkinsJobs ( args )
captured = capsys . readouterr ( )
assert " must contain a Yaml list " in captured . err
# Test without mocking get_plugins_info.
#
# This test class is used for testing the 'test' subcommand when we want
# to validate its behavior without mocking
# jenkins_jobs.builder.JenkinsManager.get_plugins_info
def test_console_output_jenkins_connection_failure_warning (
caplog , mocker , fixtures_dir , execute_jenkins_jobs
) :
"""
Run test mode and verify that failed Jenkins connection attempt
exception does not bubble out of cmd . main .
"""
mocker . patch (
" jenkins.Jenkins.get_plugins " ,
side_effect = jenkins . JenkinsException ( " Connection refused " ) ,
)
try :
args = [
" --conf " ,
os . path . join ( self . fixtures_path , " cmd-001.conf " ) ,
str ( fixtures_dir / " enable-query-plugins .conf" ) ,
" test " ,
" -p " ,
plugins_info_stub_yaml_file ,
os . path . join ( self . fixtures_path , " cmd-001.yaml " ) ,
str ( fixtures_dir / " cmd-001.yaml " ) ,
]
execute_jenkins_jobs ( args )
except jenkins . JenkinsException :
pytest . fail ( " jenkins.JenkinsException propagated to main " )
except Exception :
pass # only care about jenkins.JenkinsException for now
assert " Unable to retrieve Jenkins Plugin Info " in caplog . text
stderr = StringIO ( )
with mock . patch ( " sys.stderr " , stderr ) :
self . assertRaises ( SystemExit , entry . JenkinsJobs , args )
self . assertIn ( " must contain a Yaml list " , stderr . getvalue ( ) )
class TestJenkinsGetPluginInfoError ( CmdTestsBase ) :
""" Test without mocking get_plugins_info.
This test class is used for testing the ' test ' subcommand when we want
to validate its behavior without mocking
jenkins_jobs . builder . JenkinsManager . get_plugins_info
"""
@mock.patch ( " jenkins.Jenkins.get_plugins " )
def test_console_output_jenkins_connection_failure_warning ( self , get_plugins_mock ) :
"""
Run test mode and verify that failed Jenkins connection attempt
exception does not bubble out of cmd . main . Ideally , we would also test
that an appropriate message is logged to stderr but it ' s somewhat
difficult to figure out how to actually enable stderr in this test
suite .
"""
get_plugins_mock . side_effect = jenkins . JenkinsException ( " Connection refused " )
with mock . patch ( " sys.stdout " ) :
try :
args = [
" --conf " ,
self . default_config_file ,
" test " ,
os . path . join ( self . fixtures_path , " cmd-001.yaml " ) ,
]
self . execute_jenkins_jobs_with_args ( args )
except jenkins . JenkinsException :
self . fail ( " jenkins.JenkinsException propagated to main " )
except Exception :
pass # only care about jenkins.JenkinsException for now
@mock.patch ( " jenkins.Jenkins.get_plugins " )
def test_skip_plugin_retrieval_if_no_config_provided ( self , get_plugins_mock ) :
"""
Verify that retrieval of information from Jenkins instance about its
plugins will be skipped when run if no config file provided .
"""
with mock . patch ( " sys.stdout " , new_callable = io . BytesIO ) :
args = [