github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/test/groovy/PiperExecuteBinTest.groovy (about) 1 import com.sap.piper.DebugReport 2 import com.sap.piper.DefaultValueCache 3 import com.sap.piper.JenkinsUtils 4 import groovy.json.JsonSlurper 5 import hudson.AbortException 6 import org.junit.After 7 import org.junit.Before 8 import org.junit.Rule 9 import org.junit.Test 10 import org.junit.rules.ExpectedException 11 import org.junit.rules.RuleChain 12 import util.* 13 14 import static org.hamcrest.Matchers.* 15 import static org.junit.Assert.assertThat 16 17 class PiperExecuteBinTest extends BasePiperTest { 18 private ExpectedException exception = ExpectedException.none() 19 20 private JenkinsCredentialsRule credentialsRule = new JenkinsCredentialsRule(this) 21 private JenkinsShellCallRule shellCallRule = new JenkinsShellCallRule(this) 22 private JenkinsStepRule stepRule = new JenkinsStepRule(this) 23 private JenkinsWriteFileRule writeFileRule = new JenkinsWriteFileRule(this) 24 private JenkinsFileExistsRule fileExistsRule = new JenkinsFileExistsRule(this, []) 25 private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this) 26 27 private List withEnvArgs = [] 28 private List credentials = [] 29 private List artifacts = [] 30 31 @Rule 32 public RuleChain rules = Rules 33 .getCommonRules(this) 34 .around(exception) 35 .around(new JenkinsReadYamlRule(this)) 36 .around(credentialsRule) 37 .around(shellCallRule) 38 .around(stepRule) 39 .around(writeFileRule) 40 .around(fileExistsRule) 41 .around(dockerExecuteRule) 42 43 @Before 44 void init() { 45 credentials = [] 46 47 // Clear DebugReport to avoid left-overs from another UnitTest 48 DebugReport.instance.failedBuild = [:] 49 50 helper.registerAllowedMethod("withEnv", [List.class, Closure.class], {arguments, closure -> 51 arguments.each {arg -> 52 withEnvArgs.add(arg.toString()) 53 } 54 return closure() 55 }) 56 57 helper.registerAllowedMethod("writePipelineEnv", [Map.class], {m -> return }) 58 helper.registerAllowedMethod("readPipelineEnv", [Map.class], {m -> return }) 59 helper.registerAllowedMethod('fileExists', [Map.class], {m -> 60 if (m.file == 'noDetailsStep_errorDetails.json') { 61 return false 62 } 63 return true 64 }) 65 helper.registerAllowedMethod("readJSON", [Map], { m -> 66 if(m.file == 'testStep_reports.json') 67 return [[target: "1234.pdf", mandatory: true]] 68 if(m.file == 'testStep_links.json') 69 return [] 70 if(m.file == 'testStepCategory_errorDetails.json') 71 return [message: 'detailed error', category: 'testCategory'] 72 if(m.file == 'testStep_errorDetails.json') 73 return [message: 'detailed error'] 74 if(m.text != null) 75 return new JsonSlurper().parseText(m.text) 76 }) 77 78 helper.registerAllowedMethod('libraryResource', [String.class], {s -> 79 if (s == 'metadata/test.yaml') { 80 return '''metadata: 81 name: testStep 82 ''' 83 } else { 84 return '''general: 85 failOnError: true 86 ''' 87 } 88 }) 89 90 helper.registerAllowedMethod('file', [Map], { m -> return m }) 91 helper.registerAllowedMethod('string', [Map], { m -> return m }) 92 helper.registerAllowedMethod('usernamePassword', [Map], { m -> return m }) 93 helper.registerAllowedMethod('withCredentials', [List, Closure], { l, c -> 94 l.each {m -> 95 credentials.add(m) 96 if (m.credentialsId == 'credFile') { 97 binding.setProperty('PIPER_credFile', 'credFileContent') 98 } else if (m.credentialsId == 'credToken') { 99 binding.setProperty('PIPER_credToken','credTokenContent') 100 } else if (m.credentialsId == 'credUsernamePassword') { 101 binding.setProperty('PIPER_user', 'userId') 102 binding.setProperty('PIPER_password', '********') 103 } 104 } 105 try { 106 c() 107 } finally { 108 binding.setProperty('PIPER_credFile', null) 109 binding.setProperty('PIPER_credToken', null) 110 binding.setProperty('PIPER_user', null) 111 binding.setProperty('PIPER_password', null) 112 } 113 }) 114 115 helper.registerAllowedMethod('archiveArtifacts', [Map.class], {m -> 116 artifacts.add(m) 117 return null 118 }) 119 120 helper.registerAllowedMethod('findFiles', [Map.class], {m -> return null}) 121 } 122 123 @Test 124 void testPiperExecuteBinDefault() { 125 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 126 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{"fileCredentialsId":"credFile", "tokenCredentialsId":"credToken", "credentialsId":"credUsernamePassword", "dockerImage":"my.Registry/my/image:latest"}') 127 128 List stepCredentials = [ 129 [type: 'file', id: 'fileCredentialsId', env: ['PIPER_credFile']], 130 [type: 'token', id: 'tokenCredentialsId', env: ['PIPER_credToken']], 131 [type: 'usernamePassword', id: 'credentialsId', env: ['PIPER_user', 'PIPER_password']], 132 ] 133 stepRule.step.piperExecuteBin( 134 [ 135 juStabUtils: utils, 136 jenkinsUtilsStub: jenkinsUtils, 137 testParam: "This is test content", 138 script: nullScript 139 ], 140 'testStep', 141 'metadata/test.yaml', 142 stepCredentials 143 ) 144 // asserts 145 assertThat(writeFileRule.files['.pipeline/tmp/metadata/test.yaml'], containsString('name: testStep')) 146 assertThat(withEnvArgs[0], allOf(startsWith('PIPER_parametersJSON'), containsString('"testParam":"This is test content"'))) 147 assertThat(shellCallRule.shell[2], is('./piper testStep')) 148 assertThat(credentials.size(), is(3)) 149 assertThat(credentials[0], allOf(hasEntry('credentialsId', 'credFile'), hasEntry('variable', 'PIPER_credFile'))) 150 assertThat(credentials[1], allOf(hasEntry('credentialsId', 'credToken'), hasEntry('variable', 'PIPER_credToken'))) 151 assertThat(credentials[2], allOf(hasEntry('credentialsId', 'credUsernamePassword'), hasEntry('usernameVariable', 'PIPER_user') , hasEntry('passwordVariable', 'PIPER_password'))) 152 153 assertThat(dockerExecuteRule.dockerParams.dockerImage, is('my.Registry/my/image:latest')) 154 assertThat(dockerExecuteRule.dockerParams.stashContent, is([])) 155 156 assertThat(artifacts[0], allOf(hasEntry('artifacts', '1234.pdf'), hasEntry('allowEmptyArchive', false))) 157 } 158 159 @Test 160 void testPiperExecuteBinANSCredentialsFromHooksSection() { 161 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 162 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{"fileCredentialsId":"credFile", "tokenCredentialsId":"credToken", "credentialsId":"credUsernamePassword", "dockerImage":"my.Registry/my/image:latest"}') 163 164 def newScript = nullScript 165 DefaultValueCache.createInstance([hooks: [ans: [serviceKeyCredentialsId: "ansServiceKeyID"]]]) 166 167 List stepCredentials = [] 168 stepRule.step.piperExecuteBin( 169 [ 170 juStabUtils: utils, 171 jenkinsUtilsStub: jenkinsUtils, 172 testParam: "This is test content", 173 script: newScript 174 ], 175 'testStep', 176 'metadata/test.yaml', 177 stepCredentials 178 ) 179 // asserts 180 assertThat(credentials.size(), is(1)) 181 assertThat(credentials[0], allOf(hasEntry('credentialsId', 'ansServiceKeyID'), hasEntry('variable', 'PIPER_ansHookServiceKey'))) 182 DefaultValueCache.reset() 183 } 184 185 @Test 186 void testPiperExecuteBinDontResolveCredentialsAndNoCredId() { 187 188 // In case we have a credential entry without Id we drop that silenty. 189 // Maybe we should revisit that and fail in this case. 190 191 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 192 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{"dockerImage":"my.Registry/my/image:latest"}') 193 194 List stepCredentials = [ 195 [type: 'token', env: ['PIPER_credTokenNoResolve'], resolveCredentialsId: false], 196 ] 197 198 stepRule.step.piperExecuteBin( 199 [ 200 juStabUtils: utils, 201 jenkinsUtilsStub: jenkinsUtils, 202 testParam: "This is test content", 203 script: nullScript 204 ], 205 'testStep', 206 'metadata/test.yaml', 207 stepCredentials 208 ) 209 assertThat(credentials.size(), is(0)) 210 } 211 212 @Test 213 void testPiperExecuteBinSomeCredentials() { 214 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 215 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{"fileCredentialsId":"credFile", "tokenCredentialsId":"credToken", "dockerImage":"my.Registry/my/image:latest"}') 216 217 List stepCredentials = [ 218 [type: 'file', id: 'fileCredentialsId', env: ['PIPER_credFile']], 219 [type: 'token', id: 'tokenCredentialsId', env: ['PIPER_credToken']], 220 // for the entry below we don't have a config lookup. 221 [type: 'token', id: 'tokenCredentialsIdNoResolve', env: ['PIPER_credTokenNoResolve'], resolveCredentialsId: false], 222 [type: 'token', id: 'tokenCredentialsIdNotContainedInConfig', env: ['PIPER_credToken_doesNotMatter']], 223 [type: 'usernamePassword', id: 'credentialsId', env: ['PIPER_user', 'PIPER_password']], 224 ] 225 stepRule.step.piperExecuteBin( 226 [ 227 juStabUtils: utils, 228 jenkinsUtilsStub: jenkinsUtils, 229 testParam: "This is test content", 230 script: nullScript 231 ], 232 'testStep', 233 'metadata/test.yaml', 234 stepCredentials 235 ) 236 // asserts 237 assertThat(credentials.size(), is(3)) 238 assertThat(credentials[0], allOf(hasEntry('credentialsId', 'credFile'), hasEntry('variable', 'PIPER_credFile'))) 239 assertThat(credentials[1], allOf(hasEntry('credentialsId', 'credToken'), hasEntry('variable', 'PIPER_credToken'))) 240 assertThat(credentials[2], allOf(hasEntry('credentialsId', 'tokenCredentialsIdNoResolve'), hasEntry('variable', 'PIPER_credTokenNoResolve'))) 241 } 242 243 @Test 244 void testPiperExecuteBinSSHCredentials() { 245 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 246 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{"sshCredentialsId":"sshKey", "tokenCredentialsId":"credToken"}') 247 248 List sshKey = [] 249 helper.registerAllowedMethod("sshagent", [List, Closure], {s, c -> 250 sshKey = s 251 c() 252 }) 253 254 List stepCredentials = [ 255 [type: 'token', id: 'tokenCredentialsId', env: ['PIPER_credToken']], 256 [type: 'ssh', id: 'sshCredentialsId'], 257 ] 258 stepRule.step.piperExecuteBin( 259 [ 260 juStabUtils: utils, 261 jenkinsUtilsStub: jenkinsUtils, 262 testParam: "This is test content", 263 script: nullScript 264 ], 265 'testStep', 266 'metadata/test.yaml', 267 stepCredentials 268 ) 269 // asserts 270 assertThat(credentials.size(), is(1)) 271 assertThat(credentials[0], allOf(hasEntry('credentialsId', 'credToken'), hasEntry('variable', 'PIPER_credToken'))) 272 assertThat(sshKey, is(['sshKey'])) 273 } 274 275 @Test 276 void testPiperExecuteBinNoDockerNoCredentials() { 277 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 278 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{}') 279 280 stepRule.step.piperExecuteBin( 281 [ 282 juStabUtils: utils, 283 jenkinsUtilsStub: jenkinsUtils, 284 testParam: "This is test content", 285 script: nullScript 286 ], 287 'testStep', 288 'metadata/test.yaml', 289 [] 290 ) 291 292 assertThat(writeFileRule.files['.pipeline/tmp/metadata/test.yaml'], containsString('name: testStep')) 293 assertThat(withEnvArgs[0], allOf(startsWith('PIPER_parametersJSON'), containsString('"testParam":"This is test content"'))) 294 assertThat(shellCallRule.shell[2], is('./piper testStep')) 295 assertThat(credentials.size(), is(0)) 296 297 assertThat(dockerExecuteRule.dockerParams.size(), is(0)) 298 299 assertThat(artifacts[0], allOf(hasEntry('artifacts', '1234.pdf'), hasEntry('allowEmptyArchive', false))) 300 301 } 302 303 @Test 304 void testPiperExecuteBinNoReportFound() { 305 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 306 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{}') 307 helper.registerAllowedMethod('fileExists', [Map], { 308 return false 309 }) 310 311 exception.expect(AbortException) 312 exception.expectMessage("Expected to find testStep_reports.json in workspace but it is not there") 313 314 stepRule.step.piperExecuteBin( 315 [ 316 juStabUtils: utils, 317 jenkinsUtilsStub: jenkinsUtils, 318 testParam: "This is test content", 319 script: nullScript 320 ], 321 'testStep', 322 'metadata/test.yaml', 323 [], 324 true, 325 false, 326 false 327 ) 328 } 329 330 @Test 331 void testErrorWithCategory() { 332 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 333 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{}') 334 helper.registerAllowedMethod('sh', [String.class], {s -> throw new AbortException('exit code 1')}) 335 336 exception.expect(AbortException) 337 exception.expectMessage("[testStepCategory] Step execution failed (category: testCategory). Error: detailed error") 338 339 try { 340 stepRule.step.piperExecuteBin( 341 [ 342 juStabUtils: utils, 343 jenkinsUtilsStub: jenkinsUtils, 344 testParam: "This is test content", 345 script: nullScript 346 ], 347 'testStepCategory', 348 'metadata/test.yaml', 349 [] 350 ) 351 } catch (ex) { 352 assertThat(DebugReport.instance.failedBuild.category, is('testCategory')) 353 throw ex 354 } 355 356 } 357 358 @Test 359 void testErrorWithoutCategory() { 360 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 361 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{}') 362 helper.registerAllowedMethod('sh', [String.class], {s -> throw new AbortException('exit code 1')}) 363 364 exception.expect(AbortException) 365 exception.expectMessage("[testStep] Step execution failed. Error: detailed error") 366 367 stepRule.step.piperExecuteBin( 368 [ 369 juStabUtils: utils, 370 jenkinsUtilsStub: jenkinsUtils, 371 testParam: "This is test content", 372 script: nullScript 373 ], 374 'testStep', 375 'metadata/test.yaml', 376 [] 377 ) 378 } 379 380 @Test 381 void testErrorNoDetails() { 382 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 383 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{}') 384 helper.registerAllowedMethod('sh', [String.class], {s -> throw new AbortException('exit code 1')}) 385 386 exception.expect(AbortException) 387 exception.expectMessage("[noDetailsStep] Step execution failed. Error: hudson.AbortException: exit code 1, please see log file for more details.") 388 389 stepRule.step.piperExecuteBin( 390 [ 391 juStabUtils: utils, 392 jenkinsUtilsStub: jenkinsUtils, 393 testParam: "This is test content", 394 script: nullScript 395 ], 396 'noDetailsStep', 397 'metadata/test.yaml', 398 [] 399 ) 400 } 401 402 @Test 403 void testRespectPipelineResilienceSetting() { 404 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 405 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{}') 406 helper.registerAllowedMethod('sh', [String.class], {s -> throw new AbortException('exit code 1')}) 407 408 def unstableCalled 409 helper.registerAllowedMethod('unstable', [String.class], {s -> unstableCalled = true}) 410 411 try { 412 nullScript.commonPipelineEnvironment.configuration.steps = [handlePipelineStepErrors: [failOnError: false]] 413 414 stepRule.step.piperExecuteBin( 415 [ 416 juStabUtils: utils, 417 jenkinsUtilsStub: jenkinsUtils, 418 testParam: "This is test content", 419 script: nullScript 420 ], 421 'noDetailsStep', 422 'metadata/test.yaml', 423 [] 424 ) 425 } finally { 426 //clean up 427 nullScript.commonPipelineEnvironment.configuration.steps = [handlePipelineStepErrors: [failOnError: true]] 428 } 429 assertThat(unstableCalled, is(true)) 430 431 } 432 433 @Test 434 void testProperStashHandling() { 435 shellCallRule.setReturnValue('[ -x ./piper ]', 1) 436 shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'.pipeline/tmp/metadata/test.yaml\'', '{"dockerImage":"test","stashContent":["buildDescriptor"]}') 437 438 stepRule.step.piperExecuteBin( 439 [ 440 juStabUtils: utils, 441 jenkinsUtilsStub: jenkinsUtils, 442 script: nullScript 443 ], 444 'testStep', 445 'metadata/test.yaml', 446 [] 447 ) 448 449 assertThat(dockerExecuteRule.dockerParams.stashContent, is(["buildDescriptor", "pipelineConfigAndTests", "piper-bin", "pipelineStepReports"])) 450 } 451 }