Skip to content

Commit

Permalink
Merge branch 'feature/IJMP-1714-handle-job-errors' of github.com:zowe…
Browse files Browse the repository at this point in the history
…/zowe-zdevops-jenkins-plugin into feature/IJMP-1714-handle-job-errors
  • Loading branch information
callbacksin committed Sep 26, 2024
2 parents f8b7c69 + a3f931d commit 2f2bfdd
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ import org.kohsuke.stapler.DataBoundConstructor
import org.zowe.zdevops.utils.getZoweZosConnection
import org.zowe.zdevops.utils.validateConnection


class ZosmfStepDeclarative @DataBoundConstructor constructor(private val connectionName: String) : Step() {
override fun start(context: StepContext): StepExecution {
val listener: TaskListener? = context.get(TaskListener::class.java)
val zosConnection = getZoweZosConnection(connectionName, listener)
val zosConnection = getZoweZosConnection(connectionName, listener)

validateConnection(zosConnection)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.declarative

import hudson.EnvVars
import hudson.FilePath
import hudson.model.TaskListener
import io.kotest.assertions.assertSoftly
import io.kotest.core.spec.style.ShouldSpec
import io.kotest.matchers.shouldBe
import io.mockk.*
import org.jenkinsci.plugins.workflow.steps.StepContext
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution
import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
import org.zowe.zdevops.testutils.getPrivateFieldValue
import org.zowe.zdevops.utils.getZoweZosConnection
import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets
import java.util.concurrent.Future

class AbstractZosmfActionWithResultSpec : ShouldSpec({
var runCalledCount = 0

val zosmfActionWithResult = object : AbstractZosmfActionWithResult() {
override fun run(
workspace: FilePath,
listener: TaskListener,
envVars: EnvVars,
zoweZOSConnection: ZOSConnection
): String {
runCalledCount++
return "test"
}
}

afterEach {
runCalledCount = 0
}

context("declarative module: AbstractZosmfActionWithResult") {
should("check execution runs the step provided when the task is started and finished successfully") {
val workspace = mockk<FilePath>()
every { workspace.read() } returns ByteArrayInputStream("test".toByteArray(StandardCharsets.UTF_8))

val stepContext = mockk<StepContext>()
every { stepContext.get(FilePath::class.java) } returns workspace
every { stepContext.get(TaskListener::class.java) } returns mockk<TaskListener>()
every { stepContext.get(EnvVars::class.java) } returns mockk<EnvVars>()
every { stepContext.onSuccess(any()) } just Runs

mockkStatic(::getZoweZosConnection)
every { getZoweZosConnection("test", any()) } returns mockk<ZOSConnection>()

val execution = zosmfActionWithResult.start(stepContext) as AbstractZosmfActionWithResult.Companion.Execution
execution.start()
val executionTask: Future<*> = getPrivateFieldValue(
execution,
SynchronousNonBlockingStepExecution::class.java,
"task"
) as Future<*>
executionTask.get()

verify(exactly = 1) { stepContext.onSuccess(any()) }
assertSoftly { runCalledCount shouldBe 1 }
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.declarative

import hudson.model.TaskListener
import io.kotest.core.spec.style.ShouldSpec
import io.mockk.*
import org.jenkinsci.plugins.workflow.steps.StepContext
import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
import org.zowe.zdevops.config.ZOSConnectionList
import org.zowe.zdevops.model.ResolvedZOSConnection
import org.zowe.zdevops.utils.getZoweZosConnection
import org.zowe.zdevops.utils.validateConnection

class ZosmfStepDeclarativeSpec : ShouldSpec({
context("declarative module: ZosmfStepDeclarative") {
should("check the zosmf declarative step is run, connection is formed and validated") {
val zosmfStepDeclarative = ZosmfStepDeclarative("test")

val taskListener = mockk<TaskListener>()

val stepContext = mockk<StepContext>()
every { stepContext.get(TaskListener::class.java) } returns taskListener

val resolvedZosConnection = mockk<ResolvedZOSConnection>()
every { resolvedZosConnection.url } returns "https://test.com:1234"
every { resolvedZosConnection.username } returns "test_user"
every { resolvedZosConnection.password } returns "test_pass"

mockkObject(ZOSConnectionList)
every { ZOSConnectionList.resolve("test") } returns resolvedZosConnection

mockkStatic(::validateConnection)
every { validateConnection(any<ZOSConnection>()) } returns Unit

zosmfStepDeclarative.start(stepContext)
verify(exactly = 1) { validateConnection(any<ZOSConnection>()) }
verify(exactly = 1) { getZoweZosConnection("test", taskListener) }
}
}
})
51 changes: 51 additions & 0 deletions src/test/kotlin/org/zowe/zdevops/testutils/misc.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.testutils

import java.lang.reflect.Modifier

/**
* Mock private/protected field of the class for the object
* @param sourceObj the source object to mock the field for
* @param classWithTheField the class where the field is declared
* @param fieldName the field name to mock
* @param mockValue the mock value to set for the field
*/
fun setPrivateFieldValue(sourceObj: Any, classWithTheField: Class<*>, fieldName: String, mockValue: Any) {
return classWithTheField
.declaredFields
.filter { it.modifiers.and(Modifier.PRIVATE) > 0 || it.modifiers.and(Modifier.PROTECTED) > 0 }
.find { it.name == fieldName }
?.also { it.isAccessible = true }
?.set(sourceObj, mockValue)
?: throw NoSuchFieldException("Field with name '$fieldName' is not found amongst private or protected fields")
}

/**
* Get private/protected field of the class stored in the object
* @param sourceObj the source object to get the field from
* @param classWithTheField the class where the field is declared
* @param fieldName the field name to get value of
*/
fun getPrivateFieldValue(sourceObj: Any, classWithTheField: Class<*>, fieldName: String): Any {
val theField = classWithTheField
.declaredFields
.filter { it.modifiers.and(Modifier.PRIVATE) > 0 || it.modifiers.and(Modifier.PROTECTED) > 0 }
.find { it.name == fieldName }
?.also { it.isAccessible = true }
?: throw NoSuchFieldException("Field with name '$fieldName' is not found amongst private or protected fields")
return theField.get(sourceObj)
?: throw Exception("Field with name '$fieldName' is not accessible")
}

0 comments on commit 2f2bfdd

Please sign in to comment.