diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24abb5a9..60c45372 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ concurrency: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: matrix: product: ["IC-231","IC-233","IC-242","IC-243"] @@ -41,6 +41,9 @@ jobs: pluginVerifierHomeDir: ${{ steps.properties.outputs.pluginVerifierHomeDir }} steps: + - name: Install libsecret-tools + run: sudo apt-get install -y libsecret-tools + - name: Checkout the plugin GitHub repository uses: actions/checkout@v4 @@ -101,7 +104,7 @@ jobs: test_and_sonar: needs: [build] - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: matrix: product: ["IC-231","IC-233","IC-242","IC-243"] @@ -122,6 +125,9 @@ jobs: fail-fast: false steps: + - name: Install libsecret-tools + run: sudo apt-get install -y libsecret-tools + - name: Checkout the plugin GitHub repository uses: actions/checkout@v4 @@ -145,7 +151,7 @@ jobs: - name: Publish tests result to artifacts uses: actions/upload-artifact@v4 with: - name: tests-summary-success-report-${{ matrix.product }} + name: tests-report-${{ matrix.product }} path: ${{ github.workspace }}/build/reports/tests - name: Publish code coverage report to artifacts @@ -174,7 +180,7 @@ jobs: verify: if: ${{ contains(github.ref, 'refs/heads/release/') || contains(github.ref, 'refs/heads/main') }} needs: [build] - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: matrix: product: ["IC-231","IC-233","IC-242","IC-243"] @@ -201,6 +207,9 @@ jobs: tool-cache: false large-packages: false + - name: Install libsecret-tools + run: sudo apt-get install -y libsecret-tools + - name: Checkout the plugin GitHub repository uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41f88a5d..738da6e8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,11 +13,14 @@ on: jobs: changelog-and-preparations: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: + + - name: Install libsecret-tools + run: sudo apt-get install -y libsecret-tools - name: Checkout the plugin GitHub repository uses: actions/checkout@v4 @@ -108,7 +111,7 @@ jobs: release: needs: [changelog-and-preparations] - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest permissions: contents: write strategy: @@ -131,6 +134,9 @@ jobs: fail-fast: false steps: + - name: Install libsecret-tools + run: sudo apt-get install -y libsecret-tools + - name: Checkout the plugin GitHub repository uses: actions/checkout@v4 diff --git a/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt b/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt index e0af71dd..917f4b48 100755 --- a/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt +++ b/src/main/kotlin/org/zowe/explorer/utils/miscUtils.kt @@ -31,6 +31,7 @@ import org.zowe.explorer.explorer.WorkingSet import org.zowe.explorer.explorer.ui.ExplorerTreeView import org.zowe.explorer.explorer.ui.ExplorerUnitTreeNodeBase import java.awt.Dimension +import java.io.InputStream import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter @@ -398,3 +399,14 @@ fun createHelpButton(): JButton { fun Presentation.addTooltip(tooltipText: String) { this.putClientProperty(Key(JComponent.TOOL_TIP_TEXT_KEY), tooltipText) } + +/** + * Function returns an input stream for reading the specified resource. + * This function is required for testing purposes + * @param classLoader - current ClassLoader + * @param strPath - the resource path + * @return fn input stream for reading the resource; null if the resource could not be found + */ +fun getResourceAsStreamWrappable(classLoader: ClassLoader, strPath: String): InputStream? { + return classLoader.getResourceAsStream(strPath) +} diff --git a/src/main/kotlin/org/zowe/explorer/zowe/ZoweStartupActivity.kt b/src/main/kotlin/org/zowe/explorer/zowe/ZoweStartupActivity.kt index e640735e..a2a24f48 100644 --- a/src/main/kotlin/org/zowe/explorer/zowe/ZoweStartupActivity.kt +++ b/src/main/kotlin/org/zowe/explorer/zowe/ZoweStartupActivity.kt @@ -40,9 +40,7 @@ const val ZOWE_CONFIG_NAME = "zowe.config.json" */ fun showNotificationForAddUpdateZoweConfigIfNeeded(project: Project, type: ZoweConfigType) { val zoweConfigService = ZoweConfigService.getInstance(project) - val zoweConfigState = zoweConfigService.getZoweConfigState(type = type) - - if (zoweConfigState == ZoweConfigState.NEED_TO_ADD) { + if (zoweConfigService.findAllZosmfExistingConnection(type).isEmpty()) { val topic = if (type == ZoweConfigType.LOCAL) LOCAL_ZOWE_CONFIG_CHANGED else @@ -61,7 +59,7 @@ fun showNotificationForAddUpdateZoweConfigIfNeeded(project: Project, type: ZoweC addAction(object : DumbAwareAction("Add $type Zowe Connection") { override fun actionPerformed(e: AnActionEvent) { ZoweConfigService.getInstance(project) - .addOrUpdateZoweConfig(false, true, type) + .addOrUpdateZoweConfig(true, true, type) hideBalloon() } }).notify(project) @@ -76,8 +74,7 @@ fun showNotificationForAddUpdateZoweConfigIfNeeded(project: Project, type: ZoweC */ fun showDialogForDeleteZoweConfigIfNeeded(project: Project, type: ZoweConfigType) { val zoweConfigService = ZoweConfigService.getInstance(project) - val zoweConfigState = zoweConfigService.getZoweConfigState(type = type) - if (zoweConfigState != ZoweConfigState.NEED_TO_ADD && zoweConfigState != ZoweConfigState.NOT_EXISTS) { + if (zoweConfigService.findAllZosmfExistingConnection(type).isNotEmpty()) { val choice = Messages.showDialog( project, "$type Zowe config file has been deleted.\n" + diff --git a/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigService.kt b/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigService.kt index e0efd024..3275426e 100644 --- a/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigService.kt +++ b/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigService.kt @@ -105,6 +105,12 @@ interface ZoweConfigService { */ fun checkAndRemoveOldZoweConnection(type: ZoweConfigType) + /** + * Finds All existing connections related to zowe.config.json. + * @return list of found ConnectionConfigs related to zowe config if it exists or null otherwise. + */ + fun findAllZosmfExistingConnection(type: ZoweConfigType): List + companion object { fun getInstance(project: Project): ZoweConfigService = project.getService(ZoweConfigService::class.java) val lock = ReentrantReadWriteLock() diff --git a/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt b/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt index e2695f90..5360dc13 100644 --- a/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt +++ b/src/main/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImpl.kt @@ -36,13 +36,11 @@ import org.zowe.explorer.dataops.DataOpsManager import org.zowe.explorer.dataops.operations.InfoOperation import org.zowe.explorer.dataops.operations.ZOSInfoOperation import org.zowe.explorer.explorer.EXPLORER_NOTIFICATION_GROUP_ID +import org.zowe.explorer.telemetry.NotificationCompatibleException import org.zowe.explorer.telemetry.NotificationsService +import org.zowe.explorer.utils.* import org.zowe.explorer.utils.crudable.find import org.zowe.explorer.utils.crudable.getAll -import org.zowe.explorer.utils.runTask -import org.zowe.explorer.utils.sendTopic -import org.zowe.explorer.utils.toMutableList -import org.zowe.explorer.utils.write import org.zowe.explorer.zowe.ZOWE_CONFIG_NAME import org.zowe.explorer.zowe.service.ZoweConfigService.Companion.lock import org.zowe.kotlinsdk.annotations.ZVersion @@ -51,7 +49,6 @@ import org.zowe.kotlinsdk.zowe.config.ZoweConfig import org.zowe.kotlinsdk.zowe.config.parseConfigJson import java.io.File import java.io.FileOutputStream -import java.io.InputStream import java.nio.charset.Charset import java.nio.charset.StandardCharsets import java.nio.file.Files @@ -75,38 +72,79 @@ const val ZOWE_PROJECT_PREFIX = "zowe-" class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService { companion object { + private fun produceRareErrorNotification() { + NotificationsService.errorNotification( + NotificationCompatibleException( + "Error during Zowe Config handling", + "Project must not be null during Zowe Config local file recognition", + "This is likely a rarely occurring situation, please, contact Zowe Support in case you encountered it" + ) + ) + } + /** - * This function is required for testing purposes + * Returns base Zowe connection name */ - private fun getResourceStream(strPath: String): InputStream? { - return ZoweConfigServiceImpl::class.java.classLoader?.getResourceAsStream(strPath) + private fun getBaseZoweConnectionName( + myProject: Project?, + type: ZoweConfigType, + profileName: String = "zosmf" + ): String { + return if (type == ZoweConfigType.LOCAL) { + if (myProject == null) { + produceRareErrorNotification() + "" + } else { + "${ZOWE_PROJECT_PREFIX}${type}-${profileName}/${myProject.name}" + } + } else { + "${ZOWE_PROJECT_PREFIX}${type}-${profileName}" + } } /** * Returns Zowe connection name */ fun getZoweConnectionName(myProject: Project?, type: ZoweConfigType, profileName: String = "zosmf"): String { - return if (type == ZoweConfigType.LOCAL) - "${ZOWE_PROJECT_PREFIX}${type}-${profileName}/${myProject?.name}" - else - "${ZOWE_PROJECT_PREFIX}${type}-${profileName}" + val configCrudable = ConfigService.getService().crudable + val allConnections = configCrudable.getAll().toList() + val allConnectionsNames: MutableList = allConnections.map { it.name }.toMutableList() + val connectionNameBase = getBaseZoweConnectionName(myProject, type, profileName) + var connectionName = connectionNameBase + + var index = 1 + while (allConnectionsNames.contains(connectionName)) { + connectionName = connectionNameBase.plus(index.toString()) + index++ + } + return connectionName } /** * Returns path to Zowe configuration file */ fun getZoweConfigLocation(myProject: Project?, type: ZoweConfigType): String { - return if (type == ZoweConfigType.LOCAL) - "${myProject?.basePath}/$ZOWE_CONFIG_NAME" - else + return if (type == ZoweConfigType.LOCAL) { + if (myProject == null) { + produceRareErrorNotification() + "" + } else { + "${myProject.basePath}/$ZOWE_CONFIG_NAME" + } + } else { System.getProperty("user.home").replace("((\\*)|(/*))$", "") + "/.zowe/" + ZOWE_CONFIG_NAME + } } /** * Returns profile name for current connection */ fun getProfileNameFromConnName(connName: String): String { - var profileName = connName.replace(Regex("^$ZOWE_PROJECT_PREFIX((local)|(global))+(-)"), "") + var profileName = connName + .replace( + Regex("^$ZOWE_PROJECT_PREFIX(${ZoweConfigType.LOCAL}|${ZoweConfigType.GLOBAL})+(-)"), + "" + ) profileName = profileName.replace(Regex("/.*$"), "") return profileName } @@ -148,20 +186,20 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService } /** - * Finds A/ll existing connections related to zowe.config.json. - * @return list of found ConnectionConfigs related to zowe config if it exists or null otherwise. + * @see ZoweConfigService.findAllZosmfExistingConnection */ - private fun findAllZosmfExistingConnection(type: ZoweConfigType): List? { - val zoweConnectionList = configCrudable.find { - val pattern = - if (type == ZoweConfigType.LOCAL) { - Regex("^(" + ZOWE_PROJECT_PREFIX + type + "-).*(/" + myProject.name + ")$") - } else { - Regex("^(" + ZOWE_PROJECT_PREFIX + type + "-).*") - } - it.name.matches(pattern) && it.zoweConfigPath == getZoweConfigLocation(myProject, type) - }.collect(Collectors.toList()) - return if (zoweConnectionList.isEmpty()) null else zoweConnectionList + override fun findAllZosmfExistingConnection(type: ZoweConfigType): List { + return configCrudable + .find { + val pattern = + if (type == ZoweConfigType.LOCAL) { + Regex("^(" + ZOWE_PROJECT_PREFIX + type + "-).*(/" + myProject.name + ")\\d*$") + } else { + Regex("^($ZOWE_PROJECT_PREFIX$type-).*") + } + it.name.matches(pattern) && it.zoweConfigPath == getZoweConfigLocation(myProject, type) + } + .collect(Collectors.toList()) } /** @@ -169,22 +207,15 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService * @return ConnectionConfig instance related to zowe config if it exists or null otherwise. */ private fun findExistingConnection(type: ZoweConfigType, profileName: String): ConnectionConfig? { - val zoweConnectionList = configCrudable + return configCrudable .find { - it.name == getZoweConnectionName(myProject, type, profileName) + it.name.startsWith( + getBaseZoweConnectionName(myProject, type, profileName) + ) && it.zoweConfigPath == getZoweConfigLocation(myProject, type) } .collect(Collectors.toList()) - return if (zoweConnectionList.isEmpty()) null else zoweConnectionList[0] - } - - /** - * Check if connection config related to zowe config is presented - * then extracts existing uuid and generates a new one otherwise. - * @return created or existing uuid. - */ - private fun getOrCreateUuid(type: ZoweConfigType, profileName: String): String { - return findExistingConnection(type, profileName)?.uuid ?: UUID.randomUUID().toString() + .firstOrNull() } /** @@ -194,9 +225,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService * @return Nothing. */ private fun notifyUiOnConnectionFailure(title: String, content: String, type: ZoweConfigType) { - NotificationGroupManager - .getInstance() - .getNotificationGroup(EXPLORER_NOTIFICATION_GROUP_ID) + NotificationGroupManager.getInstance().getNotificationGroup(EXPLORER_NOTIFICATION_GROUP_ID) .createNotification(title, content, NotificationType.ERROR) .apply { addAction(object : DumbAwareAction("Add Anyway") { @@ -220,15 +249,16 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService val throwable = runTask("Testing Connection to ${zoweConnection.url}", myProject) { indicator -> return@runTask try { runCatching { - service().performOperation(InfoOperation(zoweConnection), indicator) + DataOpsManager.getService().performOperation(InfoOperation(zoweConnection), indicator) }.onSuccess { indicator.text = "Retrieving z/OS information" - val systemInfo = service().performOperation(ZOSInfoOperation(zoweConnection), indicator) + val systemInfo = DataOpsManager.getService().performOperation(ZOSInfoOperation(zoweConnection), indicator) zoweConnection.zVersion = when (systemInfo.zosVersion) { "04.25.00" -> ZVersion.ZOS_2_2 "04.26.00" -> ZVersion.ZOS_2_3 "04.27.00" -> ZVersion.ZOS_2_4 "04.28.00" -> ZVersion.ZOS_2_5 + "04.29.00" -> ZVersion.ZOS_3_1 else -> ZVersion.ZOS_2_1 } }.onSuccess { @@ -251,7 +281,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService * @see ZoweConfigService.addOrUpdateZoweConfig */ override fun addOrUpdateZoweConfig(scanProject: Boolean, checkConnection: Boolean, type: ZoweConfigType) { - return try { + try { val zoweConfig = if (scanProject) { scanForZoweConfig(type) } else if (type == ZoweConfigType.LOCAL) @@ -265,7 +295,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService val andMore = if (failedConnections.size > 3) "..." else "" notifyUiOnConnectionFailure( "Connection failed to:", - "${failedConnections.map{it.url}.joinToString(separator = ",

")} $andMore", + "${failedConnections.joinToString(separator = ",

") { it.url }} $andMore", type ) } @@ -283,6 +313,7 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService sendTopic(topic).onConfigSaved(zoweConfig, zosmfConnection) } } + } catch (e: Exception) { NotificationsService.errorNotification(e, project = myProject, custTitle="Error with Zowe config file") } @@ -298,7 +329,10 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService val username = zosmfConnection.user val password = zosmfConnection.password val zoweConnection = findExistingConnection(type, zosmfConnection.profileName) - ?.let { zosmfConnection.toConnectionConfig(it.uuid, it.zVersion, type = type) } + ?.let { oldConn -> + zosmfConnection.toConnectionConfig(oldConn.uuid, oldConn.zVersion, type = type) + .also { newConn -> newConn.name = oldConn.name } + } ?: zosmfConnection.toConnectionConfig(UUID.randomUUID().toString(), type = type) CredentialService.getService().setCredentials(zoweConnection.uuid, username, password.toCharArray()) return zoweConnection @@ -335,7 +369,8 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService override fun deleteZoweConfig(type: ZoweConfigType) { try { - val zoweConnections = findAllZosmfExistingConnection(type) ?: throw Exception("Cannot find any zosmf connections") + val zoweConnections = findAllZosmfExistingConnection(type) + if (zoweConnections.isEmpty()) throw Exception("Cannot find any z/OSMF connections") zoweConnections.forEach { zoweConnection -> @@ -392,14 +427,14 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService port = matcher.group(3).substring(1) } - val content = getResourceStream("files/${ZOWE_CONFIG_NAME}") + val content = getResourceAsStreamWrappable(ZoweConfigServiceImpl::class.java.classLoader ,"files/${ZOWE_CONFIG_NAME}") .use { iS -> iS?.readAllBytes()?.let { String(it, charset) } } ?.replace("".toRegex(), port) ?.replace("".toRegex(), "\"$host\"") ?.replace("".toRegex(), (!state.isAllowSsl).toString()) ?.toByteArray(charset) ?: throw Exception("$ZOWE_CONFIG_NAME is not found") - Files.write(Paths.get(jsonFileName), content) + saveChangedZoweConfig(jsonFileName, content) runWriteAction { val configCredentialsMap = mutableMapOf() @@ -434,11 +469,16 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService val schemaFile = File(schemaFileName) if (!schemaFile.exists()) { FileOutputStream(schemaFile, false).use { fOS -> - getResourceStream("files/zowe.schema.json").use { iS -> iS?.transferTo(fOS) } + getResourceAsStreamWrappable(ZoweConfigServiceImpl::class.java.classLoader,"files/zowe.schema.json").use { iS -> iS?.transferTo(fOS) } } } } + /** Write changes of Zowe config by the [jsonFileName] with the changed [content] */ + private fun saveChangedZoweConfig(jsonFileName: String, content: ByteArray) { + Files.write(Paths.get(jsonFileName), content) + } + /** * Converts ZoweConfig to ConnectionConfig. * @param uuid - uuid returned connection. @@ -478,50 +518,34 @@ class ZoweConfigServiceImpl(override val myProject: Project) : ZoweConfigService else globalZoweConfig ?: return ZoweConfigState.NOT_EXISTS - findAllZosmfExistingConnection(type) ?: return ZoweConfigState.NEED_TO_ADD + if (findAllZosmfExistingConnection(type).isEmpty()) return ZoweConfigState.NEED_TO_ADD - return zoweConfig - .getListOfZosmfConections() + val zoweConfigZosmfConnections = zoweConfig.getListOfZosmfConections() + return zoweConfigZosmfConnections .fold(ZoweConfigState.SYNCHRONIZED) { prevZoweConfigState, zosConnection -> val existingConnection = findExistingConnection(type, zosConnection.profileName) val currZoweConfigState = if (existingConnection == null) { ZoweConfigState.NEED_TO_ADD } else { - val newConnectionList = zoweConfig.getListOfZosmfConections() - .filter { it.profileName == getProfileNameFromConnName(existingConnection.name) } - if (newConnectionList.isNotEmpty()) { - val newConnection = newConnectionList[0].toConnectionConfig( - existingConnection.uuid, existingConnection.zVersion, existingConnection.owner, type = type - ) - val zoweUsername = zosConnection.user - val zowePassword = zosConnection.password - if ( - existingConnection == newConnection - && CredentialService.getUsername(newConnection) == zoweUsername - && CredentialService.getPassword(newConnection).contentEquals(zowePassword.toCharArray()) - ) { - ZoweConfigState.SYNCHRONIZED - } else { - ZoweConfigState.NEED_TO_UPDATE - } + val updatedConnection = zosConnection.toConnectionConfig( + existingConnection.uuid, existingConnection.zVersion, existingConnection.owner, type = type + ) + updatedConnection.name = existingConnection.name + val zoweUsername = zosConnection.user + val zowePassword = zosConnection.password + if ( + existingConnection == updatedConnection + && CredentialService.getUsername(updatedConnection) == zoweUsername + && CredentialService.getPassword(updatedConnection).contentEquals(zowePassword.toCharArray()) + ) { + ZoweConfigState.SYNCHRONIZED } else { - ZoweConfigState.NEED_TO_ADD + ZoweConfigState.NEED_TO_UPDATE } } - setZoweConfigState(prevZoweConfigState, currZoweConfigState) + if (prevZoweConfigState == ZoweConfigState.ERROR || currZoweConfigState == ZoweConfigState.SYNCHRONIZED) + prevZoweConfigState + else currZoweConfigState } } - - /** - * Returns the resulting ZoweConfigState depending on the previous and new ones - * @param prev previous state - * @param curr new state - * @return resulting ZoweConfigState - */ - private fun setZoweConfigState(prev: ZoweConfigState, curr: ZoweConfigState): ZoweConfigState { - return if (prev == ZoweConfigState.ERROR || curr == ZoweConfigState.SYNCHRONIZED) - prev - else curr - } - } diff --git a/src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt b/src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt deleted file mode 100644 index a3d8b405..00000000 --- a/src/test/kotlin/org/zowe/explorer/config/ZoweConfigTestSpec.kt +++ /dev/null @@ -1,703 +0,0 @@ -/* - * 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 - * - * Contributors: - * IBA Group - * Zowe Community - */ - -package org.zowe.explorer.config - -import com.intellij.notification.Notification -import com.intellij.notification.Notifications -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.runWriteAction -import com.intellij.openapi.progress.ProgressIndicator -import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.VirtualFileManager -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import io.kotest.matchers.string.shouldContain -import io.mockk.* -import org.zowe.explorer.config.connect.ConnectionConfig -import org.zowe.explorer.config.connect.ui.zosmf.ConnectionDialogState -import org.zowe.explorer.config.connect.whoAmI -import org.zowe.explorer.config.ws.FilesWorkingSetConfig -import org.zowe.explorer.config.ws.JesWorkingSetConfig -import org.zowe.explorer.dataops.DataOpsManager -import org.zowe.explorer.dataops.Operation -import org.zowe.explorer.dataops.operations.InfoOperation -import org.zowe.explorer.dataops.operations.ZOSInfoOperation -import org.zowe.explorer.explorer.Explorer -import org.zowe.explorer.explorer.WorkingSet -import org.zowe.explorer.telemetry.NotificationsService -import org.zowe.explorer.testutils.WithApplicationShouldSpec -import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl -import org.zowe.explorer.testutils.testServiceImpl.TestNotificationsServiceImpl -import org.zowe.explorer.utils.crudable.* -import org.zowe.explorer.utils.validateForBlank -import org.zowe.explorer.zowe.ZOWE_CONFIG_NAME -import org.zowe.explorer.zowe.service.ZoweConfigServiceImpl -import org.zowe.explorer.zowe.service.ZoweConfigServiceImpl.Companion.getZoweConfigLocation -import org.zowe.explorer.zowe.service.ZoweConfigServiceImpl.Companion.getZoweConnectionName -import org.zowe.explorer.zowe.service.ZoweConfigState -import org.zowe.explorer.zowe.service.ZoweConfigType -import org.zowe.kotlinsdk.InfoResponse -import org.zowe.kotlinsdk.SystemsResponse -import org.zowe.kotlinsdk.annotations.ZVersion -import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection -import org.zowe.kotlinsdk.zowe.config.KeytarWrapper -import org.zowe.kotlinsdk.zowe.config.ZoweConfig -import org.zowe.kotlinsdk.zowe.config.parseConfigJson -import java.io.InputStream -import java.nio.file.Path -import java.util.* -import java.util.stream.Stream -import javax.swing.JPasswordField -import javax.swing.JTextField -import kotlin.reflect.KFunction -import kotlin.reflect.full.declaredMemberFunctions -import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.jvm.isAccessible -import kotlin.reflect.jvm.javaField - -class ZoweConfigTestSpec : WithApplicationShouldSpec({ - val tmpZoweConfFile = "test/$ZOWE_CONFIG_NAME" - val connectionDialogState = ConnectionDialogState( - connectionName = "a", - connectionUrl = "https://111.111.111.111:555", - username = "testUser", - password = "testPass".toCharArray(), - isAllowSsl = true - ) - - afterSpec { - clearAllMocks() - unmockkAll() - } - - context("config module: zowe config file") { - var isFilesWriteTriggered = false - var isRunWriteActionCalled = false - var isSaveNewSecurePropertiesCalled = false - var isFindFileByNioPathCalled = false - var isInputStreamCalled = false - var isReturnedZoweConfig = false - var isAddOrUpdateConnectionCalled = false - var isZOSInfoCalled = false - var isScanForZoweConfigCalled = false - var isConnectionDeleted = false - var notified = false - - val mockedProject = mockk(relaxed = true) - every { mockedProject.basePath } returns "test" - every { mockedProject.name } returns "testProj" - - val connectionId = "000000000000" - val connection = ConnectionConfig( - "ID$connectionId", connectionId, "URL$connectionId", true, ZVersion.ZOS_2_4, zoweConfigPath = "/zowe/config/path" - ) - val crudableMockk = mockk() - every { crudableMockk.getAll() } returns Stream.of() - every { crudableMockk.getAll() } returns Stream.of() - every { crudableMockk.getAll() } returns Stream.of() - mockkObject(ConfigService) - every { ConfigService.getService().crudable } returns crudableMockk - every { crudableMockk.find(any(), any()) } answers { - listOf(connection).stream() - } - every { crudableMockk.delete(any()) } answers { - isConnectionDeleted = true - Optional.of(ConnectionConfig()) - } - every { crudableMockk.addOrUpdate(any()) } answers { - isAddOrUpdateConnectionCalled = true - Optional.of(ConnectionConfig()) - } - - val notificationsService = NotificationsService.getService() as TestNotificationsServiceImpl - notificationsService.testInstance = object : TestNotificationsServiceImpl() { - override fun notifyError( - t: Throwable, - project: Project?, - custTitle: String?, - custDetailsShort: String?, - custDetailsLong: String? - ) { - if (custTitle == "Error with Zowe config file") { - notified = true - } - } - } - - afterEach { - isFilesWriteTriggered = false - isRunWriteActionCalled = false - isSaveNewSecurePropertiesCalled = false - isFindFileByNioPathCalled = false - isInputStreamCalled = false - isReturnedZoweConfig = false - isAddOrUpdateConnectionCalled = false - isZOSInfoCalled = false - isScanForZoweConfigCalled = false - isConnectionDeleted = false - notified = false - } - - val mockedNotification: ( - Notification - ) -> Unit = Notifications.Bus::notify - mockkStatic(mockedNotification as KFunction<*>) - every { - mockedNotification( - any() - ) - } answers { - notified = true - } - - should("getResourceStream") { - ZoweConfigServiceImpl.Companion::class.declaredMemberFunctions.find { it.name == "getResourceStream" }?.let { - it.isAccessible = true - it.call(ZoweConfigServiceImpl, "test") shouldBe null - } - } - - val mockedZoweConfigService = spyk(ZoweConfigServiceImpl(mockedProject), recordPrivateCalls = true) - every { mockedZoweConfigService["createZoweSchemaJsonIfNotExists"]() } returns Unit - - val mockedZoweConfigInputStream = mockk() - every { mockedZoweConfigInputStream.readAllBytes() } returns ":;SSL".toByteArray() - every { mockedZoweConfigInputStream.close() } returns Unit - - mockkObject(ZoweConfigServiceImpl, recordPrivateCalls = true) - every { ZoweConfigServiceImpl["getResourceStream"](any()) } returns mockedZoweConfigInputStream - - val mockedRunWriteAction: KFunction = ::runWriteAction - mockkStatic(mockedRunWriteAction) - every { - mockedRunWriteAction.call(any<() -> Unit>()) - } answers { - isRunWriteActionCalled = true - firstArg<() -> Unit>().invoke() - } - - mockkObject(ZoweConfig) - every { - ZoweConfig.saveNewSecureProperties(any(), any>(), any()) - } answers { - isSaveNewSecurePropertiesCalled = true - } - - val confMap = mutableMapOf>() - val configCredentialsMap = mutableMapOf() - configCredentialsMap["profiles.base.properties.user"] = "testUser" - configCredentialsMap["profiles.base.properties.password"] = "testPass" - every { ZoweConfig.Companion["readZoweCredentialsFromStorage"](any()) } returns confMap - - val zoweConnConf = connectionDialogState.connectionConfig - zoweConnConf.zoweConfigPath = tmpZoweConfFile.replace("\\", "/") - zoweConnConf.name = "zowe-testProj" - - val vfMock = mockk() - val isMock = mockk() - val vfmMock: VirtualFileManager = mockk() - mockkStatic(VirtualFileManager::class) - every { VirtualFileManager.getInstance() } returns vfmMock - every { vfmMock.findFileByNioPath(any()) } answers { - isFindFileByNioPathCalled = true - vfMock - } - every { vfMock.path } returns "/zowe/file/path" - every { vfMock.inputStream } answers { - isInputStreamCalled = true - isMock - } - every { isMock.close() } just Runs - val zoweConfigMock = mockk() - every { zoweConfigMock.extractSecureProperties(any>(), any()) } answers { - isScanForZoweConfigCalled = true - Unit - } - every { zoweConfigMock.user } returns "ZoweUserName" - every { zoweConfigMock.password } returns "ZoweUserPass" - every { zoweConfigMock.basePath } returns "/base/config/path" - every { zoweConfigMock.port } returns 555 - every { zoweConfigMock.host } returns "111.111.111.111" - every { zoweConfigMock.protocol } returns "https" - every { zoweConfigMock.rejectUnauthorized } returns null - val zossConn1 = ZOSConnection( - "111.111.111.111", - "10443", - "testUser", - "testPassword", - rejectUnauthorized = true, - basePath = "/base/config/path/", - profileName = "zosmf" - ) - val zosConnList1 = mutableListOf(zossConn1) - every { zoweConfigMock.getListOfZosmfConections() } returns zosConnList1 - - val parseConfigJsonFun: (InputStream) -> ZoweConfig = ::parseConfigJson - mockkStatic(parseConfigJsonFun as KFunction<*>) - every { parseConfigJson(any()) } answers { - isReturnedZoweConfig = true - zoweConfigMock - } - - val explorerMock = mockk>>() - every { explorerMock.componentManager } returns ApplicationManager.getApplication() - var infoRes = InfoResponse(zosVersion = "04.27.00") - val dataOpsManagerService = DataOpsManager.getService() as TestDataOpsManagerImpl - dataOpsManagerService.testInstance = object : TestDataOpsManagerImpl() { - override fun performOperation(operation: Operation, progressIndicator: ProgressIndicator): R { - if (operation is InfoOperation) { - if (infoRes.zosVersion == "throw1") { - throw Throwable("Test performInfoOperation throw") - } - @Suppress("UNCHECKED_CAST") return SystemsResponse(numRows = 1) as R - } - if (operation is ZOSInfoOperation) { - isZOSInfoCalled = true - if (infoRes.zosVersion == "throw2") { - throw Throwable("Test performOperation throw") - } - @Suppress("UNCHECKED_CAST") return infoRes as R - } - @Suppress("UNCHECKED_CAST") return InfoResponse() as R - } - } - - mockkStatic(::whoAmI as KFunction<*>) - every { whoAmI(any()) } returns "USERID" - - should("getZoweConnectionName") { - getZoweConnectionName(mockedProject, ZoweConfigType.LOCAL) shouldBe "zowe-local-zosmf/testProj" - getZoweConnectionName(mockedProject, ZoweConfigType.GLOBAL) shouldBe "zowe-global-zosmf" - getZoweConnectionName(null, ZoweConfigType.GLOBAL) shouldBe "zowe-global-zosmf" - getZoweConnectionName(null, ZoweConfigType.LOCAL) shouldBe "zowe-local-zosmf/null" - } - - should("getZoweConfigLocation") { - getZoweConfigLocation(mockedProject, ZoweConfigType.LOCAL) shouldBe "test/zowe.config.json" - getZoweConfigLocation( - mockedProject, ZoweConfigType.GLOBAL - ) shouldBe System.getProperty("user.home").replace("((\\*)|(/*))$", "") + "/.zowe/" + ZOWE_CONFIG_NAME - getZoweConfigLocation(null, ZoweConfigType.LOCAL) shouldBe "null/zowe.config.json" - } - - should("notifyError") { - mockedZoweConfigService::class.declaredMemberFunctions.find { it.name == "notifyError" }?.let { - it.isAccessible = true - it.call(mockedZoweConfigService, Throwable("Test throwable"), "test title") - it.call(mockedZoweConfigService, Throwable("Test throwable"), null) - it.call(mockedZoweConfigService, Throwable(), null) - notified shouldBe true - } - } - - // TODO: fix test -// should("add zowe team config file") { -// mockkStatic(Files::class) { -// every { Files.write(any(), any()) } answers { -// isFilesWriteTriggered = true -// Path.of("") -// } -// mockedZoweConfigService.addZoweConfigFile(connectionDialogState) -// } -// isFilesWriteTriggered shouldBe true -// isRunWriteActionCalled shouldBe true -// isSaveNewSecurePropertiesCalled shouldBe true -// } - - should("add zowe team config connection") { - mockedZoweConfigService.addOrUpdateZoweConfig( - scanProject = true, - checkConnection = true, - type = ZoweConfigType.LOCAL - ) - isFindFileByNioPathCalled shouldBe true - isInputStreamCalled shouldBe true - isReturnedZoweConfig shouldBe true - isScanForZoweConfigCalled shouldBe true - isAddOrUpdateConnectionCalled shouldBe true - isZOSInfoCalled shouldBe true - } - - should("try to update zowe team config connection and throw") { - mockedZoweConfigService.addOrUpdateZoweConfig( - scanProject = true, checkConnection = false, type = ZoweConfigType.LOCAL - ) - isFindFileByNioPathCalled shouldBe true - isInputStreamCalled shouldBe true - isReturnedZoweConfig shouldBe true - isScanForZoweConfigCalled shouldBe true - } - - should("delete zowe team config connection") { - mockedZoweConfigService.deleteZoweConfig(type = ZoweConfigType.LOCAL) - isConnectionDeleted shouldBe true - } - - should("delete with FilesWorkingSet and JesWorkingSet with throw") { - clearMocks(crudableMockk) - connection.name = getZoweConnectionName(mockedProject, ZoweConfigType.LOCAL) - every { crudableMockk.getAll() } answers { - listOf(connection).stream() - } - every { crudableMockk.find(any(), any()) } answers { - listOf(connection).stream() - } - var f = false - var j = false - val fWSConf = FilesWorkingSetConfig() - fWSConf.connectionConfigUuid = connection.uuid - every { crudableMockk.getAll() } answers { - f = true - listOf(fWSConf).stream() - } - val jWSConf = JesWorkingSetConfig() - jWSConf.connectionConfigUuid = connection.uuid - every { crudableMockk.getAll() } answers { - j = true - listOf(jWSConf).stream() - } - val notificationsService = NotificationsService.getService() as TestNotificationsServiceImpl - notificationsService.testInstance = object : TestNotificationsServiceImpl() { - override fun notifyError( - t: Throwable, - project: Project?, - custTitle: String?, - custDetailsShort: String?, - custDetailsLong: String? - ) { - notified = true - } - } - mockedZoweConfigService.deleteZoweConfig(type = ZoweConfigType.LOCAL) - - f shouldBe true - j shouldBe true - notified shouldBe true - isConnectionDeleted shouldBe false - } - - should("checkAndRemoveOldZoweConnection") { - var isupdateConnectionCalled = false - every { crudableMockk.update(any()) } answers { - isupdateConnectionCalled = true - Optional.of(ConnectionConfig()) - } - connection.zoweConfigPath = getZoweConfigLocation(mockedProject, ZoweConfigType.LOCAL) - mockedZoweConfigService.checkAndRemoveOldZoweConnection(ZoweConfigType.LOCAL) - isupdateConnectionCalled shouldBe true - } - - clearMocks(crudableMockk) - every { crudableMockk.getAll() } returns Stream.of() - every { crudableMockk.getAll() } returns Stream.of() - every { crudableMockk.getAll() } returns Stream.of() - mockkObject(ConfigService) - every { ConfigService.getService().crudable } returns crudableMockk - every { crudableMockk.find(any(), any()) } answers { - listOf(connection).stream() - } - every { crudableMockk.delete(any()) } answers { - isConnectionDeleted = true - Optional.of(ConnectionConfig()) - } - every { crudableMockk.addOrUpdate(any()) } answers { - isAddOrUpdateConnectionCalled = true - Optional.of(ConnectionConfig()) - } - - should("testAndPrepareConnection") { - mockedZoweConfigService::class.declaredMemberFunctions.find { it.name == "testAndPrepareConnection" }?.let { - it.isAccessible = true - infoRes = InfoResponse(zosVersion = "04.25.00") - it.call(mockedZoweConfigService, connection) - connection.zVersion shouldBe ZVersion.ZOS_2_2 - infoRes = InfoResponse(zosVersion = "04.26.00") - it.call(mockedZoweConfigService, connection) - connection.zVersion shouldBe ZVersion.ZOS_2_3 - infoRes = InfoResponse(zosVersion = "04.28.00") - it.call(mockedZoweConfigService, connection) - connection.zVersion shouldBe ZVersion.ZOS_2_5 - infoRes = InfoResponse(zosVersion = "00.00.00") - every { whoAmI(any()) } returns null - it.call(mockedZoweConfigService, connection) - connection.zVersion shouldBe ZVersion.ZOS_2_1 - connection.owner shouldBe "" - infoRes = InfoResponse(zosVersion = "throw1") - try { - it.call(mockedZoweConfigService, connection) - } catch (e: Throwable) { - e.cause.toString() shouldContain "Test performInfoOperation throw" - } - infoRes = InfoResponse(zosVersion = "throw2") - try { - it.call(mockedZoweConfigService, connection) - } catch (e: Throwable) { - e.cause.toString() shouldContain "Test performOperation throw" - } - } - } - - should("addOrUpdateZoweConfig throw Cannot get Zowe config") { - mockedZoweConfigService.addOrUpdateZoweConfig( - scanProject = false, checkConnection = true, type = ZoweConfigType.GLOBAL - ) - notified shouldBe true - } - - should("addOrUpdateZoweConfig throw Cannot get password") { - every { zoweConfigMock.password } returns null - mockedZoweConfigService.addOrUpdateZoweConfig( - scanProject = false, checkConnection = true, type = ZoweConfigType.LOCAL - ) - every { zoweConfigMock.password } returns "password" - notified shouldBe true - } - - should("addOrUpdateZoweConfig throw Cannot get username") { - every { zoweConfigMock.user } returns null - mockedZoweConfigService.addOrUpdateZoweConfig( - scanProject = false, checkConnection = true, type = ZoweConfigType.LOCAL - ) - every { zoweConfigMock.user } returns "ZoweUserName" - notified shouldBe true - } - - should("getOrCreateUuid") { - mockedZoweConfigService::class.declaredMemberFunctions.find { it.name == "getOrCreateUuid" }?.let { - it.isAccessible = true - it.call(mockedZoweConfigService, ZoweConfigType.LOCAL, "zosmf") shouldBe "ID000000000000" - every { crudableMockk.find(any(), any()) } answers { - emptyList().stream() - } - val randomUUID = it.call(mockedZoweConfigService, ZoweConfigType.GLOBAL, "zosmf") - randomUUID shouldNotBe null - randomUUID shouldNotBe "ID000000000000" - } - } - - should("addOrUpdateZoweConfig New ConnectionConfig throw on check") { - mockedZoweConfigService.addOrUpdateZoweConfig( - scanProject = false, checkConnection = true, type = ZoweConfigType.LOCAL - ) - notified shouldBe true - } - - should("scanForZoweConfig global throw") { - mockedZoweConfigService::class.declaredMemberFunctions.find { it.name == "scanForZoweConfig" }?.let { - it.isAccessible = true - try { - it.call(mockedZoweConfigService, ZoweConfigType.GLOBAL) - } catch (e: Exception) { - e.cause.toString() shouldContain "Cannot parse ${ZoweConfigType.GLOBAL} Zowe config file" - } - } - } - - should("scanForZoweConfig throw") { - clearMocks(zoweConfigMock) - every { zoweConfigMock.extractSecureProperties(any>(), any()) } answers { - throw Exception("Test exception") - } - mockedZoweConfigService::class.declaredMemberFunctions.find { it.name == "scanForZoweConfig" }?.let { - it.isAccessible = true - try { - it.call(mockedZoweConfigService, ZoweConfigType.LOCAL) - } catch (e: Exception) { - e.cause.toString() shouldContain "Cannot parse ${ZoweConfigType.LOCAL} Zowe config file" - } - } - } - - should("getZoweConfigState throw") { - mockedZoweConfigService.getZoweConfigState(true, ZoweConfigType.LOCAL) - notified shouldBe true - } - - should("getZoweConfigState") { - val zosConnList = mutableListOf() - for (type in ZoweConfigType.entries) { - connection.name = getZoweConnectionName(mockedProject, type)//"zowe-$type-zosmf/untitled" - mockedZoweConfigService.globalZoweConfig = null - mockedZoweConfigService.localZoweConfig = null - mockedZoweConfigService.getZoweConfigState(false, type) shouldBe ZoweConfigState.NOT_EXISTS - mockedZoweConfigService.globalZoweConfig = zoweConfigMock - mockedZoweConfigService.localZoweConfig = zoweConfigMock - clearMocks(crudableMockk) - every { crudableMockk.find(any(), any()) } answers { - emptyList().stream() - } - mockedZoweConfigService.getZoweConfigState(false, type) shouldBe ZoweConfigState.NEED_TO_ADD - every { zoweConfigMock.user } returns "testUser" - every { zoweConfigMock.password } returns "testPassword" - every { zoweConfigMock.basePath } returns "/base/config/path/" - every { zoweConfigMock.port } returns 10443 - every { zoweConfigMock.host } returns "111.111.111.111" - every { zoweConfigMock.protocol } returns "https" - every { zoweConfigMock.rejectUnauthorized } returns true - val conn = ZOSConnection( - "111.111.111.111", - "10443", - "testUser", - "testPassword", - rejectUnauthorized = true, - basePath = "/base/config/path/", - profileName = "zosmf" - ) - zosConnList.clear() - zosConnList.add(conn) - every { zoweConfigMock.getListOfZosmfConections() } returns zosConnList - clearMocks(crudableMockk) - every { crudableMockk.find(any(), any()) } answers { - listOf(connection).stream() - } - mockedZoweConfigService.getZoweConfigState(false, type) shouldBe ZoweConfigState.NEED_TO_UPDATE - connection.name = getZoweConnectionName(mockedProject, type) - connection.url = "https://111.111.111.111:10443/base/config/path" - connection.isAllowSelfSigned = false - connection.zVersion = ZVersion.ZOS_2_1 - connection.zoweConfigPath = getZoweConfigLocation(mockedProject, type) - connection.owner = "" - mockedZoweConfigService.getZoweConfigState(false, type) shouldBe ZoweConfigState.SYNCHRONIZED - every { zoweConfigMock.password } returns "wrongPass" - zosConnList.clear() - zosConnList.add( - ZOSConnection( - "111.111.111.111", - "10443", - "testUser", - "wrongPass", - rejectUnauthorized = true, - basePath = "/base/config/path/", - profileName = "zosmf" - ) - ) - mockedZoweConfigService.getZoweConfigState(false, type) shouldBe ZoweConfigState.NEED_TO_UPDATE - every { zoweConfigMock.password } returns "testPassword" - every { zoweConfigMock.user } returns "wrongUser" - zosConnList.clear() - zosConnList.add( - ZOSConnection( - "111.111.111.111", - "10443", - "wrongUser", - "testPassword", - rejectUnauthorized = true, - basePath = "/base/config/path/", - profileName = "zosmf" - ) - ) - mockedZoweConfigService.getZoweConfigState(false, type) shouldBe ZoweConfigState.NEED_TO_UPDATE - every { zoweConfigMock.user } returns "testUser" - } - } - - should("addZoweConfigFile") { - every { crudableMockk.getAll() } returns Stream.of() - clearMocks(mockedZoweConfigInputStream) - every { mockedZoweConfigInputStream.readAllBytes() } returns null - every { mockedZoweConfigInputStream.close() } returns Unit - try { - mockedZoweConfigService.addZoweConfigFile(connectionDialogState) - } catch (e: Exception) { - e.message shouldContain "zowe.config.json is not found" - } - } - - should("deleteZoweConfig throw") { - every { crudableMockk.find(any(), any()) } returns emptyList().stream() - mockedZoweConfigService.deleteZoweConfig(type = ZoweConfigType.LOCAL) - notified shouldBe true - } - - should("findAllZosmfExistingConnection") { - val configCollections: MutableMap> = mutableMapOf( - Pair(ConnectionConfig::class.java.name, mutableListOf(connection)), - Pair(FilesWorkingSetConfig::class.java.name, mutableListOf()), - Pair(JesWorkingSetConfig::class.java.name, mutableListOf()), - ) - val sandboxState = SandboxState(ConfigStateV2(configCollections)) - val crudable = makeCrudableWithoutListeners(true, { sandboxState.credentials }) { - sandboxState.configState - } - - mockedZoweConfigService::class.declaredMemberProperties.find { it.name == "configCrudable" }?.let { - it.isAccessible = true - it.javaField?.set(mockedZoweConfigService, crudable) - } - - mockedZoweConfigService::class.declaredMemberFunctions.find { it.name == "findAllZosmfExistingConnection" }?.let { - it.isAccessible = true - (it.call(mockedZoweConfigService, ZoweConfigType.LOCAL) as List).size shouldBe 1 - it.call(mockedZoweConfigService, ZoweConfigType.GLOBAL) shouldBe null - connection.name = "zowe-global-zosmf" - it.call(mockedZoweConfigService, ZoweConfigType.GLOBAL) shouldBe null - connection.zoweConfigPath = getZoweConfigLocation(mockedProject, ZoweConfigType.GLOBAL) - (it.call(mockedZoweConfigService, ZoweConfigType.GLOBAL) as List).size shouldBe 1 - } - } - - should("findExistingConnection") { - mockedZoweConfigService::class.declaredMemberFunctions.find { it.name == "findExistingConnection" }?.let { - it.isAccessible = true - connection.name = "zowe-local-zosmf/testProj" - connection.zoweConfigPath = getZoweConfigLocation(mockedProject, ZoweConfigType.LOCAL) - (it.call( - mockedZoweConfigService, - ZoweConfigType.LOCAL, - "zosmf" - ) as ConnectionConfig).name shouldBe connection.name - connection.name = "zowe-global-zosmf" - connection.zoweConfigPath = getZoweConfigLocation(mockedProject, ZoweConfigType.LOCAL) - it.call(mockedZoweConfigService, ZoweConfigType.LOCAL, "zosmf") shouldBe null - it.call(mockedZoweConfigService, ZoweConfigType.GLOBAL, "zosmf") shouldBe null - } - } - - should("validateForBlank") { - validateForBlank(JPasswordField())?.message shouldBe "This field must not be blank" - validateForBlank(JTextField())?.message shouldBe "This field must not be blank" - } - - should("getZoweConfigState NEED_TO_ADD") { - every { mockedZoweConfigService["findAllZosmfExistingConnection"](any()) } returns listOf( - connection - ) - every { zoweConfigMock.getListOfZosmfConections() } returns zosConnList1 - every { mockedZoweConfigService["findExistingConnection"](any(), any()) } returns null - mockedZoweConfigService.getZoweConfigState(false, ZoweConfigType.LOCAL) shouldBe ZoweConfigState.NEED_TO_ADD - val zossConn2 = ZOSConnection( - "222.222.222.222", - "10443", - "testUser", - "testPassword", - rejectUnauthorized = true, - basePath = "/base/config/path/", - profileName = "zosmf1" - ) - zosConnList1.clear() - zosConnList1.add(zossConn2) - connection.name = "zowe-global-lpar.zosmf" - every { - mockedZoweConfigService["findExistingConnection"]( - any(), any() - ) - } returns connection - mockedZoweConfigService.getZoweConfigState(false, ZoweConfigType.LOCAL) shouldBe ZoweConfigState.NEED_TO_ADD - } - - } - -}) diff --git a/src/test/kotlin/org/zowe/explorer/explorer/actions/AddZoweTeamConfigActionTestSpec.kt b/src/test/kotlin/org/zowe/explorer/explorer/actions/AddZoweTeamConfigActionTestSpec.kt index cb88e852..cbc7cfef 100644 --- a/src/test/kotlin/org/zowe/explorer/explorer/actions/AddZoweTeamConfigActionTestSpec.kt +++ b/src/test/kotlin/org/zowe/explorer/explorer/actions/AddZoweTeamConfigActionTestSpec.kt @@ -24,6 +24,7 @@ import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.ui.MessageDialogBuilder import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFileManager +import io.kotest.assertions.assertSoftly import io.kotest.matchers.shouldBe import io.mockk.* import kotlinx.coroutines.Dispatchers @@ -35,8 +36,10 @@ import org.zowe.explorer.config.ConfigService import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.config.connect.ui.zosmf.ConnectionDialogState import org.zowe.explorer.config.connect.ui.zosmf.ZoweTeamConfigDialog +import org.zowe.explorer.telemetry.NotificationsService import org.zowe.explorer.testutils.WithApplicationShouldSpec import org.zowe.explorer.testutils.testServiceImpl.TestConfigServiceImpl +import org.zowe.explorer.testutils.testServiceImpl.TestNotificationsServiceImpl import org.zowe.explorer.zowe.service.ZoweConfigServiceImpl import java.nio.file.Path import java.util.* @@ -51,13 +54,27 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ var isFindFileByNioPathCalled = false var isShowUntilDoneSucceeded = false + var isErrorNotificationProduced = false beforeEach { isFindFileByNioPathCalled = false isShowUntilDoneSucceeded = false + isErrorNotificationProduced = false } context("AddZoweTeamConfigAction") { + val notificationsServiceMock = NotificationsService.getService() as TestNotificationsServiceImpl + notificationsServiceMock.testInstance = object : TestNotificationsServiceImpl() { + override fun notifyError( + t: Throwable, + project: Project?, + custTitle: String?, + custDetailsShort: String?, + custDetailsLong: String? + ) { + isErrorNotificationProduced = true + } + } val addZoweTeamConfigAction = spyk(recordPrivateCalls = true) val event = mockk() @@ -126,15 +143,21 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ should("getActionUpdateThread") { addZoweTeamConfigAction.actionUpdateThread shouldBe ActionUpdateThread.EDT + + assertSoftly { isErrorNotificationProduced shouldBe false } } should("isDumbAware") { addZoweTeamConfigAction.isDumbAware shouldBe true + + assertSoftly { isErrorNotificationProduced shouldBe false } } should("update") { addZoweTeamConfigAction.update(event) isFindFileByNioPathCalled shouldBe true + + assertSoftly { isErrorNotificationProduced shouldBe false } } should("update null project ") { @@ -142,6 +165,8 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ addZoweTeamConfigAction.update(event) every { event.project } returns ProjectManager.getInstance().defaultProject isFindFileByNioPathCalled shouldBe false + + assertSoftly { isErrorNotificationProduced shouldBe false } } should("update null file ") { @@ -155,6 +180,8 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ isFindFileByNioPathCalled = true vfMock } + + assertSoftly { isErrorNotificationProduced shouldBe false } } should("actionPerformed") { @@ -167,6 +194,8 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ } isFindFileByNioPathCalled shouldBe true isShowUntilDoneSucceeded shouldBe false + + assertSoftly { isErrorNotificationProduced shouldBe false } } should("actionPerformed null project and file exists") { @@ -179,6 +208,8 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ isShowUntilDoneSucceeded shouldBe false isFindFileByNioPathCalled shouldBe true every { event.project } returns ProjectManager.getInstance().defaultProject + + assertSoftly { isErrorNotificationProduced shouldBe false } } should("actionPerformed null project and file does not exist") { @@ -199,6 +230,8 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ isFindFileByNioPathCalled = true vfMock } + + assertSoftly { isErrorNotificationProduced shouldBe true } } should("actionPerformed null findFileByNioPath ") { @@ -217,6 +250,8 @@ class AddZoweTeamConfigActionTestSpec : WithApplicationShouldSpec({ isFindFileByNioPathCalled = true vfMock } + + assertSoftly { isErrorNotificationProduced shouldBe false } } } diff --git a/src/test/kotlin/org/zowe/explorer/zowe/ZoweStartupActivityTest.kt b/src/test/kotlin/org/zowe/explorer/zowe/ZoweStartupActivityTest.kt index 05b1111a..c77cd058 100644 --- a/src/test/kotlin/org/zowe/explorer/zowe/ZoweStartupActivityTest.kt +++ b/src/test/kotlin/org/zowe/explorer/zowe/ZoweStartupActivityTest.kt @@ -18,6 +18,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.ui.Messages import io.kotest.matchers.shouldBe import io.mockk.* +import org.zowe.explorer.config.connect.ConnectionConfig import org.zowe.explorer.testutils.WithApplicationShouldSpec import org.zowe.explorer.zowe.service.ZoweConfigService import org.zowe.explorer.zowe.service.ZoweConfigServiceImpl @@ -51,6 +52,7 @@ class ZoweStartupActivityTest : WithApplicationShouldSpec({ every { mockedZoweConfigService.deleteZoweConfig(type = any()) } answers { isConnDeleted = true } + every { mockedZoweConfigService.findAllZosmfExistingConnection(any()) } returns listOf(mockk()) var ret = 0 val showDialogRef: (Project, String, String, Array, Int, Icon) -> Int = Messages::showDialog mockkStatic(showDialogRef as KFunction<*>) @@ -62,6 +64,7 @@ class ZoweStartupActivityTest : WithApplicationShouldSpec({ } should("showDialogForDeleteZoweConfigIfNeeded NEED_TO_ADD") { + every { mockedZoweConfigService.findAllZosmfExistingConnection(any()) } returns emptyList() showDialogForDeleteZoweConfigIfNeeded(mockedProject, ZoweConfigType.LOCAL) isDialogCalled shouldBe false } @@ -73,6 +76,7 @@ class ZoweStartupActivityTest : WithApplicationShouldSpec({ } should("showDialogForDeleteZoweConfigIfNeeded SYNCHRONIZED") { + every { mockedZoweConfigService.findAllZosmfExistingConnection(any()) } returns listOf(mockk()) every { mockedZoweConfigService.getZoweConfigState(type = any()) } returns ZoweConfigState.SYNCHRONIZED showDialogForDeleteZoweConfigIfNeeded(mockedProject, ZoweConfigType.LOCAL) isDialogCalled shouldBe true diff --git a/src/test/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImplCompanionTestSpec.kt b/src/test/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImplCompanionTestSpec.kt new file mode 100644 index 00000000..0b519ef4 --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceImplCompanionTestSpec.kt @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2025 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.explorer.zowe.service + +import com.intellij.openapi.project.Project +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.mockk +import io.mockk.unmockkAll +import org.zowe.explorer.config.ConfigService +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.telemetry.NotificationsService +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.testutils.testServiceImpl.TestConfigServiceImpl +import org.zowe.explorer.testutils.testServiceImpl.TestNotificationsServiceImpl +import org.zowe.explorer.utils.crudable.Crudable +import org.zowe.explorer.zowe.ZOWE_CONFIG_NAME + +class ZoweConfigServiceImplCompanionTestSpec : WithApplicationShouldSpec({ + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("zowe/service/ZoweConfigServiceImpl.Companion") { + lateinit var configServiceMock: ConfigService + lateinit var configServiceCrudableMock: Crudable + lateinit var notificationsServiceMock: TestNotificationsServiceImpl + + var errorNotificationTrigerredCount = 0 + + val projectMock = mockk { + every { name } returns "test_project_name" + every { basePath } returns "test/project/base/path" + } + + beforeEach { + errorNotificationTrigerredCount = 0 + + configServiceMock = ConfigService.getService() as TestConfigServiceImpl + configServiceCrudableMock = configServiceMock.crudable + every { + configServiceCrudableMock.getAll(any>()) + } answers { + emptyList().stream() + } + + notificationsServiceMock = NotificationsService.getService() as TestNotificationsServiceImpl + + notificationsServiceMock.testInstance = object : TestNotificationsServiceImpl() { + override fun notifyError( + t: Throwable, + project: Project?, + custTitle: String?, + custDetailsShort: String?, + custDetailsLong: String? + ) { + errorNotificationTrigerredCount += 1 + } + } + } + + context("ZoweConfigServiceImpl.getZoweConnectionName") { + should("return a new Zowe connection name both for local and global Zowe Configs") { + val custProfileName = "test-profile" + val resultLocalDefaultProfile = ZoweConfigServiceImpl.getZoweConnectionName(projectMock, ZoweConfigType.LOCAL) + val resultLocalCustomProfile = ZoweConfigServiceImpl.getZoweConnectionName(projectMock, ZoweConfigType.LOCAL, custProfileName) + val resultGlobal = ZoweConfigServiceImpl.getZoweConnectionName(null, ZoweConfigType.GLOBAL) + + assertSoftly { resultLocalDefaultProfile shouldBe "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}" } + assertSoftly { resultLocalCustomProfile shouldBe "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$custProfileName/${projectMock.name}" } + assertSoftly { resultGlobal shouldBe "$ZOWE_PROJECT_PREFIX${ZoweConfigType.GLOBAL}-zosmf" } + assertSoftly { errorNotificationTrigerredCount shouldBe 0 } + } + + should("return an empty string when there is no project for the local Zowe Config") { + val result = ZoweConfigServiceImpl.getZoweConnectionName(null, ZoweConfigType.LOCAL) + + assertSoftly { result shouldBe "" } + assertSoftly { errorNotificationTrigerredCount shouldBe 1 } + } + + should("return a new Zowe connection name when there is a duplicating connection name already saved") { + every { + configServiceCrudableMock.getAll(any>()) + } answers { + listOf( + mockk { every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.GLOBAL}-zosmf" }, + mockk { every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}" }, + mockk { every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}1" }, + mockk { every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}2" } + ).stream() + } + + val result = ZoweConfigServiceImpl.getZoweConnectionName(projectMock, ZoweConfigType.LOCAL) + + assertSoftly { result shouldBe "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}3" } + assertSoftly { errorNotificationTrigerredCount shouldBe 0 } + } + } + + context("ZoweConfigServiceImpl.getZoweConfigLocation") { + should("return both local and global Zowe Config paths on request") { + val resultLocalConfig = ZoweConfigServiceImpl.getZoweConfigLocation(projectMock, ZoweConfigType.LOCAL) + val resultGlobalConfig = ZoweConfigServiceImpl.getZoweConfigLocation(null, ZoweConfigType.GLOBAL) + + assertSoftly { resultLocalConfig shouldBe "${projectMock.basePath}/$ZOWE_CONFIG_NAME" } + assertSoftly { resultGlobalConfig shouldNotContain "${projectMock.basePath}" } + assertSoftly { resultGlobalConfig shouldContain "/.zowe/$ZOWE_CONFIG_NAME" } + assertSoftly { errorNotificationTrigerredCount shouldBe 0 } + } + + should("return an empty string when there is no project for the local Zowe Config") { + val result = ZoweConfigServiceImpl.getZoweConfigLocation(null, ZoweConfigType.LOCAL) + + assertSoftly { result shouldBe "" } + assertSoftly { errorNotificationTrigerredCount shouldBe 1 } + } + } + + context("ZoweConfigServiceImpl.getProfileNameFromConnName") { + should("recognize a profile name both from local and global Zowe Configs") { + val profileName = "test_profile_name" + val localConfigConnName = "${ZOWE_PROJECT_PREFIX}${ZoweConfigType.LOCAL}-$profileName/${projectMock.name}" + val globalConfigConnName = "${ZOWE_PROJECT_PREFIX}${ZoweConfigType.LOCAL}-$profileName" + + val resultLocalConfig = ZoweConfigServiceImpl.getProfileNameFromConnName(localConfigConnName) + val resultGlobalConfig = ZoweConfigServiceImpl.getProfileNameFromConnName(globalConfigConnName) + + assertSoftly { resultLocalConfig shouldBe profileName } + assertSoftly { resultGlobalConfig shouldBe profileName } + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceTestSpec.kt b/src/test/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceTestSpec.kt new file mode 100644 index 00000000..01da5bb8 --- /dev/null +++ b/src/test/kotlin/org/zowe/explorer/zowe/service/ZoweConfigServiceTestSpec.kt @@ -0,0 +1,1384 @@ +/* + * Copyright (c) 2025 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.explorer.zowe.service + +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.application.runWriteAction +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.testFramework.UsefulTestCase.assertThrows +import com.intellij.util.messages.Topic +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.fail +import io.kotest.matchers.shouldBe +import io.mockk.* +import org.zowe.explorer.config.ConfigService +import org.zowe.explorer.config.connect.ConnectionConfig +import org.zowe.explorer.config.connect.CredentialService +import org.zowe.explorer.config.connect.ui.zosmf.ConnectionDialogState +import org.zowe.explorer.config.connect.whoAmI +import org.zowe.explorer.config.ws.FilesWorkingSetConfig +import org.zowe.explorer.config.ws.JesWorkingSetConfig +import org.zowe.explorer.dataops.DataOpsManager +import org.zowe.explorer.dataops.Operation +import org.zowe.explorer.dataops.operations.InfoOperation +import org.zowe.explorer.dataops.operations.ZOSInfoOperation +import org.zowe.explorer.telemetry.NotificationsService +import org.zowe.explorer.testutils.WithApplicationShouldSpec +import org.zowe.explorer.testutils.testServiceImpl.TestConfigServiceImpl +import org.zowe.explorer.testutils.testServiceImpl.TestCredentialsServiceImpl +import org.zowe.explorer.testutils.testServiceImpl.TestDataOpsManagerImpl +import org.zowe.explorer.testutils.testServiceImpl.TestNotificationsServiceImpl +import org.zowe.explorer.utils.crudable.Crudable +import org.zowe.explorer.utils.getResourceAsStreamWrappable +import org.zowe.explorer.utils.optional +import org.zowe.explorer.utils.sendTopic +import org.zowe.explorer.zowe.ZOWE_CONFIG_NAME +import org.zowe.kotlinsdk.InfoResponse +import org.zowe.kotlinsdk.SystemsResponse +import org.zowe.kotlinsdk.annotations.ZVersion +import org.zowe.kotlinsdk.zowe.config.KeytarWrapper +import org.zowe.kotlinsdk.zowe.config.ZoweConfig +import org.zowe.kotlinsdk.zowe.config.parseConfigJson +import java.io.InputStream +import java.nio.file.Path +import java.util.* +import java.util.function.Predicate +import javax.swing.Icon +import kotlin.reflect.KFunction + +class ZoweConfigServiceTestSpec : WithApplicationShouldSpec({ + afterSpec { + clearAllMocks() + unmockkAll() + } + + context("zowe/service/ZoweConfigService") { + lateinit var credentialServiceMock: TestCredentialsServiceImpl + lateinit var dataOpsManagerServiceMock: TestDataOpsManagerImpl + lateinit var configServiceMock: ConfigService + lateinit var configServiceCrudableMock: Crudable + lateinit var notificationsServiceMock: TestNotificationsServiceImpl + + var errorNotificationTrigerredCount = 0 + + val projectMock = mockk { + every { name } returns "test_project_name" + every { basePath } returns "test/project/base/path" + every { isDisposed } returns true + } + + mockkStatic(::whoAmI as KFunction<*>) + + mockkStatic(VirtualFileManager::getInstance) + + beforeEach { + errorNotificationTrigerredCount = 0 + + configServiceMock = ConfigService.getService() as TestConfigServiceImpl + configServiceCrudableMock = configServiceMock.crudable + every { + configServiceCrudableMock.getAll(any>()) + } answers { + emptyList().stream() + } + + credentialServiceMock = CredentialService.getService() as TestCredentialsServiceImpl + dataOpsManagerServiceMock = DataOpsManager.getService() as TestDataOpsManagerImpl + notificationsServiceMock = NotificationsService.getService() as TestNotificationsServiceImpl + + notificationsServiceMock.testInstance = object : TestNotificationsServiceImpl() { + override fun notifyError( + t: Throwable, + project: Project?, + custTitle: String?, + custDetailsShort: String?, + custDetailsLong: String? + ) { + errorNotificationTrigerredCount += 1 + } + } + + every { whoAmI(any()) } returns "USERID" + + every { + VirtualFileManager.getInstance() + } answers { + mockk { + every { + findFileByNioPath(any()) + } answers { + mockk { + every { inputStream } returns mockk { + every { close() } answers {} + } + every { path } returns "test" + } + } + } + } + } + + context("findAllZosmfExistingConnection") { + should("find connection configs both for local and global Zowe Configs") { + val localConnectionConfigMock = mockk { + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + } + val globalConnectionConfigMock = mockk { + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.GLOBAL}-zosmf" + every { + zoweConfigPath + } returns System.getProperty("user.home").replace("((\\*)|(/*))$", "") + "/.zowe/" + ZOWE_CONFIG_NAME + } + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf( + mockk { + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}1" + every { zoweConfigPath } returns "other/test/project/$ZOWE_CONFIG_NAME" + }, + localConnectionConfigMock, + globalConnectionConfigMock + ) + .filter(secondArg>()::test) + .stream() + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + val resultLocal = zoweConfigService.findAllZosmfExistingConnection(ZoweConfigType.LOCAL) + val resultGlobal = zoweConfigService.findAllZosmfExistingConnection(ZoweConfigType.GLOBAL) + + assertSoftly { resultLocal.size shouldBe 1 } + assertSoftly { resultLocal[0] shouldBe localConnectionConfigMock } + assertSoftly { resultGlobal.size shouldBe 1 } + assertSoftly { resultGlobal[0] shouldBe globalConnectionConfigMock } + } + + should("return empty list if there is no connection configs found for the currently opened project") { + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf( + mockk { + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}1" + every { zoweConfigPath } returns "other/test/project/$ZOWE_CONFIG_NAME" + } + ) + .filter(secondArg>()::test) + .stream() + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + val result = zoweConfigService.findAllZosmfExistingConnection(ZoweConfigType.LOCAL) + + assertSoftly { result.isEmpty() shouldBe true } + } + } + + context("addOrUpdateZoweConfig") { + var setCredentialsCalledCount = 0 + var onConfigSavedCalledCount = 0 + var addOrUpdateCalledCount = 0 + var infoOperationCount = 0 + var zosInfoOperationCount = 0 + + val sendTopicRef: (Topic, Project) -> ZoweConfigHandler = ::sendTopic + mockkStatic(sendTopicRef as KFunction<*>) + every { + sendTopic(any>()) + } answers { + mockk { + every { + onConfigSaved(any(), any()) + } answers { + onConfigSavedCalledCount += 1 + } + } + } + + mockkStatic(NotificationGroupManager::getInstance) + + val parseConfigJsonRef: (InputStream) -> ZoweConfig = ::parseConfigJson + mockkStatic(parseConfigJsonRef as KFunction<*>) + + every { + configServiceCrudableMock.addOrUpdate(any()) + } answers { + addOrUpdateCalledCount += 1 + firstArg().optional + } + + beforeEach { + setCredentialsCalledCount = 0 + onConfigSavedCalledCount = 0 + addOrUpdateCalledCount = 0 + infoOperationCount = 0 + zosInfoOperationCount = 0 + + credentialServiceMock.testInstance = object : TestCredentialsServiceImpl() { + override fun setCredentials(connectionConfigUuid: String, username: String, password: CharArray) { + setCredentialsCalledCount += 1 + } + } + + dataOpsManagerServiceMock.testInstance = object : TestDataOpsManagerImpl() {} + } + + should("update an existing connection config for the local Zowe config") { + val testProfileName = "test_profile" + + dataOpsManagerServiceMock.testInstance = object : TestDataOpsManagerImpl() { + override fun performOperation(operation: Operation, progressIndicator: ProgressIndicator): R { + return when (operation) { + is InfoOperation -> { + infoOperationCount += 1 + mockk() as R + } + + is ZOSInfoOperation -> { + zosInfoOperationCount += 1 + mockk { + every { zosVersion } returns "04.28.00" + } as R + } + + else -> { + mockk() as R + } + } + } + } + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf( + mockk { + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-custom/${projectMock.name}" + }, + mockk { + every { uuid } returns "test_uuid" + every { zVersion } returns ZVersion.ZOS_2_5 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testProfileName/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + } + ) + .filter(secondArg>()::test) + .stream() + } + + val localZoweConfig: ZoweConfig = mockk { + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testProfileName + every { basePath } returns "test/base/path" + every { host } returns "test.com" + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns true + } + ) + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + zoweConfigService.localZoweConfig = localZoweConfig + + zoweConfigService + .addOrUpdateZoweConfig(scanProject = false, checkConnection = false, ZoweConfigType.LOCAL) + + assertSoftly { setCredentialsCalledCount shouldBe 1 } + assertSoftly { infoOperationCount shouldBe 1 } + assertSoftly { zosInfoOperationCount shouldBe 1 } + assertSoftly { onConfigSavedCalledCount shouldBe 1 } + assertSoftly { addOrUpdateCalledCount shouldBe 1 } + } + + should("add a new connection for the local Zowe config, scanning a project, with failed connections and their check") { + val testSuccessProfileName = "test_profile_name_success" + val testFailProfileName1 = "test_profile_name_fail1" + val testFailProfileName2 = "test_profile_name_fail2" + val testFailHost1 = "test1.com" + val testFailHost2 = "test2.com" + + var extractSecurePropertiesCalledCount = 0 + var isCorrectConnectionErrorNotificationTrigerred = false + + dataOpsManagerServiceMock.testInstance = object : TestDataOpsManagerImpl() { + override fun performOperation(operation: Operation, progressIndicator: ProgressIndicator): R { + return when (operation) { + is InfoOperation -> { + infoOperationCount += 1 + if (infoOperationCount == 1) { + throw Exception() + } else { + mockk() as R + } + } + + is ZOSInfoOperation -> { + zosInfoOperationCount += 1 + if (zosInfoOperationCount == 1) { + throw Exception() + } else { + mockk { + every { zosVersion } returns "04.29.00" + } as R + } + } + + else -> { + mockk() as R + } + } + } + } + + every { NotificationGroupManager.getInstance() } returns mockk { + every { getNotificationGroup(any()) } returns mockk { + every { createNotification(any(), any(), any()) } answers { + val title = firstArg() + val details = secondArg() + val notificationType = thirdArg() + + mockk { + every { addAction(any()) } returns mockk() + every { + notify(any()) + } answers { + if ( + notificationType == NotificationType.ERROR + && title.contains("Connection failed to") + && details.contains(testFailHost1) + && details.contains(testFailHost2) + ) { + isCorrectConnectionErrorNotificationTrigerred = true + } + } + } + } + } + } + + val localZoweConfig: ZoweConfig = mockk { + every { + extractSecureProperties(any>(), any()) + } answers { + extractSecurePropertiesCalledCount += 1 + } + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testFailProfileName1 + every { basePath } returns "test/base/path/" + every { host } returns testFailHost1 + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns null + }, + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testFailProfileName2 + every { basePath } returns "test/base/path" + every { host } returns testFailHost2 + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns false + }, + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testSuccessProfileName + every { basePath } returns "test/base/path" + every { host } returns "test3.com" + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns true + } + ) + } + every { parseConfigJsonRef(any()) } returns localZoweConfig + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf( + mockk { + every { uuid } returns "test_uuid_fail1" + every { zVersion } returns ZVersion.ZOS_2_3 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testFailProfileName1/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + }, + mockk { + every { uuid } returns "test_uuid_fail2" + every { zVersion } returns ZVersion.ZOS_2_4 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testFailProfileName2/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + }, + mockk { + every { name } returns "invalid-config-name" + } + ) + .filter(secondArg>()::test) + .stream() + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService + .addOrUpdateZoweConfig(scanProject = true, checkConnection = true, ZoweConfigType.LOCAL) + + assertSoftly { setCredentialsCalledCount shouldBe 3 } + assertSoftly { onConfigSavedCalledCount shouldBe 1 } + assertSoftly { extractSecurePropertiesCalledCount shouldBe 1 } + assertSoftly { infoOperationCount shouldBe 3 } + assertSoftly { zosInfoOperationCount shouldBe 2 } + assertSoftly { isCorrectConnectionErrorNotificationTrigerred shouldBe true } + assertSoftly { addOrUpdateCalledCount shouldBe 1 } + } + + should("add a new connection for the global Zowe config without a check") { + val testProfileName = "test_profile_name" + + every { whoAmI(any()) } returns null + + dataOpsManagerServiceMock.testInstance = object : TestDataOpsManagerImpl() { + override fun performOperation(operation: Operation, progressIndicator: ProgressIndicator): R { + return when (operation) { + is InfoOperation -> { + infoOperationCount += 1 + mockk() as R + } + + is ZOSInfoOperation -> { + zosInfoOperationCount += 1 + mockk { + every { zosVersion } returns "04.27.00" + } as R + } + + else -> { + mockk() as R + } + } + } + } + + val globalZoweConfig: ZoweConfig = mockk { + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testProfileName + every { basePath } returns "test/base/path" + every { host } returns "test3.com" + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns true + } + ) + } + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf( + mockk { + every { uuid } returns "test_uuid" + every { zVersion } returns ZVersion.ZOS_2_4 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testProfileName" + every { + zoweConfigPath + } returns System.getProperty("user.home").replace("((\\*)|(/*))$", "") + "/.zowe/" + ZOWE_CONFIG_NAME + } + ) + .filter(secondArg>()::test) + .stream() + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + zoweConfigService.globalZoweConfig = globalZoweConfig + + zoweConfigService + .addOrUpdateZoweConfig(scanProject = false, checkConnection = false, ZoweConfigType.GLOBAL) + + assertSoftly { setCredentialsCalledCount shouldBe 1 } + assertSoftly { onConfigSavedCalledCount shouldBe 1 } + assertSoftly { infoOperationCount shouldBe 1 } + assertSoftly { zosInfoOperationCount shouldBe 1 } + assertSoftly { addOrUpdateCalledCount shouldBe 1 } + } + + should("try to add a new connection for the global Zowe config, scanning a project, with failed connections and their check") { + val testSuccessProfileName = "test_profile_name_success" + val testFailProfileName1 = "test_profile_name_fail1" + val testFailProfileName2 = "test_profile_name_fail2" + val testFailProfileName3 = "test_profile_name_fail3" + val testFailProfileName4 = "test_profile_name_fail4" + val testFailHost1 = "test1.com" + val testFailHost2 = "test2.com" + val testFailHost3 = "test3.com" + val testFailHost4 = "test4.com" + + var extractSecurePropertiesCalledCount = 0 + var isCorrectConnectionErrorNotificationTrigerred = false + + dataOpsManagerServiceMock.testInstance = object : TestDataOpsManagerImpl() { + override fun performOperation(operation: Operation, progressIndicator: ProgressIndicator): R { + return when (operation) { + is InfoOperation -> { + infoOperationCount += 1 + if (infoOperationCount <= 2) { + throw Exception() + } else { + mockk() as R + } + } + + is ZOSInfoOperation -> { + zosInfoOperationCount += 1 + if (zosInfoOperationCount <= 2) { + throw Exception() + } else { + mockk { + every { zosVersion } returns "04.26.00" + } as R + } + } + + else -> { + mockk() as R + } + } + } + } + + every { NotificationGroupManager.getInstance() } returns mockk { + every { getNotificationGroup(any()) } returns mockk { + every { createNotification(any(), any(), any()) } answers { + val title = firstArg() + val details = secondArg() + val notificationType = thirdArg() + + mockk { + every { addAction(any()) } returns mockk() + every { + notify(any()) + } answers { + if ( + notificationType == NotificationType.ERROR + && title.contains("Connection failed to") + && details.contains(testFailHost1) + && details.contains(testFailHost2) + && details.contains(testFailHost3) + && details.contains(testFailHost4) + && details.contains("...") + ) { + isCorrectConnectionErrorNotificationTrigerred = true + } + } + } + } + } + } + + val globalZoweConfig: ZoweConfig = mockk { + every { + extractSecureProperties(any>(), any()) + } answers { + extractSecurePropertiesCalledCount += 1 + } + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testFailProfileName1 + every { basePath } returns "test/base/path/" + every { host } returns testFailHost1 + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns null + }, + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testFailProfileName2 + every { basePath } returns "test/base/path" + every { host } returns testFailHost2 + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns false + }, + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testFailProfileName3 + every { basePath } returns "test/base/path" + every { host } returns testFailHost3 + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns false + }, + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testFailProfileName4 + every { basePath } returns "test/base/path" + every { host } returns testFailHost4 + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns false + }, + mockk { + every { user } returns "TSTUSR" + every { password } returns "TSTPWD" + every { profileName } returns testSuccessProfileName + every { basePath } returns "test/base/path" + every { host } returns "test3.com" + every { zosmfPort } returns "1234" + every { protocol } returns "https" + every { rejectUnauthorized } returns true + } + ) + } + every { parseConfigJsonRef(any()) } returns globalZoweConfig + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf( + mockk { + every { uuid } returns "test_uuid_fail1" + every { zVersion } returns ZVersion.ZOS_2_3 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testFailProfileName1/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + }, + mockk { + every { uuid } returns "test_uuid_fail2" + every { zVersion } returns ZVersion.ZOS_2_4 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testFailProfileName2/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + }, + mockk { + every { uuid } returns "test_uuid_fail3" + every { zVersion } returns ZVersion.ZOS_2_4 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testFailProfileName3/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + }, + mockk { + every { uuid } returns "test_uuid_fail4" + every { zVersion } returns ZVersion.ZOS_2_4 + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testFailProfileName4/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + } + ) + .filter(secondArg>()::test) + .stream() + } + + every { + configServiceCrudableMock.addOrUpdate(any()) + } answers { + addOrUpdateCalledCount += 1 + Optional.empty() + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService + .addOrUpdateZoweConfig(scanProject = true, checkConnection = true, ZoweConfigType.GLOBAL) + + assertSoftly { setCredentialsCalledCount shouldBe 5 } + assertSoftly { onConfigSavedCalledCount shouldBe 0 } + assertSoftly { extractSecurePropertiesCalledCount shouldBe 1 } + assertSoftly { infoOperationCount shouldBe 5 } + assertSoftly { zosInfoOperationCount shouldBe 3 } + assertSoftly { isCorrectConnectionErrorNotificationTrigerred shouldBe true } + assertSoftly { addOrUpdateCalledCount shouldBe 1 } + } + + should("produce an error notification cause the Zowe config file is not found") { + every { + VirtualFileManager.getInstance() + } answers { + mockk { + every { + findFileByNioPath(any()) + } returns null + } + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService + .addOrUpdateZoweConfig(scanProject = true, checkConnection = false, ZoweConfigType.LOCAL) + + assertSoftly { errorNotificationTrigerredCount shouldBe 1 } + } + + should("produce an error notification during a Zowe config JSON parse") { + every { parseConfigJsonRef(any()) } answers { throw Exception() } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService + .addOrUpdateZoweConfig(scanProject = true, checkConnection = false, ZoweConfigType.LOCAL) + + assertSoftly { errorNotificationTrigerredCount shouldBe 2 } + } + } + + context("deleteZoweConfig") { + val testUuid = "test_uuid" + val localConnectionConfigMock = mockk { + every { uuid } returns testUuid + every { name } returns "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}" + every { zoweConfigPath } returns "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + } + + var didWarningMessageForDeleteAppear = false + var didCorrectWarningMessageForDeleteAppear = false + var isClearCredentialsCalled = false + var didDeleteZoweConnection = false + + val showOkCancelDialogMock: (String, String, String, String, Icon?) -> Int = Messages::showOkCancelDialog + mockkStatic(showOkCancelDialogMock as KFunction<*>) + + beforeEach { + didWarningMessageForDeleteAppear = false + didCorrectWarningMessageForDeleteAppear = false + isClearCredentialsCalled = false + didDeleteZoweConnection = false + + every { + showOkCancelDialogMock(any(), any(), any(), any(), any()) + } answers { + didWarningMessageForDeleteAppear = true + if (!firstArg().contains("wrong")) { + didCorrectWarningMessageForDeleteAppear = true + } + Messages.OK + } + + credentialServiceMock.testInstance = object : TestCredentialsServiceImpl() { + override fun clearCredentials(connectionConfigUuid: String) { + isClearCredentialsCalled = true + } + } + + every { + configServiceCrudableMock.delete(any()) + } answers { + didDeleteZoweConnection = true + Optional.empty() + } + } + + should("delete the Zowe connection together with the files and JES working sets that use the connection") { + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf(localConnectionConfigMock).stream() + } + every { + configServiceCrudableMock.getAll(any>()) + } answers { + when ((invocation.args[0] as Class<*>).name) { + FilesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "right_test_name" + every { connectionConfigUuid } returns testUuid + }, + mockk { + every { name } returns "wrong_test_name" + every { connectionConfigUuid } returns "test_other_uuid" + } + ).stream() + } + JesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "right_test_name" + every { connectionConfigUuid } returns testUuid + }, + mockk { + every { name } returns "wrong_test_name" + every { connectionConfigUuid } returns "test_other_uuid" + } + ).stream() + } + else -> { + fail("Unrecognized getAll call") + } + } + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService.deleteZoweConfig(ZoweConfigType.LOCAL) + + assertSoftly { didWarningMessageForDeleteAppear shouldBe true } + assertSoftly { didCorrectWarningMessageForDeleteAppear shouldBe true } + assertSoftly { isClearCredentialsCalled shouldBe true } + assertSoftly { didDeleteZoweConnection shouldBe true } + assertSoftly { errorNotificationTrigerredCount shouldBe 0 } + } + + should("delete the Zowe connection without files of JES working sets") { + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf(localConnectionConfigMock).stream() + } + every { + configServiceCrudableMock.getAll(any>()) + } answers { + when ((invocation.args[0] as Class<*>).name) { + FilesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "wrong_test_name" + every { connectionConfigUuid } returns "test_other_uuid" + } + ).stream() + } + JesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "wrong_test_name" + every { connectionConfigUuid } returns "test_other_uuid" + } + ).stream() + } + else -> { + fail("Unrecognized getAll call") + } + } + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService.deleteZoweConfig(ZoweConfigType.LOCAL) + + assertSoftly { didWarningMessageForDeleteAppear shouldBe false } + assertSoftly { didCorrectWarningMessageForDeleteAppear shouldBe false } + assertSoftly { isClearCredentialsCalled shouldBe true } + assertSoftly { didDeleteZoweConnection shouldBe true } + assertSoftly { errorNotificationTrigerredCount shouldBe 0 } + } + + should("delete the Zowe connection with files working set only") { + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf(localConnectionConfigMock).stream() + } + every { + configServiceCrudableMock.getAll(any>()) + } answers { + when ((invocation.args[0] as Class<*>).name) { + FilesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "right_test_name" + every { connectionConfigUuid } returns testUuid + }, + ).stream() + } + JesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "wrong_test_name" + every { connectionConfigUuid } returns "test_other_uuid" + } + ).stream() + } + else -> { + fail("Unrecognized getAll call") + } + } + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService.deleteZoweConfig(ZoweConfigType.LOCAL) + + assertSoftly { didWarningMessageForDeleteAppear shouldBe true } + assertSoftly { didCorrectWarningMessageForDeleteAppear shouldBe true } + assertSoftly { isClearCredentialsCalled shouldBe true } + assertSoftly { didDeleteZoweConnection shouldBe true } + assertSoftly { errorNotificationTrigerredCount shouldBe 0 } + } + + should("not delete the Zowe connection with JES working set cause user declined removal of the JES working set") { + every { + showOkCancelDialogMock(any(), any(), any(), any(), any()) + } answers { + didWarningMessageForDeleteAppear = true + if (!firstArg().contains("wrong")) { + didCorrectWarningMessageForDeleteAppear = true + } + Messages.CANCEL + } + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf(localConnectionConfigMock).stream() + } + every { + configServiceCrudableMock.getAll(any>()) + } answers { + when ((invocation.args[0] as Class<*>).name) { + FilesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "right_test_name" + every { connectionConfigUuid } returns testUuid + }, + ).stream() + } + JesWorkingSetConfig::class.java.name -> { + listOf( + mockk { + every { name } returns "right_test_name" + every { connectionConfigUuid } returns testUuid + }, + ).stream() + } + else -> { + fail("Unrecognized getAll call") + } + } + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService.deleteZoweConfig(ZoweConfigType.LOCAL) + + assertSoftly { didWarningMessageForDeleteAppear shouldBe true } + assertSoftly { didCorrectWarningMessageForDeleteAppear shouldBe true } + assertSoftly { isClearCredentialsCalled shouldBe false } + assertSoftly { didDeleteZoweConnection shouldBe false } + assertSoftly { errorNotificationTrigerredCount shouldBe 0 } + } + + should("throw an exception without the Zowe connection removal cause there is no z/OSMF connections") { + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf().stream() + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + + zoweConfigService.deleteZoweConfig(ZoweConfigType.LOCAL) + + assertSoftly { didWarningMessageForDeleteAppear shouldBe false } + assertSoftly { didCorrectWarningMessageForDeleteAppear shouldBe false } + assertSoftly { isClearCredentialsCalled shouldBe false } + assertSoftly { didDeleteZoweConnection shouldBe false } + assertSoftly { errorNotificationTrigerredCount shouldBe 1 } + } + } + + context("addZoweConfigFile") { + val testIsAllowSelfSigned = true + + var zoweConnectionConfig = ConnectionConfig() + + var didSaveNewSecurePropertiesCalled = false + + mockkObject(ZoweConfig) + + val runWriteActionRef: (() -> Unit) -> Unit = ::runWriteAction + mockkStatic(runWriteActionRef as KFunction<*>) + every { + runWriteActionRef(any<() -> Unit>()) + } answers { + firstArg<() -> Unit>()() + } + + beforeEach { + zoweConnectionConfig = ConnectionConfig() + zoweConnectionConfig.name = "Test not a Zowe connection" + + didSaveNewSecurePropertiesCalled = false + + every { + ZoweConfig.saveNewSecureProperties(any(), any>(), any()) + } answers { + didSaveNewSecurePropertiesCalled = true + } + } + + should("add Zowe config file, updating the contents with the correct parameters") { + val testHost = "test.com" + val testPort = "1234" + + var didCorrectlyChangeContent = false + + every { + configServiceCrudableMock.getAll(any>()) + } returns listOf(zoweConnectionConfig).stream() + + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { zoweConfigService["createZoweSchemaJsonIfNotExists"]() } returns Unit + every { + zoweConfigService["saveChangedZoweConfig"](any(), any()) + } answers { + didCorrectlyChangeContent = (String(secondArg()) == "\"$testHost\":$testPort:${!testIsAllowSelfSigned}") + Unit + } + + mockkStatic("org.zowe.explorer.utils.MiscUtilsKt") + every { + getResourceAsStreamWrappable(any(),any()) + } returns mockk { + every { close() } answers {} + every { readAllBytes() } returns "::".toByteArray() + } + + val connectionDialogState = ConnectionDialogState( + connectionUrl = "https://$testHost:$testPort", + isAllowSsl = testIsAllowSelfSigned, + username = "TSTUSR", + password = "TSTPWD".toCharArray() + ) + + zoweConfigService.addZoweConfigFile(connectionDialogState) + + assertSoftly { didSaveNewSecurePropertiesCalled shouldBe true } + assertSoftly { didCorrectlyChangeContent shouldBe true } + } + + should("add Zowe config file, updating the contents with the localhost:10443, cause host and port are not recognized") { + var didCorrectlyChangeContent = false + + every { + configServiceCrudableMock.getAll(any>()) + } returns listOf(zoweConnectionConfig).stream() + + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { zoweConfigService["createZoweSchemaJsonIfNotExists"]() } returns Unit + every { + zoweConfigService["saveChangedZoweConfig"](any(), any()) + } answers { + didCorrectlyChangeContent = (String(secondArg()) == "\"localhost\":10443:${!testIsAllowSelfSigned}") + Unit + } + + mockkStatic("org.zowe.explorer.utils.MiscUtilsKt") + every { + getResourceAsStreamWrappable(any(),any()) + } returns mockk { + every { close() } answers {} + every { readAllBytes() } returns "::".toByteArray() + } + + val connectionDialogState = ConnectionDialogState( + connectionUrl = "invalid_url", + isAllowSsl = testIsAllowSelfSigned, + username = "TSTUSR", + password = "TSTPWD".toCharArray() + ) + + zoweConfigService.addZoweConfigFile(connectionDialogState) + + assertSoftly { didSaveNewSecurePropertiesCalled shouldBe true } + assertSoftly { didCorrectlyChangeContent shouldBe true } + } + + should("throw an exception cause there is no Zowe config file or it cannot be opened") { + every { + configServiceCrudableMock.getAll(any>()) + } returns listOf(zoweConnectionConfig).stream() + + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { zoweConfigService["createZoweSchemaJsonIfNotExists"]() } returns Unit + + mockkStatic("org.zowe.explorer.utils.MiscUtilsKt") + every { getResourceAsStreamWrappable(any(),any()) } returns null + + val connectionDialogState = ConnectionDialogState( + connectionUrl = "http://test.com", + isAllowSsl = testIsAllowSelfSigned, + username = "TSTUSR", + password = "TSTPWD".toCharArray() + ) + + assertThrows(Exception::class.java, "$ZOWE_CONFIG_NAME is not found") { + zoweConfigService.addZoweConfigFile(connectionDialogState) + } + assertSoftly { didSaveNewSecurePropertiesCalled shouldBe false } + } + } + + context("checkAndRemoveOldZoweConnection") { + should("remove Zowe config path and change the related connection name to transform Zowe connection to a simple plugin connection") { + var updateCalledCount = 0 + var didCorrectlyChangeConnectionConfig = false + + val testConnectionConfigName = "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}" + val zoweConnectionConfig = ConnectionConfig() + zoweConnectionConfig.name = testConnectionConfigName + zoweConnectionConfig.zoweConfigPath = "${projectMock.basePath}/${ZOWE_CONFIG_NAME}" + + every { + configServiceCrudableMock.getAll(any>()) + } returns listOf(zoweConnectionConfig).stream() + every { + configServiceCrudableMock.update(any()) + } answers { + updateCalledCount += 1 + val newConnectionConfig = firstArg() + didCorrectlyChangeConnectionConfig = ( + newConnectionConfig.name == "${testConnectionConfigName}1" && newConnectionConfig.zoweConfigPath == null + ) + firstArg().optional + } + + val zoweConfigService = ZoweConfigServiceImpl(projectMock) + zoweConfigService.checkAndRemoveOldZoweConnection(ZoweConfigType.LOCAL) + + assertSoftly { updateCalledCount shouldBe 1 } + assertSoftly { didCorrectlyChangeConnectionConfig shouldBe true } + } + } + + context("getZoweConfigState") { + val testProtocol = "https" + val testHost = "test.com" + val testPort = "1234" + val testBasePath = "/test/base/path" + val testUsername = "TSTUSR" + val testPassword = "TSTPWD" + val testProfileName = "test_profile_name.test_profile_name_inner" + val testIsAllowSelfSigned = false + + credentialServiceMock.testInstance = object : TestCredentialsServiceImpl() { + override fun getUsernameByKey(connectionConfigUuid: String): String { + return testUsername + } + + override fun getPasswordByKey(connectionConfigUuid: String): CharArray { + return testPassword.toCharArray() + } + } + + should("return SYNCHRONIZED config state for the local Zowe config") { + val localConnectionConfig = ConnectionConfig() + localConnectionConfig.name = "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testProfileName/${projectMock.name}" + localConnectionConfig.zoweConfigPath = "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + localConnectionConfig.url = "$testProtocol://$testHost:$testPort$testBasePath" + localConnectionConfig.isAllowSelfSigned = testIsAllowSelfSigned + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf(localConnectionConfig).stream() + } + + val localZoweConfig: ZoweConfig = mockk { + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns testUsername + every { password } returns testPassword + every { profileName } returns testProfileName + every { basePath } returns testBasePath + every { host } returns testHost + every { zosmfPort } returns testPort + every { protocol } returns testProtocol + every { rejectUnauthorized } returns !testIsAllowSelfSigned + } + ) + } + + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { + zoweConfigService["findExistingConnection"](any(), any()) + } returns localConnectionConfig + zoweConfigService.localZoweConfig = localZoweConfig + + val zoweConfigState = zoweConfigService.getZoweConfigState(false, ZoweConfigType.LOCAL) + + assertSoftly { zoweConfigState shouldBe ZoweConfigState.SYNCHRONIZED } + } + + should("return NEED_TO_UPDATE config state for the global Zowe config cause the config is changed") { + val globalConnectionConfig = ConnectionConfig() + globalConnectionConfig.name = "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-$testProfileName" + globalConnectionConfig.zoweConfigPath = System.getProperty("user.home").replace("((\\*)|(/*))$", "") + "/.zowe/" + ZOWE_CONFIG_NAME + globalConnectionConfig.url = "$testProtocol://$testHost:$testPort$testBasePath" + globalConnectionConfig.isAllowSelfSigned = true + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf(globalConnectionConfig).stream() + } + + val globalZoweConfig: ZoweConfig = mockk { + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns testUsername + every { password } returns testPassword + every { profileName } returns testProfileName + every { basePath } returns testBasePath + every { host } returns testHost + every { zosmfPort } returns testPort + every { protocol } returns testProtocol + every { rejectUnauthorized } returns true + } + ) + } + + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { + zoweConfigService["findExistingConnection"](any(), any()) + } returns globalConnectionConfig + zoweConfigService.globalZoweConfig = globalZoweConfig + + val zoweConfigState = zoweConfigService.getZoweConfigState(false, ZoweConfigType.GLOBAL) + + assertSoftly { zoweConfigState shouldBe ZoweConfigState.NEED_TO_UPDATE } + } + + should("return NEED_TO_ADD config state for the local Zowe config cause there is no connection configs at all") { + val localConnectionConfig = ConnectionConfig() + localConnectionConfig.name = "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}" + localConnectionConfig.zoweConfigPath = "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + localConnectionConfig.url = "$testProtocol://$testHost:$testPort$testBasePath" + localConnectionConfig.isAllowSelfSigned = testIsAllowSelfSigned + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf().stream() + } + + val localZoweConfig: ZoweConfig = mockk { + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns testUsername + every { password } returns testPassword + every { profileName } returns testProfileName + every { basePath } returns testBasePath + every { host } returns testHost + every { zosmfPort } returns testPort + every { protocol } returns testProtocol + every { rejectUnauthorized } returns !testIsAllowSelfSigned + } + ) + } + + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { + zoweConfigService["findExistingConnection"](any(), any()) + } returns localConnectionConfig + zoweConfigService.localZoweConfig = localZoweConfig + + val zoweConfigState = zoweConfigService.getZoweConfigState(false, ZoweConfigType.LOCAL) + + assertSoftly { zoweConfigState shouldBe ZoweConfigState.NEED_TO_ADD } + } + + should("return NEED_TO_ADD config state for the local Zowe config cause there is no related connection config") { + val localConnectionConfig = ConnectionConfig() + localConnectionConfig.name = "$ZOWE_PROJECT_PREFIX${ZoweConfigType.LOCAL}-zosmf/${projectMock.name}" + localConnectionConfig.zoweConfigPath = "${projectMock.basePath}/$ZOWE_CONFIG_NAME" + localConnectionConfig.url = "$testProtocol://$testHost:$testPort$testBasePath" + localConnectionConfig.isAllowSelfSigned = testIsAllowSelfSigned + + every { + configServiceCrudableMock.find(any>(), any>()) + } answers { + listOf(localConnectionConfig).stream() + } + + val localZoweConfig: ZoweConfig = mockk { + every { + getListOfZosmfConections() + } returns listOf( + mockk { + every { user } returns testUsername + every { password } returns testPassword + every { profileName } returns testProfileName + every { basePath } returns testBasePath + every { host } returns testHost + every { zosmfPort } returns testPort + every { protocol } returns testProtocol + every { rejectUnauthorized } returns !testIsAllowSelfSigned + } + ) + } + + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { + zoweConfigService["findExistingConnection"](any(), any()) + } returns null + zoweConfigService.localZoweConfig = localZoweConfig + + val zoweConfigState = zoweConfigService.getZoweConfigState(false, ZoweConfigType.LOCAL) + + assertSoftly { zoweConfigState shouldBe ZoweConfigState.NEED_TO_ADD } + } + + should("return NOT_EXISTS config state for the local Zowe config cause there is no local config found") { + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + every { zoweConfigService["scanForZoweConfig"](any()) } returns null + zoweConfigService.localZoweConfig = null + + val zoweConfigState = zoweConfigService.getZoweConfigState(true, ZoweConfigType.LOCAL) + + assertSoftly { zoweConfigState shouldBe ZoweConfigState.NOT_EXISTS } + } + + should("return NOT_EXISTS config state for the global Zowe config cause there is no global config found") { + val zoweConfigService = spyk(ZoweConfigServiceImpl(projectMock), recordPrivateCalls = true) + zoweConfigService.globalZoweConfig = null + + val zoweConfigState = zoweConfigService.getZoweConfigState(false, ZoweConfigType.GLOBAL) + + assertSoftly { zoweConfigState shouldBe ZoweConfigState.NOT_EXISTS } + } + } + } +}) \ No newline at end of file