From b928166c0ca41adb30366c22898c8dac53bd375a Mon Sep 17 00:00:00 2001 From: Arseni Tsikhamirau Date: Fri, 2 Aug 2024 12:12:21 +0200 Subject: [PATCH 1/5] IJMP-1799-Index-out-of-bound-exception --- .../formainframe/explorer/actions/PurgeJobAction.kt | 5 ++--- .../explorer/actions/PurgeJobActionTestSpec.kt | 12 ++++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt b/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt index 62f6bec2e..d1769215b 100644 --- a/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt +++ b/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt @@ -257,7 +257,6 @@ class PurgeJobAction : AnAction() { e: AnActionEvent, view: JesExplorerView ) { - val dataOpsManager = service() nodes.forEach { nodeData -> val jobAttributes = nodeData.attributes as RemoteJobAttributes val jobStatus = jobAttributes.jobInfo @@ -268,7 +267,7 @@ class PurgeJobAction : AnAction() { cancellable = true ) { runCatching { - dataOpsManager.performOperation( + service().performOperation( operation = PurgeJobOperation( request = BasicPurgeJobParams(jobStatus.jobName, jobStatus.jobId), connectionConfig = connectionConfig @@ -309,7 +308,7 @@ class PurgeJobAction : AnAction() { val selected = view.mySelectedNodesData val wrongNode = selected.find { it.node !is JobNode } e.presentation.apply { - isEnabledAndVisible = wrongNode == null && isSelectedJobNodesFromSameWS(selected) + isEnabledAndVisible = selected.isNotEmpty() && wrongNode == null && isSelectedJobNodesFromSameWS(selected) text = if (isEnabledAndVisible && selected.size > 1) "Purge Jobs" else "Purge Job" } } else if (view is JobBuildTreeView) { diff --git a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobActionTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobActionTestSpec.kt index b4149c015..02f57f05f 100644 --- a/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobActionTestSpec.kt +++ b/src/test/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobActionTestSpec.kt @@ -375,6 +375,18 @@ class PurgeJobActionTestSpec : WithApplicationShouldSpec({ isEnableAndVisibleAction = true } + should("action is not visible when no selected nodes") { + isEnableAndVisibleAction = true + mySelectedData = mutableListOf() + every { jesExplorerView.mySelectedNodesData } returns mySelectedData + every { mockActionEventForJesEx.presentation.isEnabledAndVisible } returns false + purgeAction.update(mockActionEventForJesEx) + + assertSoftly { + isEnableAndVisibleAction shouldBe false + } + } + should("action is not visible when selected job nodes contains wrong node") { isEnableAndVisibleAction = true val nodeData1 = NodeData(jobNode1, virtualFileMock, attributes1) From e8c677d2ca6b53e51146ab2cf4a15985c6d9900c Mon Sep 17 00:00:00 2001 From: Katsiaryna Tsytsenia Date: Fri, 30 Aug 2024 14:21:26 +0200 Subject: [PATCH 2/5] IJMP-1879 Changed color of the job with a warning RC to orange --- .../formainframe/explorer/ui/JobNode.kt | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/JobNode.kt b/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/JobNode.kt index a5feb746d..442a5ebab 100644 --- a/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/JobNode.kt +++ b/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/JobNode.kt @@ -102,10 +102,10 @@ class JobNode( if (job.execSubmitted == null) { "JOB ENDED. RC = ${job.returnedCode}" } else { "ENDED AT: " + parseJobTimestampValueToDisplay(job.execSubmitted) + ". RC = ${job.returnedCode}" }, - if (isErrorReturnCode(job.returnedCode)) { - SimpleTextAttributes(STYLE_BOLD, JBColor.RED) - } else { - SimpleTextAttributes(STYLE_BOLD, JBColor.GREEN) + when (getReturnCode(job.returnedCode)) { + ReturnCode.SUCCESS -> SimpleTextAttributes(STYLE_BOLD, JBColor.GREEN) + ReturnCode.WARNING -> SimpleTextAttributes(STYLE_BOLD, JBColor.ORANGE) + else -> SimpleTextAttributes(STYLE_BOLD, JBColor.RED) } ) } else { @@ -116,10 +116,10 @@ class JobNode( } else { parseJobTimestampValueToDisplay(job.execEnded) } + ". RC = ${job.returnedCode}", - if (isErrorReturnCode(job.returnedCode)) { - SimpleTextAttributes(STYLE_BOLD, JBColor.RED) - } else { - SimpleTextAttributes(STYLE_BOLD, JBColor.GREEN) + when (getReturnCode(job.returnedCode)) { + ReturnCode.SUCCESS -> SimpleTextAttributes(STYLE_BOLD, JBColor.GREEN) + ReturnCode.WARNING -> SimpleTextAttributes(STYLE_BOLD, JBColor.ORANGE) + else -> SimpleTextAttributes(STYLE_BOLD, JBColor.RED) } ) } @@ -162,19 +162,29 @@ class JobNode( /** * Function to parse return code after job completion * @param returnCode - return code to parse - * @return true if return code is kind of Error. False otherwise + * @return ReturnCode.ERR if return code is kind of Error. + * ReturnCode.WARN if return code in [1..8] range. + * ReturnCode.SUCCESS otherwise */ - private fun isErrorReturnCode(returnCode : String?) : Boolean { + private fun getReturnCode(returnCode: String?): ReturnCode { if (returnCode != null) { return if (!returnCode.contains(Regex("ERR|ABEND|CANCEL|FAIL"))) { val numberedRC = returnCode.split(" ").getOrNull(1)?.toIntOrNull() if (numberedRC != null) { - numberedRC > 0 + when (numberedRC) { + 0 -> ReturnCode.SUCCESS + in 1..7 -> ReturnCode.WARNING + else -> ReturnCode.ERROR + } } else { - true + ReturnCode.ERROR } - } else true + } else ReturnCode.ERROR } - return false + return ReturnCode.SUCCESS + } + + enum class ReturnCode { + SUCCESS, WARNING, ERROR } } From 5f7904870823e3678718eaa43305c81a49d0a5ee Mon Sep 17 00:00:00 2001 From: Arseni Tsikhamirau Date: Tue, 24 Sep 2024 16:07:19 +0200 Subject: [PATCH 3/5] IJMP-1832-Do-not-reconnect-on-CredentialsNotFound --- .../explorer/actions/PurgeJobAction.kt | 2 +- .../explorer/ui/ExplorerTreeView.kt | 8 +-- .../formainframe/tso/TSOWindowFactory.kt | 30 +++++++---- .../explorer/ui/ExplorerTreeViewTestSpec.kt | 8 +-- .../tso/TSOWindowFactoryTestSpec.kt | 50 +++++++++++++++++++ 5 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt b/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt index cfb3f4f59..e7ab138c5 100644 --- a/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt +++ b/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/PurgeJobAction.kt @@ -270,7 +270,7 @@ class PurgeJobAction : AnAction() { cancellable = true ) { runCatching { - service().performOperation( + DataOpsManager.getService().performOperation( operation = PurgeJobOperation( request = BasicPurgeJobParams(jobStatus.jobName, jobStatus.jobId), connectionConfig = connectionConfig diff --git a/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/ExplorerTreeView.kt b/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/ExplorerTreeView.kt index 0b7910618..a4b5f64e3 100644 --- a/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/ExplorerTreeView.kt +++ b/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/ExplorerTreeView.kt @@ -64,12 +64,8 @@ import eu.ibagroup.formainframe.explorer.ExplorerUnit import eu.ibagroup.formainframe.explorer.UNITS_CHANGED import eu.ibagroup.formainframe.explorer.WorkingSet import eu.ibagroup.formainframe.telemetry.NotificationsService -import eu.ibagroup.formainframe.utils.castOrNull +import eu.ibagroup.formainframe.utils.* import eu.ibagroup.formainframe.utils.crudable.EntityWithUuid -import eu.ibagroup.formainframe.utils.getAncestorNodes -import eu.ibagroup.formainframe.utils.runInEdtAndWait -import eu.ibagroup.formainframe.utils.rwLocked -import eu.ibagroup.formainframe.utils.subscribe import eu.ibagroup.formainframe.vfs.MFBulkFileListener import eu.ibagroup.formainframe.vfs.MFVFilePropertyChangeEvent import eu.ibagroup.formainframe.vfs.MFVirtualFile @@ -475,7 +471,7 @@ abstract class ExplorerTreeView() as TestDataOpsManagerImpl + dataOpsManager.testInstance = object : TestDataOpsManagerImpl() { + override fun performOperation(operation: Operation, progressIndicator: ProgressIndicator): R { + throw CredentialsNotFoundForConnection(ConnectionConfig()) + } + + } + + val oldSessionResponse = TsoResponse(servletKey = "test-servletKey-1") + val session = TSOConfigWrapper(tsoSessionConfig, connectionConfig, oldSessionResponse) + val command = "TIME" + val messageType = mockk() + val messageData = mockk() + + every { console.getProcessHandler() } returns processHandler + every { console.getTsoSession() } returns session + every { processHandler.notifyTextAvailable(any(), any()) } just Runs + + // when + sendTopic(SESSION_COMMAND_ENTERED).processCommand( + project, + console, + session, + command, + messageType, + messageData, + processHandler + ) + + // then + verify(exactly = 1) { + processHandler.notifyTextAvailable( + "Unable to obtain the connection information for connection=${session.getConnectionConfig()}.\n Session will be closed.", + ProcessOutputType.STDOUT + ) + } + verify(exactly = 1) { + processHandler.destroyProcess() + } + assertSoftly { + session.unresponsive shouldBe true + } + } + should("should reconnect to the tso session after unsuccessful execution of the command") { // given clearMocks(processHandler, verificationMarks = true, recordedCalls = true) From 878be970caa938ec727eada6bebde53b9b7e1c63 Mon Sep 17 00:00:00 2001 From: Arseni Tsikhamirau Date: Tue, 24 Sep 2024 16:37:15 +0200 Subject: [PATCH 4/5] IJMP-1732-Working-sets-and-masks-deletions --- .../explorer/actions/DeleteJesNodeAction.kt | 52 ++--- .../explorer/ui/FileExplorerView.kt | 73 ++----- .../formainframe/utils/explorerUtils.kt | 82 +++++++ .../utils/ExplorerUtilsTestSpec.kt | 202 ++++++++++++++++++ 4 files changed, 315 insertions(+), 94 deletions(-) create mode 100644 src/main/kotlin/eu/ibagroup/formainframe/utils/explorerUtils.kt create mode 100644 src/test/kotlin/eu/ibagroup/formainframe/utils/ExplorerUtilsTestSpec.kt diff --git a/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/DeleteJesNodeAction.kt b/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/DeleteJesNodeAction.kt index 24430b823..ebc5ef639 100644 --- a/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/DeleteJesNodeAction.kt +++ b/src/main/kotlin/eu/ibagroup/formainframe/explorer/actions/DeleteJesNodeAction.kt @@ -20,10 +20,9 @@ import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.ui.showYesNoDialog import eu.ibagroup.formainframe.explorer.JesWorkingSetImpl -import eu.ibagroup.formainframe.explorer.ui.JesExplorerView -import eu.ibagroup.formainframe.explorer.ui.JesFilterNode -import eu.ibagroup.formainframe.explorer.ui.JesWsNode -import eu.ibagroup.formainframe.explorer.ui.getExplorerView +import eu.ibagroup.formainframe.explorer.ui.* +import eu.ibagroup.formainframe.utils.performUnitsDeletionBasedOnSelection +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty /** * Action class for delete JES node action (working set or filter) @@ -40,39 +39,18 @@ class DeleteJesNodeAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { val view = e.getExplorerView() ?: return val selected = view.mySelectedNodesData - // Delete selected JES working sets - selected - .map { it.node } - .filterIsInstance() - .forEach { - if ( - showYesNoDialog( - title = "Deletion of JES Working Set ${it.unit.name}", - message = "Do you want to delete this JES Working Set from configs? Note: all data under it will be untouched", - project = e.project, - icon = AllIcons.General.QuestionDialog - ) - ) { - view.explorer.disposeUnit(it.unit as JesWorkingSetImpl) - } - } - // Delete selected job filters - selected - .map { it.node } - .filterIsInstance() - .filter { view.explorer.isUnitPresented(it.unit) } - .forEach { - if ( - showYesNoDialog( - title = "Deletion Of Jobs Filter", - message = "Do you want to delete this jobs filter with ${it.value} from configs? Note: all data under the filter will be untouched", - project = e.project, - icon = AllIcons.General.QuestionDialog - ) - ) { - it.unit.removeFilter(it.value) - } - } + + // Find items to delete (working set node or filter node) + val suitableToDelete = selected.map { it.node } + .filter { node -> node is JesWsNode || node is JesFilterNode } + + val (workingSetsToDelete, selectedFilters) = suitableToDelete.partition { node -> node is JesWsNode } + val jesFiltersToDelete = selectedFilters.filter { jesFilter -> !workingSetsToDelete.contains(jesFilter.parent) } + + // Delete working sets and filters that do not belong to them + (workingSetsToDelete + jesFiltersToDelete).ifNotEmpty { + performUnitsDeletionBasedOnSelection(e.project, null, view) + } } /** diff --git a/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/FileExplorerView.kt b/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/FileExplorerView.kt index cbc21de18..dc968289e 100644 --- a/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/FileExplorerView.kt +++ b/src/main/kotlin/eu/ibagroup/formainframe/explorer/ui/FileExplorerView.kt @@ -55,7 +55,9 @@ import eu.ibagroup.formainframe.explorer.FilesWorkingSet import eu.ibagroup.formainframe.telemetry.NotificationsService import eu.ibagroup.formainframe.utils.getMinimalCommonParents import eu.ibagroup.formainframe.utils.getParentsChain +import eu.ibagroup.formainframe.utils.performUnitsDeletionBasedOnSelection import eu.ibagroup.formainframe.vfs.MFVirtualFile +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import java.awt.Toolkit import java.awt.datatransfer.DataFlavor import java.awt.datatransfer.Transferable @@ -440,63 +442,20 @@ class FileExplorerView( /** Deletes files corresponding to the selected nodes data. */ override fun deleteElement(dataContext: DataContext) { val selected = mySelectedNodesData - selected.map { it.node } - .filterIsInstance() - .forEach { - if ( - showYesNoDialog( - title = "Deletion of Working Set ${it.unit.name}", - message = "Do you want to delete this Working Set from configs? Note: all data under it will be untouched", - project = project, - icon = AllIcons.General.QuestionDialog - ) - ) { - explorer.disposeUnit(it.unit as FilesWorkingSet) - } - } - selected.map { it.node } - .filterIsInstance() - .filter { explorer.isUnitPresented(it.unit) } - .forEach { - if ( - showYesNoDialog( - title = "Deletion of DS Mask ${it.value.mask}", - message = "Do you want to delete this mask from configs? Note: all data sets under it will be untouched", - project = project, - icon = AllIcons.General.QuestionDialog - ) - ) { - it.cleanCache( - recursively = true, - cleanFetchProviderCache = true, - cleanBatchedQuery = true, - sendTopic = false - ) - it.unit.removeMask(it.value) - } - } - selected.map { it.node } - .filterIsInstance() - .filter { it.isUssMask && explorer.isUnitPresented(it.unit) } - .forEach { - val node = it - if ( - showYesNoDialog( - title = "Deletion of Uss Path Root ${node.value.path}", - message = "Do you want to delete this USS path root from configs? Note: all files under it will be untouched", - project = project, - icon = AllIcons.General.QuestionDialog - ) - ) { - node.cleanCache( - recursively = true, - cleanFetchProviderCache = true, - cleanBatchedQuery = true, - sendTopic = false - ) - node.unit.removeUssPath(node.value) - } - } + + // Find items to delete (working set node or dataset mask node or USS mask node) + val suitableToDelete = selected.map { it.node } + .filter { node -> node is FilesWorkingSetNode || node is DSMaskNode || (node is UssDirNode && node.isUssMask) } + + val (workingSetsToDelete, selectedMasks) = suitableToDelete.partition { node -> node is FilesWorkingSetNode } + val masksToDelete = selectedMasks.filter { mask -> !workingSetsToDelete.contains(mask.parent) } + + // Delete working sets and masks that do not belong to them + (workingSetsToDelete + masksToDelete).ifNotEmpty { + performUnitsDeletionBasedOnSelection(project, this@FileExplorerView, null) + } + + // perform files deletion val nodeAndFilePairs = optimizeDeletion(selected) if (nodeAndFilePairs.isNotEmpty()) { val files = nodeAndFilePairs.map { it.second }.toSet().toList() diff --git a/src/main/kotlin/eu/ibagroup/formainframe/utils/explorerUtils.kt b/src/main/kotlin/eu/ibagroup/formainframe/utils/explorerUtils.kt new file mode 100644 index 000000000..a23e64d3f --- /dev/null +++ b/src/main/kotlin/eu/ibagroup/formainframe/utils/explorerUtils.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020-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 + * + * Copyright IBA Group 2020 + * Contributors: + * IBA Group + * Zowe Community + */ +package eu.ibagroup.formainframe.utils + +import com.intellij.icons.AllIcons +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.showYesNoDialog +import eu.ibagroup.formainframe.explorer.FilesWorkingSet +import eu.ibagroup.formainframe.explorer.JesWorkingSetImpl +import eu.ibagroup.formainframe.explorer.ui.* + +/** + * Function is used to perform units deletion based on current selection + * Function accepts only: + * Files Working Set nodes + * Dataset Mask nodes + * Uss Directory Root nodes (isUssMask = true) + * JES Working Set nodes + * JES Filter nodes + * @param project + * @param fileExplorerView + * @param jesExplorerView + * @receiver List of filtered nodes by type, e.g. List dsMasksToDelete + */ +fun > List.performUnitsDeletionBasedOnSelection( + project: Project?, + fileExplorerView: FileExplorerView?, + jesExplorerView: JesExplorerView? +) { + var unitTypes = when (first()) { + is FilesWorkingSetNode -> "File Working Set(s)" + is DSMaskNode -> "Dataset Mask(s)" + is UssDirNode -> "Uss Root Path(s)" + is JesWsNode -> "JES Working Set(s)" + else -> "Job Filter(s)" + } + // Change the header and body of the dialog in case both JesWsNode's and JesFilter's are present in receiver list + unitTypes = + if (this.filterIsInstance().isNotEmpty() && this.filterIsInstance().size != this.size) + "Jes Working Set(s) and Jes Filter(s)" + else unitTypes + + if (showYesNoDialog( + title = "Confirm $unitTypes Deletion", + message = "Do you want to delete selected $unitTypes from config? Note: all data under it(them) will be untouched", + project = project, + icon = AllIcons.General.QuestionDialog + ) + ) { + forEach { + when (val node: T = it) { + is FilesWorkingSetNode -> fileExplorerView?.explorer?.disposeUnit(node.unit as FilesWorkingSet) + is DSMaskNode, is UssDirNode -> { + (node as FileFetchNode<*,*,*,*,*,*>).cleanCache( + recursively = true, + cleanFetchProviderCache = true, + cleanBatchedQuery = true, + sendTopic = false + ) + } + } + when (val node: T = it) { + is DSMaskNode -> node.unit.removeMask(node.value) + is UssDirNode -> node.unit.removeUssPath(node.value) + is JesWsNode -> jesExplorerView?.explorer?.disposeUnit(node.unit as JesWorkingSetImpl) + is JesFilterNode -> node.unit.removeFilter(node.value) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/eu/ibagroup/formainframe/utils/ExplorerUtilsTestSpec.kt b/src/test/kotlin/eu/ibagroup/formainframe/utils/ExplorerUtilsTestSpec.kt new file mode 100644 index 000000000..156a4f0b8 --- /dev/null +++ b/src/test/kotlin/eu/ibagroup/formainframe/utils/ExplorerUtilsTestSpec.kt @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2020-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 + * + * Copyright IBA Group 2020 + * Contributors: + * IBA Group + * Zowe Community + */ + +package eu.ibagroup.formainframe.utils + +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import eu.ibagroup.formainframe.config.connect.ConnectionConfig +import eu.ibagroup.formainframe.config.ws.DSMask +import eu.ibagroup.formainframe.config.ws.JobsFilter +import eu.ibagroup.formainframe.config.ws.UssPath +import eu.ibagroup.formainframe.explorer.* +import eu.ibagroup.formainframe.explorer.ui.* +import eu.ibagroup.formainframe.testutils.WithApplicationShouldSpec +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.mockk.* +import javax.swing.Icon + +class ExplorerUtilsTestSpec : WithApplicationShouldSpec({ + + val filesWSSlot = slot() + val dsMaskSlot = slot() + val ussMaskSlot = slot() + val jobFilterSlot = slot() + val jesWSSlot = slot() + + afterSpec { + clearAllMocks() + unmockkAll() + } + + fun > T.doMockStubs() { + val filesWorkingSet = mockk() + val jesWorkingSet = mockk() + when (val node: T = this) { + is FilesWorkingSetNode -> { + every { node.unit } returns filesWorkingSet + every { filesWorkingSet.name } returns "DELETED_WORKING_SET" + } + is DSMaskNode -> { + val dsMask = mockk() + every { dsMask.mask } returns "DELETED_MASK" + every { node.unit } returns filesWorkingSet + every { node.value } returns dsMask + every { node.cleanCache(any(), any(), any(), any()) } just Runs + every { node.unit.removeMask(capture(dsMaskSlot)) } just Runs + } + is UssDirNode -> { + val ussMask = mockk() + every { ussMask.path } returns "DELETED_USS_MASK" + every { node.isUssMask } returns true + every { node.unit } returns filesWorkingSet + every { node.value } returns ussMask + every { node.cleanCache(any(), any(), any(), any()) } just Runs + every { node.unit.removeUssPath(capture(ussMaskSlot)) } just Runs + } + is JesFilterNode -> { + val jobsFilter = mockk() + every { jobsFilter.jobId } returns "DELETED_JOB_FILTER" + every { node.unit } returns jesWorkingSet + every { node.value } returns jobsFilter + every { node.unit.removeFilter(capture(jobFilterSlot)) } just Runs + } + is JesWsNode -> { + every { node.unit } returns jesWorkingSet + every { jesWorkingSet.name } returns "DELETED_JES_WORKING_SET" + } + } + } + + context("utils module: explorerUtils") { + + val fileExplorer = mockk>() + val jesExplorer = mockk>() + + // explorers + val fileExplorerView = mockk() + every { fileExplorerView.explorer } returns fileExplorer + every { fileExplorerView.explorer.disposeUnit(capture(filesWSSlot)) } just Runs + + val jesExplorerView = mockk() + every { jesExplorerView.explorer } returns jesExplorer + every { jesExplorerView.explorer.disposeUnit(capture(jesWSSlot)) } just Runs + + val project = mockk() + + // File explorer view + val fileWS = mockk() + val dsMask = mockk() + val rootDirNode = mockk() + + // Jes explorer view + val jesWS = mockk() + val jobFilter = mockk() + + // dialog stubs + mockkStatic(Messages::class) + every { + Messages.showYesNoDialog( + any() as Project?, any() as String, any() as String, any() as String, any() as String, any() as Icon? + ) + } returns Messages.YES + + should("perform working set unit deletion for the file explorer view") { + // given + fileWS.doMockStubs() + val listOfNodesToTest = mutableListOf(fileWS) + // when + listOfNodesToTest.performUnitsDeletionBasedOnSelection(project, fileExplorerView, jesExplorerView) + // then + assertSoftly { + filesWSSlot.captured.name shouldBe "DELETED_WORKING_SET" + } + } + + should("perform dataset mask deletion for the file explorer view") { + // given + dsMask.doMockStubs() + val listOfNodesToTest = mutableListOf(dsMask) + // when + listOfNodesToTest.performUnitsDeletionBasedOnSelection(project, fileExplorerView, jesExplorerView) + // then + assertSoftly { + dsMaskSlot.captured.mask shouldBe "DELETED_MASK" + } + } + + should("perform root uss dir deletion for the file explorer view") { + // given + rootDirNode.doMockStubs() + val listOfNodesToTest = mutableListOf(rootDirNode) + // when + listOfNodesToTest.performUnitsDeletionBasedOnSelection(project, fileExplorerView, jesExplorerView) + // then + assertSoftly { + ussMaskSlot.captured.path shouldBe "DELETED_USS_MASK" + } + } + + should("perform jes working set unit deletion for the jes explorer view") { + // given + jesWS.doMockStubs() + val listOfNodesToTest = mutableListOf(jesWS) + // when + listOfNodesToTest.performUnitsDeletionBasedOnSelection(project, fileExplorerView, jesExplorerView) + // then + assertSoftly { + jesWSSlot.captured.name shouldBe "DELETED_JES_WORKING_SET" + } + } + + should("perform jes filter deletion for the jes explorer view") { + // given + jobFilter.doMockStubs() + val listOfNodesToTest = mutableListOf(jobFilter) + // when + listOfNodesToTest.performUnitsDeletionBasedOnSelection(project, fileExplorerView, jesExplorerView) + // then + assertSoftly { + jobFilterSlot.captured.jobId shouldBe "DELETED_JOB_FILTER" + } + } + + should("perform jes WS and jes filter(not included in jes WS to be deleted) deletion for the jes explorer view") { + // given + // clear static mock to be able to capture only the last invocation + mockkStatic(Messages::class) + every { + Messages.showYesNoDialog( + any() as Project?, any() as String, any() as String, any() as String, any() as String, any() as Icon? + ) + } returns Messages.YES + + jobFilter.doMockStubs() + jesWS.doMockStubs() + val messageHeader = slot() + val listOfNodesToTest = mutableListOf(jesWS, jobFilter) + // when + listOfNodesToTest.performUnitsDeletionBasedOnSelection(project, fileExplorerView, jesExplorerView) + // then + verify { Messages.showYesNoDialog(any() as Project?, any() as String, capture(messageHeader), any() as String, any() as String, any() as Icon?) } + assertSoftly { + jesWSSlot.captured.name shouldBe "DELETED_JES_WORKING_SET" + jobFilterSlot.captured.jobId shouldBe "DELETED_JOB_FILTER" + messageHeader.captured shouldBe "Confirm Jes Working Set(s) and Jes Filter(s) Deletion" + } + } + } +}) From bcb2b20bac65d3f19d0f6baa97dd923ff1e85021 Mon Sep 17 00:00:00 2001 From: Uladzislau Date: Thu, 26 Sep 2024 11:07:18 +0200 Subject: [PATCH 5/5] Version update Signed-off-by: Uladzislau --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c4c25b97a..9316c9fa7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ # org.gradle.jvmargs=-Xss1M # SemVer format -> https://semver.org -pluginVersion=2.0.0 +pluginVersion=2.1.0 pluginGroup=eu.ibagroup pluginRepositoryUrl=https://github.com/for-mainframe/For-Mainframe # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html