// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.s3.objectActions

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.verify
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeContinuationNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeDirectoryNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeErrorNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeObjectNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeObjectVersionNode
import java.time.Instant

class RefreshTreeActionTest : ObjectActionTestBase() {
    override val sut = RefreshTreeAction()

    @Test
    fun `refresh tree action is enabled on empty selection`() {
        assertThat(sut.updateAction(emptyList()).isEnabled).isTrue
    }

    @Test
    fun `refresh tree action is enabled on directory selection`() {
        val nodes = listOf(
            S3TreeDirectoryNode(s3Bucket, null, "path1/")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isTrue
    }

    @Test
    fun `refresh tree action is enabled on object selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isTrue
    }

    @Test
    fun `refresh tree action is disabled on object version selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val obj = S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        val nodes = listOf(
            S3TreeObjectVersionNode(obj, "version", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `refresh tree action is disabled on multiple selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            dir,
            S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `refresh tree action is disabled on error node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeErrorNode(s3Bucket, dir)
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `refresh tree action is disabled on continuation node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeContinuationNode(s3Bucket, dir, "path1/", "marker")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `refresh tree on an object`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val obj = S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        val nodes = listOf(obj)

        sut.executeAction(nodes)

        argumentCaptor<S3TreeNode>().apply {
            verify(treeTable).invalidateLevel(capture())

            assertThat(allValues).hasSize(1)
            assertThat(firstValue).isEqualTo(obj)
        }

        verify(treeTable).refresh()
    }

    @Test
    fun `refresh tree on directory`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(dir)

        sut.executeAction(nodes)

        argumentCaptor<S3TreeNode>().apply {
            verify(treeTable).invalidateLevel(capture())

            assertThat(allValues).hasSize(1)
            assertThat(firstValue).isEqualTo(dir)
        }

        verify(treeTable).refresh()
    }

    @Test
    fun `refresh tree with no select uses root`() {
        sut.executeAction(emptyList())

        argumentCaptor<S3TreeNode>().apply {
            verify(treeTable).invalidateLevel(capture())

            assertThat(allValues).hasSize(1)
            assertThat(firstValue).isEqualTo(treeTable.rootNode)
        }

        verify(treeTable).refresh()
    }
}


// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.s3.objectActions

import com.intellij.openapi.ui.TestDialog
import com.intellij.openapi.ui.TestDialogManager
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyBlocking
import software.aws.toolkits.core.utils.test.aString
import software.aws.toolkits.core.utils.test.retryableAssert
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeContinuationNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeDirectoryNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeErrorNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeObjectNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeObjectVersionNode
import java.time.Instant

class RenameObjectActionTest : ObjectActionTestBase() {
    override val sut = RenameObjectAction()

    @After
    fun tearDown() {
        TestDialogManager.setTestDialog(TestDialog.DEFAULT)
    }

    @Test
    fun `rename object action is disabled on empty selection`() {
        assertThat(sut.updateAction(emptyList()).isEnabled).isFalse
    }

    @Test
    fun `rename object action is disabled on directory selection`() {
        val nodes = listOf(
            S3TreeDirectoryNode(s3Bucket, null, "path1/")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `rename object action is enabled on object selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isTrue
    }

    @Test
    fun `rename object action is disabled on object version selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val obj = S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        val nodes = listOf(
            S3TreeObjectVersionNode(obj, "version", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `rename object action is disabled on multiple selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            dir,
            S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `rename action is disabled on error node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeErrorNode(s3Bucket, dir)
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `rename action is disabled on continuation node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeContinuationNode(s3Bucket, dir, "path1/", "marker")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `rename object action works`() {
        val input = aString()
        TestDialogManager.setTestInputDialog { input }

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val obj = S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        val nodes = listOf(obj)

        sut.executeAction(nodes)

        retryableAssert {
            argumentCaptor<String>().apply {
                verifyBlocking(s3Bucket) { renameObject(capture(), capture()) }

                assertThat(allValues).hasSize(2)
                assertThat(firstValue).isEqualTo("path1/obj1")
                assertThat(secondValue).isEqualTo("path1/$input")
            }

            verify(treeTable).invalidateLevel(obj)
            verify(treeTable).refresh()
        }
    }

    @Test
    fun `rename object action can be cancelled`() {
        TestDialogManager.setTestInputDialog { null }

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        )

        sut.executeAction(nodes)

        verifyBlocking(s3Bucket, never()) { renameObject(any(), any()) }
        verify(treeTable, never()).invalidateLevel(any())
        verify(treeTable, never()).refresh()
    }
}


// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.s3.objectActions

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileChooser.FileChooserDialog
import com.intellij.openapi.fileChooser.FileChooserFactory
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.replaceService
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyBlocking
import software.amazon.awssdk.services.s3.model.S3Exception
import software.aws.toolkits.core.utils.test.retryableAssert
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeContinuationNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeDirectoryNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeErrorNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeObjectNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeObjectVersionNode
import java.io.File
import java.time.Instant

class UploadObjectActionTest : ObjectActionTestBase() {
    @Rule
    @JvmField
    val tempFolder = TemporaryFolder()

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    override val sut = UploadObjectAction()

    @Test
    fun `upload object action is enabled on empty selection`() {
        assertThat(sut.updateAction(emptyList()).isEnabled).isTrue
    }

    @Test
    fun `upload object action is enabled on directory selection`() {
        val nodes = listOf(
            S3TreeDirectoryNode(s3Bucket, null, "path1/")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isTrue
    }

    @Test
    fun `upload object action is enabled on object selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `upload object action is disabled on object version selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val obj = S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        val nodes = listOf(
            S3TreeObjectVersionNode(obj, "version", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `upload object action is disabled on multiple selection`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            dir,
            S3TreeObjectNode(dir, "path1/obj1", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `upload action is disabled on error node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeErrorNode(s3Bucket, dir)
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `upload action is disabled on continuation node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeContinuationNode(s3Bucket, dir, "path1/", "marker")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `upload object action does nothing when no files selected`() {
        createFileChooserMock(emptyList())

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(dir)

        sut.executeAction(nodes)

        retryableAssert {
            verifyBlocking(s3Bucket, never()) {
                upload(any(), any(), any())
            }
            verify(treeTable, never()).invalidateLevel(any())
            verify(treeTable, never()).refresh()
        }
    }

    @Test
    fun `upload object action works for a single file`() {
        val newFile = tempFolder.newFile()
        createFileChooserMock(listOf(newFile))

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(dir)

        sut.executeAction(nodes)

        retryableAssert {
            argumentCaptor<String>().apply {
                verifyBlocking(s3Bucket) { upload(any(), any(), capture()) }

                assertThat(allValues).hasSize(1)
                assertThat(firstValue).isEqualTo("path1/${newFile.name}")
            }

            verify(treeTable).invalidateLevel(dir)
            verify(treeTable).refresh()
        }
    }

    @Test
    fun `upload object action works for multiple files`() {
        val newFile = tempFolder.newFile()
        val newFile2 = tempFolder.newFile()
        createFileChooserMock(listOf(newFile, newFile2))

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(dir)

        sut.executeAction(nodes)

        retryableAssert {
            argumentCaptor<String>().apply {
                verifyBlocking(s3Bucket, times(2)) { upload(any(), any(), capture()) }

                assertThat(firstValue).isEqualTo("path1/${newFile.name}")
                assertThat(secondValue).isEqualTo("path1/${newFile2.name}")
            }

            verify(treeTable).invalidateLevel(dir)
            verify(treeTable).refresh()
        }
    }

    @Test
    fun `upload object action skips on a folder`() {
        val newFile = tempFolder.newFile()
        val newFolder = tempFolder.newFolder()
        val newFile2 = tempFolder.newFile()
        createFileChooserMock(listOf(newFile, newFolder, newFile2))

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(dir)

        sut.executeAction(nodes)

        retryableAssert {
            argumentCaptor<String>().apply {
                verifyBlocking(s3Bucket, times(2)) { upload(any(), any(), capture()) }

                assertThat(firstValue).isEqualTo("path1/${newFile.name}")
                assertThat(secondValue).isEqualTo("path1/${newFile2.name}")
            }
            verify(treeTable).invalidateLevel(dir)
            verify(treeTable).refresh()
        }
    }

    @Test
    fun `upload object action aborts on an error`() {
        val newFile = tempFolder.newFile()
        val newFile2 = tempFolder.newFile()
        val newFile3 = tempFolder.newFile()
        createFileChooserMock(listOf(newFile, newFile2, newFile3))

        s3Bucket.stub {
            onBlocking {
                upload(any(), any(), any())
            }.thenReturn(null).thenThrow(S3Exception.builder().message("Test exception").build())
        }

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(dir)

        sut.executeAction(nodes)

        retryableAssert {
            argumentCaptor<String>().apply {
                verifyBlocking(s3Bucket, times(2)) { upload(any(), any(), capture()) }

                assertThat(firstValue).isEqualTo("path1/${newFile.name}")
            }
            verify(treeTable).invalidateLevel(dir)
            verify(treeTable).refresh()
        }
    }

    @Test
    fun `upload object action doesn't refresh if nothing changed`() {
        val newFolder = tempFolder.newFolder()
        createFileChooserMock(listOf(newFolder))

        s3Bucket.stub {
            onBlocking {
                upload(any(), any(), any())
            }.thenReturn(null).thenThrow(S3Exception.builder().message("Test exception").build())
        }

        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(dir)

        sut.executeAction(nodes)

        retryableAssert {
            verifyBlocking(s3Bucket, never()) { upload(any(), any(), any()) }
            verify(treeTable, never()).invalidateLevel(dir)
            verify(treeTable, never()).refresh()
        }
    }

    @Test
    fun `upload object action with no selection uploads to root`() {
        val newFile = tempFolder.newFile()
        createFileChooserMock(listOf(newFile))

        sut.executeAction(emptyList())

        retryableAssert {
            argumentCaptor<String>().apply {
                verifyBlocking(s3Bucket) { upload(any(), any(), capture()) }

                assertThat(allValues).hasSize(1)
                assertThat(firstValue).isEqualTo(newFile.name)
            }

            verify(treeTable).invalidateLevel(treeTable.rootNode)
            verify(treeTable).refresh()
        }
    }

    private fun createFileChooserMock(files: List<File>) {
        val dialog = object : FileChooserDialog {
            @Deprecated("needs to be implemented, but interface doesn't provide default impl")
            override fun choose(toSelect: VirtualFile?, project: Project?): Array<VirtualFile> = toSelect?.let {
                choose(project, it)
            } ?: choose(project)

            override fun choose(project: Project?, vararg toSelect: VirtualFile): Array<VirtualFile> {
                val lfs = LocalFileSystem.getInstance()
                return files.mapNotNull {
                    lfs.refreshAndFindFileByIoFile(it)
                }.toTypedArray()
            }
        }

        val mock = mock<FileChooserFactory> {
            on { createFileChooser(any(), anyOrNull(), anyOrNull()) }.thenReturn(dialog)
        }

        ApplicationManager.getApplication().replaceService(FileChooserFactory::class.java, mock, disposableRule.disposable)
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.s3.objectActions

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.verify
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeContinuationNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeDirectoryNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeErrorNode
import software.aws.toolkits.jetbrains.services.s3.editor.S3TreeObjectNode
import java.time.Instant

class ViewObjectVersionActionTest : ObjectActionTestBase() {
    override val sut = ViewObjectVersionAction()

    @Test
    fun `show history is disabled on empty selection`() {
        assertThat(sut.updateAction(emptyList()).isEnabled).isFalse
    }

    @Test
    fun `show history is disabled on directory selection`() {
        val nodes = listOf(
            S3TreeDirectoryNode(s3Bucket, null, "path1/")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `show history is enabled on object selection`() {
        val dirNode = S3TreeDirectoryNode(s3Bucket, null, "")
        val nodes = listOf(
            S3TreeObjectNode(dirNode, "testKey", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isTrue
    }

    @Test
    fun `show history is disabled on object version selection`() {
        val nodes = listOf(
            S3TreeDirectoryNode(s3Bucket, null, "path1/")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `show history is disabled on multiple selection`() {
        val dirNode = S3TreeDirectoryNode(s3Bucket, null, "")
        val nodes = listOf(
            S3TreeObjectNode(dirNode, "testKey2", 1, Instant.now()),
            S3TreeObjectNode(dirNode, "testKey", 1, Instant.now())
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `show history action is disabled on error node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeErrorNode(s3Bucket, dir)
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `show history action is disabled on continuation node`() {
        val dir = S3TreeDirectoryNode(s3Bucket, null, "path1/")
        val nodes = listOf(
            S3TreeContinuationNode(s3Bucket, dir, "path1/", "marker")
        )

        assertThat(sut.updateAction(nodes).isEnabled).isFalse
    }

    @Test
    fun `show object history on object node`() {
        val dirNode = S3TreeDirectoryNode(s3Bucket, null, "")
        val objectNode = S3TreeObjectNode(dirNode, "testKey", 1, Instant.now())

        assertThat(objectNode.showHistory).isFalse

        sut.executeAction(listOf(objectNode))

        assertThat(objectNode.showHistory).isTrue
        verify(treeTable).refresh()
    }
}


// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.schemas

import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.amazon.awssdk.services.schemas.model.RegistrySummary
import software.amazon.awssdk.services.schemas.model.SchemaSummary
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.services.schemas.resources.SchemasResources
import java.util.concurrent.CompletableFuture

class SchemaRegistryNodeTest {

    @JvmField
    @Rule
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val mockClientManager = MockClientManagerRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Test
    fun showRegistrySchemas() {
        val registry = "aws.events"
        val node = aSchemaRegistryNode(registry)

        val schema1 = "schema1"
        val schema2 = "schema2"
        registryWithSchemas(
            registry,
            listOf(
                schema1,
                schema2
            )
        )

        assertThat(node.children).hasSize(2)
        assertThat(node.children).hasOnlyElementsOfType(SchemaNode::class.java)

        assertThat(node.children.map { it.displayName() }).contains(schema1, schema2)
    }

    private fun aSchemaRegistryNode(registry: String) = SchemaRegistryNode(projectRule.project, RegistrySummary.builder().registryName(registry).build())

    private fun registryWithSchemas(registryName: String, schemas: List<String>) {
        resourceCache.addEntry(
            projectRule.project,
            SchemasResources.listSchemas(registryName),
            CompletableFuture.completedFuture(
                schemas.map {
                    SchemaSummary.builder()
                        .schemaName(it)
                        .build()
                }
            )
        )
    }
}


// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.schemas

import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.amazon.awssdk.services.schemas.model.RegistrySummary
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.SchemasExplorerRootNode
import software.aws.toolkits.jetbrains.services.schemas.resources.SchemasResources
import java.util.concurrent.CompletableFuture

class SchemasServiceNodeTest {

    @JvmField
    @Rule
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Test
    fun registriesAreShown() {
        val registry1 = "Registry1"
        val registry2 = "aws.events"
        registries(listOf(registry1, registry2))

        val node = SchemasServiceNode(projectRule.project, SCHEMAS_EXPLORER_NODE)

        assertThat(node.children).hasSize(2)

        assertThat(node.children.map { it.displayName() }).contains(registry1, registry2)
    }

    @Test
    fun noRegistriesShowsEmptyNode() {
        registries(emptyList())

        val node = SchemasServiceNode(projectRule.project, SCHEMAS_EXPLORER_NODE)

        assertThat(node.children).hasOnlyElementsOfType(AwsExplorerEmptyNode::class.java)
    }

    private fun registries(names: List<String>) {
        resourceCache.addEntry(
            projectRule.project,
            SchemasResources.LIST_REGISTRIES,
            CompletableFuture.completedFuture(
                names.map {
                    RegistrySummary.builder()
                        .registryName(it)
                        .build()
                }
            )
        )
    }

    private companion object {
        val SCHEMAS_EXPLORER_NODE = SchemasExplorerRootNode()
    }
}


// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.schemas

import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import software.amazon.awssdk.services.schemas.model.DescribeSchemaResponse
import software.aws.toolkits.core.ConnectionSettings
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.credentials.MockCredentialManagerRule
import software.aws.toolkits.jetbrains.core.region.MockRegionProviderRule
import software.aws.toolkits.jetbrains.services.schemas.resources.SchemasResources
import software.aws.toolkits.jetbrains.utils.rules.NotificationListenerRule
import java.util.concurrent.CompletableFuture.completedFuture

class SchemasViewerTest {

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Rule
    @JvmField
    val mockCredentialManager = MockCredentialManagerRule()

    @Rule
    @JvmField
    val mockRegionProvider = MockRegionProviderRule()

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    @Rule
    @JvmField
    val notificationListener = NotificationListenerRule(projectRule, disposableRule.disposable)

    private lateinit var fileEditorManager: FileEditorManager

    @Before
    fun setup() {
        fileEditorManager = FileEditorManager.getInstance(projectRule.project)
    }

    @After
    fun cleanUp() {
        runInEdtAndWait {
            fileEditorManager.openFiles.forEach { fileEditorManager.closeFile(it) }
        }
    }

    @Test
    fun canGetSchemaContent() {
        val schemaResponse = DescribeSchemaResponse.builder()
            .content(AWS_EVENT_SCHEMA_RAW)
            .build()

        val connectionSettings = ConnectionSettings(mockCredentialManager.createCredentialProvider(), mockRegionProvider.createAwsRegion())
        mockSchemaCache(connectionSettings, schemaResponse)

        val actualResponse = SchemaDownloader().getSchemaContent(
            REGISTRY,
            SCHEMA,
            connectionSettings = connectionSettings
        ).toCompletableFuture().get()

        assertThat(actualResponse).isEqualTo(schemaResponse)
    }

    @Test
    fun canPrettyPrintSchemaContent() {
        val prettySchema = SchemaFormatter().prettySchemaContent(AWS_EVENT_SCHEMA_RAW).toCompletableFuture().get()

        assertThat(prettySchema.trim()).isEqualToNormalizingNewlines(AWS_EVENT_SCHEMA_PRETTY.trim())
    }

    @Test
    fun canOpenFileDialog() {
        val connectionSettings = ConnectionSettings(mockCredentialManager.createCredentialProvider(), mockRegionProvider.createAwsRegion())

        SchemaPreviewer().openFileInEditor(REGISTRY, SCHEMA, AWS_EVENT_SCHEMA_PRETTY, VERSION, projectRule.project, connectionSettings)
            .toCompletableFuture()
            .get()

        assertThat(fileEditorManager.openFiles).hasOnlyOneElementSatisfying { assertThat(it.name).isEqualTo(getSchemaFileName(connectionSettings, SCHEMA)) }
    }

    @Test
    fun canOpenFileDialogLongSchemaName() {
        val connectionSettings = ConnectionSettings(mockCredentialManager.createCredentialProvider(), mockRegionProvider.createAwsRegion())

        runInEdtAndGet {
            SchemaPreviewer().openFileInEditor(REGISTRY, SCHEMA_SUPER_LONG_NAME, AWS_EVENT_SCHEMA_PRETTY, VERSION, projectRule.project, connectionSettings)
                .toCompletableFuture()
        }.get()

        val trimmedSchemaFileName = getSchemaFileName(connectionSettings, SCHEMA_SUPER_LONG_NAME).substring(0, SchemaPreviewer.MAX_FILE_LENGTH) +
            SchemaPreviewer.SCHEMA_EXTENSION
        assertThat(fileEditorManager.openFiles).hasOnlyOneElementSatisfying { assertThat(it.name).isEqualTo(trimmedSchemaFileName) }
    }

    @Test
    fun canOpenFileDialogSchemaNameWithSpecialCharacters() {
        val connectionSettings = ConnectionSettings(mockCredentialManager.createCredentialProvider(), mockRegionProvider.createAwsRegion())

        runInEdtAndGet {
            SchemaPreviewer().openFileInEditor(REGISTRY, SCHEMA_SPECIAL_CHARACTER, AWS_EVENT_SCHEMA_PRETTY, VERSION, projectRule.project, connectionSettings)
                .toCompletableFuture()
        }.get()

        assertThat(fileEditorManager.openFiles).hasOnlyOneElementSatisfying {
            assertThat(it.name).isEqualTo(getSchemaFileName(connectionSettings, SCHEMA_SPECIAL_CHARACTER_SANITIZED))
        }
    }

    @Test
    fun canDownloadAndViewSchema() {
        val schemaResponse = DescribeSchemaResponse.builder()
            .content(AWS_EVENT_SCHEMA_RAW)
            .schemaName(SCHEMA)
            .schemaVersion(VERSION)
            .build()

        val connectionSettings = ConnectionSettings(
            mockCredentialManager.createCredentialProvider(),
            mockRegionProvider.createAwsRegion()
        )
        mockSchemaCache(connectionSettings, schemaResponse)

        val mockSchemaDownloader = mock<SchemaDownloader> {
            on {
                getSchemaContent(
                    REGISTRY,
                    SCHEMA,
                    connectionSettings = connectionSettings
                )
            }.thenReturn(completedFuture(schemaResponse))
        }
        val mockSchemaFormatter = mock<SchemaFormatter> {
            on { prettySchemaContent(AWS_EVENT_SCHEMA_RAW) }.thenReturn(completedFuture(AWS_EVENT_SCHEMA_PRETTY))
        }
        val mockSchemaPreviewer = mock<SchemaPreviewer> {
            on {
                openFileInEditor(
                    REGISTRY,
                    SCHEMA,
                    AWS_EVENT_SCHEMA_PRETTY,
                    VERSION,
                    projectRule.project,
                    connectionSettings
                )
            }.thenReturn(completedFuture(null))
        }

        runInEdtAndGet {
            SchemaViewer(projectRule.project, mockSchemaDownloader, mockSchemaFormatter, mockSchemaPreviewer).downloadAndViewSchema(
                SCHEMA,
                REGISTRY,
                connectionSettings
            ).toCompletableFuture()
        }.get()

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(mockSchemaDownloader).getSchemaContent(
            REGISTRY,
            SCHEMA,
            connectionSettings = connectionSettings
        )
        verify(mockSchemaFormatter).prettySchemaContent(AWS_EVENT_SCHEMA_RAW)
        verify(mockSchemaPreviewer).openFileInEditor(REGISTRY, SCHEMA, AWS_EVENT_SCHEMA_PRETTY, VERSION, projectRule.project, connectionSettings)
    }

    private fun mockSchemaCache(connectionSettings: ConnectionSettings, schema: DescribeSchemaResponse) {
        resourceCache.addEntry(connectionSettings, SchemasResources.getSchema(REGISTRY, SCHEMA), completedFuture(schema))
    }

    private fun getSchemaFileName(connectionSettings: ConnectionSettings, schemaName: String) =
        "${connectionSettings.credentials.id}_${connectionSettings.region.id}_${REGISTRY}_${schemaName}_$VERSION.json"

    private companion object {
        private const val REGISTRY = "registry"
        private const val SCHEMA = "schema"
        private const val SCHEMA_SUPER_LONG_NAME =
            "schema12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
                "012345678901234567890123456789012345678901234567890" +
                "1234567890123456789012345678901234567890123456789012" +
                "3456789012345678901234567890"
        private const val SCHEMA_SPECIAL_CHARACTER = "schema:awesome"
        private const val SCHEMA_SPECIAL_CHARACTER_SANITIZED = "schema_awesome"
        private const val VERSION = "2"

        private val AWS_EVENT_SCHEMA_RAW = SchemasViewerTest::class.java.getResourceAsStream("/awsEventSchemaRaw.json.txt")!!.bufferedReader().readText()
        private val AWS_EVENT_SCHEMA_PRETTY = SchemasViewerTest::class.java.getResourceAsStream("/awsEventSchemaPretty.json.txt")!!.bufferedReader().readText()
    }
}


// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.schemas.code

import com.intellij.notification.NotificationType
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import software.amazon.awssdk.services.schemas.model.SchemaVersionSummary
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.services.schemas.Schema
import software.aws.toolkits.jetbrains.services.schemas.SchemaCodeLangs
import software.aws.toolkits.jetbrains.services.schemas.SchemaSummary
import software.aws.toolkits.jetbrains.services.schemas.resources.SchemasResources
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.NotificationListenerRule
import software.aws.toolkits.jetbrains.utils.rules.PyTestSdk
import software.aws.toolkits.resources.message
import java.io.File
import java.util.concurrent.CompletableFuture.completedFuture
import java.util.concurrent.CompletableFuture.failedFuture
import java.util.function.Function

class DownloadCodeForSchemaDialogTest {

    @JvmField
    @Rule
    val projectRule = JavaCodeInsightTestFixtureRule()

    @JvmField
    @Rule
    val mockClientManager = MockClientManagerRule()

    @Rule
    @JvmField
    val tempFolder = TemporaryFolder()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    @Rule
    @JvmField
    val notificationListener = NotificationListenerRule(projectRule, disposableRule.disposable)

    private lateinit var fileEditorManager: FileEditorManager

    private val schemaCodeDownloader = mock<SchemaCodeDownloader>()

    @Before
    fun setup() {
        fileEditorManager = FileEditorManager.getInstance(projectRule.project)

        mockSchemaVersions()
    }

    @After
    fun cleanUp() {
        runInEdtAndWait {
            fileEditorManager.openFiles.forEach { fileEditorManager.closeFile(it) }
        }
    }

    @Test
    fun versionComboIncludesLatestVersionFirst() {
        runInEdtAndWait {
            val dialog = DownloadCodeForSchemaDialog(projectRule.project, SCHEMA)

            assertThat(dialog.view.version.itemCount).isEqualTo(VERSIONS.size + 1)
            assertThat(dialog.latestVersion).isEqualTo(LATEST)
            assertThat(dialog.view.version.selectedItem).isEqualTo(DownloadCodeForSchemaDialog.LATEST_VERSION)
        }

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.ERROR }
            .isEmpty()

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.INFORMATION }
            .isEmpty()
    }

    @Test
    fun languageComboDetectsJavaRuntime() {
        initJavaSdk()

        runInEdtAndWait {
            val dialog = DownloadCodeForSchemaDialog(projectRule.project, SCHEMA)

            assertThat(dialog.view.language.selectedItem).isEqualTo(SchemaCodeLangs.JAVA8)
        }

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.ERROR }
            .isEmpty()

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.INFORMATION }
            .isEmpty()
    }

    @Test
    fun languageComboDetectsPythonRuntime() {
        initPythonSdk()

        runInEdtAndWait {
            val dialog = DownloadCodeForSchemaDialog(projectRule.project, SCHEMA)

            assertThat(dialog.view.language.selectedItem).isEqualTo(SchemaCodeLangs.PYTHON3_6)
        }

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.ERROR }
            .isEmpty()

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.INFORMATION }
            .isEmpty()
    }

    @Test
    fun okActionTriggersDownloaderFileOpenInEditorAndSuccessNotification() {
        initJavaSdk()

        val newFolder = tempFolder.newFolder()
        val testFile = File(newFolder, "test123")
        testFile.createNewFile()
        testFile.writeText("test123")
        val fileName = testFile.name

        schemaCodeDownloader.stub {
            on { downloadCode(any(), any()) }.thenReturn(completedFuture(testFile.toPath()))
        }

        runInEdtAndWait {
            val dialog = DownloadCodeForSchemaDialog(projectRule.project, SCHEMA)
            selectDialogDefaults(dialog, newFolder.absolutePath)

            dialog.downloadSchemaCode(schemaCodeDownloader)
        }

        val request = SchemaCodeDownloadRequestDetails(SchemaSummary(SCHEMA_NAME, REGISTRY), VERSION, LANGUAGE, newFolder)
        verify(schemaCodeDownloader).downloadCode(eq(request), any())
        assertThat(fileEditorManager.openFiles).hasOnlyOneElementSatisfying { assertThat(it.name).isEqualTo(fileName) }

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.ERROR }
            .isEmpty()

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.INFORMATION }
            .extracting(Function { t -> t.content })
            .containsOnly(
                message("schemas.schema.download_code_bindings.notification.start", SCHEMA_NAME),
                message("schemas.schema.download_code_bindings.notification.finished", SCHEMA_NAME)
            )
    }

    @Test
    fun downloaderExceptionsShown() {
        initJavaSdk()

        val newFolder = tempFolder.newFolder()

        val exception = SchemaCodeDownloadFileCollisionException(SCHEMA_NAME)

        schemaCodeDownloader.stub {
            on { downloadCode(any(), any()) }.thenReturn(failedFuture(exception))
        }

        runInEdtAndWait {
            val dialog = DownloadCodeForSchemaDialog(projectRule.project, SCHEMA)
            selectDialogDefaults(dialog, newFolder.absolutePath)

            dialog.downloadSchemaCode(schemaCodeDownloader)
        }

        assertThat(fileEditorManager.openFiles.size).isEqualTo(0)

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.ERROR }
            .extracting(Function { t -> t.content })
            .containsOnly(exception.message)

        assertThat(notificationListener.notifications)
            .filteredOn { it.type == NotificationType.INFORMATION }
            .extracting(Function { t -> t.content })
            .containsOnly(message("schemas.schema.download_code_bindings.notification.start", SCHEMA_NAME))
    }

    private fun selectDialogDefaults(
        dialog: DownloadCodeForSchemaDialog,
        path: String
    ) {
        dialog.view.location.text = path
        dialog.view.version.selectedItem = VERSION
        dialog.view.language.selectedItem = LANGUAGE
    }

    private fun initJavaSdk() {
        val sdk = IdeaTestUtil.getMockJdk18()
        runInEdtAndWait {
            runWriteAction {
                ProjectJdkTable.getInstance().addJdk(sdk, projectRule.fixture.projectDisposable)
                ProjectRootManager.getInstance(projectRule.project).projectSdk = sdk
            }
        }
    }

    private fun initPythonSdk() {
        val sdk = PyTestSdk("3.7.0")
        runInEdtAndWait {
            runWriteAction {
                ProjectJdkTable.getInstance().addJdk(sdk, projectRule.fixture.projectDisposable)
                ProjectRootManager.getInstance(projectRule.project).projectSdk = sdk
            }
        }
    }

    private fun mockSchemaVersions() {
        resourceCache.addEntry(
            projectRule.project,
            SchemasResources.getSchemaVersions(REGISTRY, SCHEMA_NAME),
            completedFuture(
                VERSIONS.map { v ->
                    SchemaVersionSummary.builder()
                        .schemaName(SCHEMA_NAME)
                        .schemaVersion(v)
                        .build()
                }
            )
        )
    }

    private companion object {
        private const val REGISTRY = "registry"
        private const val SCHEMA_NAME = "schema"
        private val SCHEMA = Schema(SCHEMA_NAME, REGISTRY, null)
        private const val VERSION = "4"
        private const val LATEST = "5"
        private val VERSIONS = listOf("3", VERSION, LATEST)
        private val LANGUAGE = SchemaCodeLangs.JAVA8
    }
}


// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.schemas.code

import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.ProjectRule
import com.intellij.util.io.Compressor
import org.apache.commons.lang.exception.ExceptionUtils
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.assertj.core.api.Assertions.catchThrowable
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.schemas.SchemasClient
import software.amazon.awssdk.services.schemas.model.CodeGenerationStatus
import software.amazon.awssdk.services.schemas.model.DescribeCodeBindingRequest
import software.amazon.awssdk.services.schemas.model.DescribeCodeBindingResponse
import software.amazon.awssdk.services.schemas.model.GetCodeBindingSourceRequest
import software.amazon.awssdk.services.schemas.model.GetCodeBindingSourceResponse
import software.amazon.awssdk.services.schemas.model.InternalServerErrorException
import software.amazon.awssdk.services.schemas.model.NotFoundException
import software.amazon.awssdk.services.schemas.model.PutCodeBindingRequest
import software.amazon.awssdk.services.schemas.model.PutCodeBindingResponse
import software.aws.toolkits.core.utils.WaiterTimeoutException
import software.aws.toolkits.core.utils.delegateMock
import software.aws.toolkits.jetbrains.services.schemas.SchemaCodeLangs
import software.aws.toolkits.jetbrains.services.schemas.SchemaSummary
import software.aws.toolkits.jetbrains.utils.rules.NotificationListenerRule
import java.io.File
import java.io.FileOutputStream
import java.nio.ByteBuffer
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.concurrent.CompletableFuture.completedFuture
import java.util.concurrent.CompletableFuture.failedFuture

class SchemaCodeDownloaderTest {

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @Rule
    @JvmField
    val tempFolder = TemporaryFolder()

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    @Rule
    @JvmField
    val notificationListener = NotificationListenerRule(projectRule, disposableRule.disposable)

    private lateinit var sourceFolder: File
    private lateinit var zipFile: File
    private lateinit var destinationFolder: File

    private val mockSchemasClient = delegateMock<SchemasClient>()
    private val codeGenerator = mock<CodeGenerator>()
    private val codePoller = mock<CodeGenerationStatusPoller>()
    private val codeDownloader = mock<CodeDownloader>()
    private val codeExtractor = mock<CodeExtractor>()
    private val progressIndicator = mock<ProgressIndicator>()
    private val downloadedSchemaCode = mock<DownloadedSchemaCode>()
    private val schemaCodeCoreFile = mock<Path>()

    @Before
    fun setUp() {
        sourceFolder = tempFolder.newFolder()
        zipFile = tempFolder.newFile()
        destinationFolder = tempFolder.newFolder()
    }

    @Test
    fun canGenerateCode() {
        val putCodeBindingRequest = PutCodeBindingRequest.builder()
            .schemaName(SCHEMA)
            .registryName(REGISTRY)
            .language(LANGUAGE.apiValue)
            .schemaVersion(VERSION)
            .build()

        val putCodeBindingResponse = PutCodeBindingResponse.builder()
            .status(CodeGenerationStatus.CREATE_IN_PROGRESS)
            .schemaVersion(VERSION)
            .build()

        mockSchemasClient.stub {
            on { putCodeBinding(putCodeBindingRequest) }.thenReturn(putCodeBindingResponse)
        }

        val codeGenerationStatus = CodeGenerator(mockSchemasClient).generate(REQUEST).toCompletableFuture().get()

        verify(mockSchemasClient).putCodeBinding(putCodeBindingRequest)
        assertThat(codeGenerationStatus).isEqualTo(CodeGenerationStatus.CREATE_IN_PROGRESS)
    }

    @Test
    fun canGetCurrentCodeGenerationStatus() {
        val describeCodeBindingRequest = DescribeCodeBindingRequest.builder()
            .schemaName(SCHEMA)
            .registryName(REGISTRY)
            .language(LANGUAGE.apiValue)
            .schemaVersion(VERSION)
            .build()

        val expectedCodeGenerationStatus = CodeGenerationStatus.CREATE_IN_PROGRESS
        val describeCodeBindingResponse = DescribeCodeBindingResponse.builder()
            .status(expectedCodeGenerationStatus)
            .schemaVersion(VERSION)
            .build()

        mockSchemasClient.stub {
            on { mockSchemasClient.describeCodeBinding(describeCodeBindingRequest) }.thenReturn(describeCodeBindingResponse)
        }

        val actualCodeGenerationStatus = CodeGenerationStatusPoller(mockSchemasClient).getCurrentStatus(REQUEST).toCompletableFuture().get()

        verify(mockSchemasClient).describeCodeBinding(describeCodeBindingRequest)
        assertThat(actualCodeGenerationStatus).isEqualTo(expectedCodeGenerationStatus)
    }

    @Test
    fun canPollForCurrentCodeGenerationStatus() {
        val inProgressResponse = DescribeCodeBindingResponse.builder()
            .status(CodeGenerationStatus.CREATE_IN_PROGRESS)
            .schemaVersion(VERSION)
            .build()
        val completedResponse = DescribeCodeBindingResponse.builder()
            .status(CodeGenerationStatus.CREATE_COMPLETE)
            .schemaVersion(VERSION)
            .build()

        mockSchemasClient.stub {
            on { describeCodeBinding(any<DescribeCodeBindingRequest>()) }.thenReturn(inProgressResponse)
                .thenReturn(inProgressResponse)
                .thenReturn(inProgressResponse)
                .thenReturn(completedResponse)
        }

        val createdSchemaName = CodeGenerationStatusPoller(mockSchemasClient).pollForCompletion(REQUEST).toCompletableFuture().get()

        verify(mockSchemasClient, times(4)).describeCodeBinding(any<DescribeCodeBindingRequest>())
        assertThat(createdSchemaName).isEqualTo(SCHEMA)
    }

    @Test
    fun canDownloadGeneratedCode() {
        val getCodeBindingRequest = GetCodeBindingSourceRequest.builder()
            .schemaName(SCHEMA)
            .registryName(REGISTRY)
            .language(LANGUAGE.apiValue)
            .schemaVersion(VERSION)
            .build()

        val zipByteBuffer = mock<ByteBuffer>()

        val sdkBytesResponse = mock<SdkBytes> {
            on { asByteBuffer() }.thenReturn(zipByteBuffer)
        }

        val getCodeBindingResponse = GetCodeBindingSourceResponse.builder()
            .body(sdkBytesResponse)
            .build()

        mockSchemasClient.stub {
            on { getCodeBindingSource(getCodeBindingRequest) }.thenReturn(getCodeBindingResponse)
        }

        val downloadedSchemaCode = CodeDownloader(mockSchemasClient).download(REQUEST).toCompletableFuture().get()

        verify(mockSchemasClient).getCodeBindingSource(getCodeBindingRequest)

        assertThat(downloadedSchemaCode.zipContents).isEqualTo(zipByteBuffer)
    }

    @Test
    fun downloadGeneratedCodeWrapsExceptions() {
        val someException = IllegalStateException()

        mockSchemasClient.stub {
            on { getCodeBindingSource(any<GetCodeBindingSourceRequest>()) }.thenThrow(someException)
        }

        assertThatThrownBy {
            CodeDownloader(mockSchemasClient).download(REQUEST).toCompletableFuture().get()
        }.hasRootCause(someException)
    }

    @Test
    fun downloadGeneratedCodeDoesNotWrapNotFoundException() {
        val notFoundException = NotFoundException.builder().build()

        mockSchemasClient.stub {
            on { getCodeBindingSource(any<GetCodeBindingSourceRequest>()) }.thenThrow(notFoundException)
        }

        assertThatThrownBy {
            CodeDownloader(mockSchemasClient).download(REQUEST).toCompletableFuture().get()
        }.hasRootCause(notFoundException)
    }

    @Test
    fun canExtractZipFile() {
        // Initialize directories and files to put into a zip file
        val directory = File(sourceFolder, ZIP_FOLDER_HIERARCHY)
        val file1 = File(directory, ZIP_FILE_1)
        val file2 = File(directory, ZIP_FILE_2)
        val file3 = File(directory, ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME)
        assertThat(directory.mkdirs()).isTrue
        assertThat(file1.createNewFile()).isTrue
        assertThat(file2.createNewFile()).isTrue
        assertThat(file3.createNewFile()).isTrue

        file1.writeText(SOME_TEXT)
        file2.writeText(SOME_TEXT)
        file3.writeText(SOME_TEXT)

        assertThat(sourceFolder.exists()).isTrue
        assertThat(destinationFolder.exists()).isTrue

        // Write all to zip file, and put in a ByteBuffer
        Compressor.Zip(zipFile).use {
            it.addDirectory(sourceFolder)
        }
        val byteBufferZipContents = fileToByteBuffer(zipFile)
        val downloadedSchemaCode = DownloadedSchemaCode(byteBufferZipContents)

        val realDestinationRequest = SchemaCodeDownloadRequestDetails(SCHEMA_SUMMARY, VERSION, LANGUAGE, destinationFolder)

        // Invoke CodeExtractor which should unzip the files and places in new directory
        val schemaCoreCodeFile = CodeExtractor().extractAndPlace(realDestinationRequest, downloadedSchemaCode).toCompletableFuture().get()

        // Assert zip itself exists
        val destinationFolderFileZipFolder = File(destinationFolder, ZIP_FOLDER_HIERARCHY)
        assertThat(destinationFolderFileZipFolder.exists()).isTrue

        // Assert files unzipped and exist
        val extractedFile1 = Paths.get(destinationFolderFileZipFolder.path, ZIP_FILE_1).toFile()
        val extractedFile2 = Paths.get(destinationFolderFileZipFolder.path, ZIP_FILE_2).toFile()
        val extractedFile3 = Paths.get(destinationFolderFileZipFolder.path, ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME).toFile()

        assertThat(extractedFile1.exists()).isTrue
        assertThat(extractedFile2.exists()).isTrue
        assertThat(extractedFile3.exists()).isTrue

        // Assert the schema core code file exists, and is returned
        assertThat(schemaCoreCodeFile).isNotNull
        schemaCoreCodeFile?.let {
            assertThat(Files.exists(schemaCoreCodeFile)).isTrue
            assertThat(schemaCoreCodeFile.fileName.toString()).isEqualTo(ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME)
        }
    }

    @Test
    fun canValidateZipFileDirectoryFileContentsClash() {
        // Initialize directories and files to put into a zip file
        val sourceDirectory = File(sourceFolder, ZIP_FOLDER_HIERARCHY)
        val sourceFile = File(sourceDirectory, ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME)

        // Initialize a destination directory and files that clash with source contents
        val destinationDirectory = File(destinationFolder, ZIP_FOLDER_HIERARCHY)
        val destinationFile = File(destinationDirectory, ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME)

        assertThat(sourceDirectory.mkdirs()).isTrue
        assertThat(sourceFile.createNewFile()).isTrue

        assertThat(destinationDirectory.mkdirs()).isTrue
        assertThat(destinationFile.createNewFile()).isTrue

        // Write some known content to the file in order to detect that it's not overwritten later
        val expectedExistingContent = "ExistingContentInFile"
        FileOutputStream(destinationFile).channel.use { fileChannel ->
            fileChannel.write(ByteBuffer.wrap(expectedExistingContent.toByteArray()))
        }

        // Write all to zip file, and put in a ByteBuffer
        Compressor.Zip(zipFile).use {
            it.addDirectory(sourceFolder)
        }
        val byteBufferZipContents = fileToByteBuffer(zipFile)
        val downloadedSchemaCode = DownloadedSchemaCode(byteBufferZipContents)

        val realDestinationRequest = SchemaCodeDownloadRequestDetails(SCHEMA_SUMMARY, VERSION, LANGUAGE, destinationFolder)

        // Invoke CodeExtractor which should throw an exception and not overwrite the destination files
        val exception = catchThrowable { CodeExtractor().extractAndPlace(realDestinationRequest, downloadedSchemaCode).toCompletableFuture().get() }

        assertThat(ExceptionUtils.getRootCause(exception)).isInstanceOf(SchemaCodeDownloadFileCollisionException::class.java)

        // Directory and File already exists
        assertThat(destinationDirectory.exists()).isTrue
        assertThat(destinationFile.exists()).isTrue

        // Read the file and verify it's the unmodified content
        val actualContent = String(Files.readAllBytes(destinationFile.toPath()))
        assertThat(actualContent).isEqualTo(expectedExistingContent)
    }

    @Test
    fun canValidateZipFileDirectoryDirectoryOnlyClashes() {
        // Initialize directories and files to put into a zip file
        val sourceDirectory = File(sourceFolder, ZIP_FOLDER_HIERARCHY)
        val sourceFile = File(sourceDirectory, ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME)

        // Initialize a destination directory and files that clash with source contents
        val destinationDirectory = File(destinationFolder, ZIP_FOLDER_HIERARCHY)
        val destinationFile = File(destinationDirectory, ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME)

        assertThat(sourceDirectory.mkdirs()).isTrue
        assertThat(sourceFile.createNewFile()).isTrue

        assertThat(destinationDirectory.mkdirs()).isTrue
        assertThat(destinationDirectory.isDirectory).isTrue
        assertThat(destinationFile.exists()).isFalse

        // Write all to zip file, and put in a ByteBuffer
        Compressor.Zip(zipFile).use {
            it.addDirectory(sourceFolder)
        }
        val byteBufferZipContents = fileToByteBuffer(zipFile)
        val downloadedSchemaCode = DownloadedSchemaCode(byteBufferZipContents)

        val realDestinationRequest = SchemaCodeDownloadRequestDetails(SCHEMA_SUMMARY, VERSION, LANGUAGE, destinationFolder)

        // Invoke CodeExtractor which should not throw any
        CodeExtractor().extractAndPlace(realDestinationRequest, downloadedSchemaCode).toCompletableFuture().get()

        // Directory and File already exist
        assertThat(destinationDirectory.exists()).isTrue
        assertThat(destinationFile.exists()).isTrue
    }

    @Test
    fun canDownloadCodeFirstInvocationPath() {
        standardDependencyMockInitialization()

        val schema = SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
            .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        assertThat(schema).isEqualTo(schemaCodeCoreFile)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codePoller).pollForCompletion(REQUEST)
        verify(codeDownloader, times(2)).download(REQUEST)
        verify(codeExtractor).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(4)).text = any()
    }

    @Test
    fun canDownloadCodeCodeGeneratedImmediately() {
        standardDependencyMockInitialization()

        codeGenerator.stub {
            on { generate(REQUEST) }.thenReturn(completedFuture(CodeGenerationStatus.CREATE_COMPLETE))
        }

        val schema = SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
            .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        assertThat(schema).isEqualTo(schemaCodeCoreFile)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codePoller, times(0)).pollForCompletion(REQUEST)
        verify(codeDownloader, times(2)).download(REQUEST)
        verify(codeExtractor).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(4)).text = any()
    }

    @Test
    fun canDownloadAlreadyGeneratedCode() {
        standardDependencyMockInitialization()

        codeDownloader.stub {
            on { download(REQUEST) }.thenReturn(completedFuture(downloadedSchemaCode))
        }

        val schema = SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
            .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        assertThat(schema).isEqualTo(schemaCodeCoreFile)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator, times(0)).generate(REQUEST)
        verify(codePoller, times(0)).pollForCompletion(REQUEST)
        verify(codeDownloader).download(REQUEST)
        verify(codeExtractor).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(2)).text = any()
    }

    @Test
    fun canDownloadHandleFailedToGenerateCodeCall() {
        standardDependencyMockInitialization()

        val someException = InternalServerErrorException.builder().build()

        codeGenerator.stub {
            on { generate(REQUEST) }.thenReturn(failedFuture(someException))
        }

        assertThatThrownBy {
            SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
                .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        }.hasRootCause(someException)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codePoller, times(0)).pollForCompletion(REQUEST)
        verify(codeDownloader).download(REQUEST)
        verify(codeExtractor, times(0)).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(2)).text = any()
    }

    @Test
    fun canDownloadHandleFailedToPollCode() {
        standardDependencyMockInitialization()

        val someException = InternalServerErrorException.builder().build()

        codePoller.stub {
            on { pollForCompletion(REQUEST) }.thenReturn(failedFuture(someException))
        }

        assertThatThrownBy {
            SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
                .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        }.hasRootCause(someException)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codeDownloader).download(REQUEST)
        verify(codePoller).pollForCompletion(REQUEST)
        verify(codeExtractor, times(0)).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(2)).text = any()
    }

    @Test
    fun canDownloadHandleFailedToPollCodeTimeout() {
        standardDependencyMockInitialization()

        val waiterTimeoutException = WaiterTimeoutException("")

        codePoller.stub {
            on { pollForCompletion(REQUEST) }.thenReturn(failedFuture(waiterTimeoutException))
        }

        assertThatThrownBy {
            SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
                .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        }.hasRootCause(waiterTimeoutException)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codePoller).pollForCompletion(REQUEST)
        verify(codeDownloader).download(REQUEST)
        verify(codeExtractor, times(0)).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(2)).text = any()
    }

    @Test
    fun canDownloadHandleFailedToDownloadCodeFirstCall() {
        standardDependencyMockInitialization()

        val someException = InternalServerErrorException.builder().build()
        codeDownloader.stub {
            on { download(REQUEST) }.thenReturn(failedFuture(someException))
        }

        assertThatThrownBy {
            SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
                .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        }.hasRootCause(someException)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator, times(0)).generate(REQUEST)
        verify(codePoller, times(0)).pollForCompletion(REQUEST)
        verify(codeDownloader).download(REQUEST)
        verify(codeExtractor, times(0)).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator).text = any()
    }

    @Test
    fun canDownloadHandleFailedToDownloadCodeSecondCall() {
        standardDependencyMockInitialization()

        val notFoundException = NotFoundException.builder().build()
        codeDownloader.stub {
            on { codeDownloader.download(REQUEST) }.thenReturn(failedFuture(notFoundException))
        }

        assertThatThrownBy {
            SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
                .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        }.hasRootCause(notFoundException)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codePoller).pollForCompletion(REQUEST)
        verify(codeDownloader, times(2)).download(REQUEST)
        verify(codeExtractor, times(0)).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(3)).text = any()
    }

    @Test
    fun canDownloadHandleFailedToExtractCode() {
        standardDependencyMockInitialization()

        val someException = InternalServerErrorException.builder().build()

        codeExtractor.stub {
            on { extractAndPlace(REQUEST, downloadedSchemaCode) }.thenReturn(failedFuture(someException))
        }

        assertThatThrownBy {
            SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
                .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        }.hasRootCause(someException)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codePoller).pollForCompletion(REQUEST)
        verify(codeDownloader, times(2)).download(REQUEST)
        verify(codeExtractor).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(4)).text = any()
    }

    @Test
    fun canDownloadHandleCodeHierarchyCollision() {
        standardDependencyMockInitialization()

        val schemaCodeDownloadFileCollisionException = SchemaCodeDownloadFileCollisionException("SomeFile")

        codeExtractor.stub {
            on { extractAndPlace(REQUEST, downloadedSchemaCode) }.thenReturn(failedFuture(schemaCodeDownloadFileCollisionException))
        }

        assertThatThrownBy {
            SchemaCodeDownloader(codeGenerator, codePoller, codeDownloader, codeExtractor)
                .downloadCode(REQUEST, progressIndicator).toCompletableFuture().get()
        }.hasRootCause(schemaCodeDownloadFileCollisionException)

        // Assert no error notifications
        assertThat(notificationListener.notifications).isEmpty()

        verify(codeGenerator).generate(REQUEST)
        verify(codePoller).pollForCompletion(REQUEST)
        verify(codeDownloader, times(2)).download(REQUEST)
        verify(codeExtractor).extractAndPlace(REQUEST, downloadedSchemaCode)
        verify(progressIndicator, times(4)).text = any()
    }

    private fun standardDependencyMockInitialization() {
        codeDownloader.stub {
            on { download(REQUEST) }.thenReturn(failedFuture(NotFoundException.builder().build()))
                .thenReturn(completedFuture(downloadedSchemaCode))
        }

        codeGenerator.stub {
            on { generate(REQUEST) }.thenReturn(completedFuture(CodeGenerationStatus.CREATE_IN_PROGRESS))
        }

        codePoller.stub {
            on { pollForCompletion(eq(REQUEST), any()) }.thenReturn(completedFuture(SCHEMA))
        }

        codeExtractor.stub {
            on { extractAndPlace(REQUEST, downloadedSchemaCode) }.thenReturn(completedFuture(schemaCodeCoreFile))
        }
    }

    private fun fileToByteBuffer(file: File): ByteBuffer = ByteBuffer.wrap(Files.readAllBytes(file.toPath()))

    private companion object {
        private const val ZIP_FOLDER_HIERARCHY = "srcDir/comDir/fooBarDir/"
        private const val ZIP_FILE_1 = "File1.java"
        private const val ZIP_FILE_2 = "File2.java"
        private const val SOME_TEXT = "someText"

        private const val REGISTRY = "registry"
        private const val SCHEMA = "schema"
        private val FAKE_DESTINATION = File("/some/destination/anything/really")
        private val SCHEMA_SUMMARY = SchemaSummary(SCHEMA, REGISTRY)
        private const val VERSION = "2"
        private val LANGUAGE = SchemaCodeLangs.JAVA8
        private val REQUEST = SchemaCodeDownloadRequestDetails(SCHEMA_SUMMARY, VERSION, LANGUAGE, FAKE_DESTINATION)
        private val ZIP_FILE_SCHEMA_CORE_CODE_FILE_NAME = REQUEST.schemaCoreCodeFileName()
    }
}


// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.schemas.search

import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import software.aws.toolkits.jetbrains.services.schemas.SchemaViewer
import software.aws.toolkits.resources.message
import java.util.concurrent.CompletableFuture.completedFuture
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class SchemaSearchDialogTest {
    @Rule
    @JvmField
    val projectRule = ProjectRule()

    private val mockSchemaSearchExecutor = mock<SchemaSearchExecutor>()
    private val mockSchemaViewer = mock<SchemaViewer>()

    @Test
    fun initializeSearchRegistryDialog() {
        val dialog = runInEdtAndGet {
            initSingleRegistryDialog()
        }

        assertInitialDialogState(dialog)
    }

    @Test
    fun initializeSearchAllRegistriesDialog() {
        val dialog = runInEdtAndGet {
            initAllRegistriesDialog()
        }

        assertInitialDialogState(dialog)
    }

    @Test
    fun initializeSearchRegistryDialogFromState() {
        val result1 = SchemaSearchResultWithRegistry(SCHEMA1, VERSIONS, REGISTRY1)
        val result2 = SchemaSearchResultWithRegistry(SCHEMA2, VERSIONS, REGISTRY1)
        val searchResults = listOf(result1, result2)

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasInRegistry(eq(REGISTRY1), eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultReturned>(2).invoke(searchResults)
                latch.countDown()
            }
        }

        mockSchemaViewer.stub {
            on { downloadPrettySchema(eq(SCHEMA2), eq(REGISTRY1), any(), any()) }.thenReturn(completedFuture(SCHEMA2_CONTENTS))
        }

        val dialog = runInEdtAndGet {
            initSingleRegistryDialog(SchemaSearchDialogState(SEARCH_CRITERIA, searchResults, result2, LAST_VERSION))
        }

        latch.wait()

        assertSearchResultSelectedDialogState(
            dialog,
            SCHEMA2_CONTENTS,
            VERSIONS,
            LAST_VERSION
        )
    }

    @Test
    fun initializeSearchAllRegistriesDialogFromState() {
        val result1 = SchemaSearchResultWithRegistry(SCHEMA1, VERSIONS, REGISTRY1)
        val result2 = SchemaSearchResultWithRegistry(SCHEMA2, VERSIONS, REGISTRY2)
        val searchResults = listOf(result1, result2)

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasAcrossAllRegistries(eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultReturned>(2).invoke(searchResults)
                latch.countDown()
            }
        }

        mockSchemaViewer.stub {
            on { downloadPrettySchema(eq(SCHEMA2), eq(REGISTRY2), any(), any()) }.thenReturn(completedFuture(SCHEMA2_CONTENTS))
        }

        val dialog = runInEdtAndGet {
            initAllRegistriesDialog(SchemaSearchDialogState(SEARCH_CRITERIA, searchResults, result2, LAST_VERSION))
        }

        latch.wait()

        assertSearchResultSelectedDialogState(
            dialog,
            SCHEMA2_CONTENTS,
            VERSIONS,
            LAST_VERSION
        )
    }

    @Test
    fun singleRegistrySearchInProgress() {
        val searchingLatch = CountDownLatch(1)
        val waitForAsserts = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasInRegistry(eq(REGISTRY1), eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                searchingLatch.countDown()

                // Make this take a long time, so that we can assert the intermediate state

                waitForAsserts.await()
            }
        }

        val dialog = runInEdtAndGet {
            initSingleRegistryDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        searchingLatch.wait()

        try {
            assertSearchingDialogState(dialog)
        } finally {
            waitForAsserts.countDown()
        }
    }

    @Test
    fun allRegistriesSearchInProgress() {
        val searchingLatch = CountDownLatch(1)
        val waitForAsserts = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasAcrossAllRegistries(eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                searchingLatch.countDown()

                // Make this take a long time, so that we can assert the intermediate state

                waitForAsserts.await()
            }
        }

        val dialog = runInEdtAndGet {
            initAllRegistriesDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        searchingLatch.wait()

        try {
            assertSearchingDialogState(dialog)
        } finally {
            waitForAsserts.countDown()
        }
    }

    @Test
    fun singleRegistrySearchWithResults() {
        val searchResults = listOf(SchemaSearchResultWithRegistry(SCHEMA1, VERSIONS, REGISTRY1))

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasInRegistry(eq(REGISTRY1), eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultReturned>(2).invoke(searchResults)
                latch.countDown()
            }
        }

        val dialog = runInEdtAndGet {
            initSingleRegistryDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        latch.wait()

        assertSearchResultsDialogState(dialog, searchResults, emptyList())
    }

    @Test
    fun singleRegistrySearchError() {
        val searchError = SchemaSearchError(REGISTRY1, "someErrorMessage")

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasInRegistry(eq(REGISTRY1), eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultError>(3).invoke(searchError)
                latch.countDown()
            }
        }

        val dialog = runInEdtAndGet {
            initSingleRegistryDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        latch.wait()

        assertSearchResultsDialogState(dialog, emptyList(), listOf(searchError))
    }

    @Test
    fun allRegistriesSearchWithResults() {
        val searchResultsPart1 = listOf(SchemaSearchResultWithRegistry(SCHEMA1, VERSIONS, REGISTRY1))
        val searchResultsPart2 = listOf(SchemaSearchResultWithRegistry(SCHEMA2, VERSIONS, REGISTRY2))

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasAcrossAllRegistries(eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultReturned>(1).invoke(searchResultsPart1)
                it.getArgument<OnSearchResultReturned>(1).invoke(searchResultsPart2)
                latch.countDown()
            }
        }

        val dialog = runInEdtAndGet {
            initAllRegistriesDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        latch.wait()

        assertSearchResultsDialogState(dialog, searchResultsPart1 + searchResultsPart2, emptyList())
    }

    @Test
    fun allRegistriesSearchError() {
        val searchResultsPart1 = listOf(SchemaSearchResultWithRegistry(SCHEMA1, VERSIONS, REGISTRY1))
        val searchError = SchemaSearchError(REGISTRY1, "someErrorMessage")

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasAcrossAllRegistries(eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultReturned>(1).invoke(searchResultsPart1)
                it.getArgument<OnSearchResultError>(2).invoke(searchError)
                latch.countDown()
            }
        }

        val dialog = runInEdtAndGet {
            initAllRegistriesDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        latch.wait()

        assertSearchResultsDialogState(dialog, searchResultsPart1, listOf(searchError))
    }

    @Test
    fun singleRegistrySearchResultSelected() {
        val searchResults = listOf(SchemaSearchResultWithRegistry(SCHEMA1, VERSIONS, REGISTRY1))

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasInRegistry(eq(REGISTRY1), eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultReturned>(2).invoke(searchResults)
                latch.countDown()
            }
        }

        mockSchemaViewer.stub {
            on { downloadPrettySchema(eq(SCHEMA1), eq(REGISTRY1), any(), any()) }.thenReturn(completedFuture(SCHEMA1_CONTENTS))
        }

        val dialog = runInEdtAndGet {
            initSingleRegistryDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        latch.wait()

        runInEdtAndWait {
            dialog.resultsList.selectedIndex = 0
        }

        assertSearchResultSelectedDialogState(
            dialog,
            SCHEMA1_CONTENTS,
            VERSIONS,
            FIRST_VERSION
        )
    }

    @Test
    fun allRegistriesSearchResultSelected() {
        val searchResults = listOf(
            SchemaSearchResultWithRegistry(SCHEMA1, VERSIONS, REGISTRY1),
            SchemaSearchResultWithRegistry(SCHEMA2, VERSIONS, REGISTRY2)
        )

        val latch = CountDownLatch(1)

        mockSchemaSearchExecutor.stub {
            on { searchSchemasAcrossAllRegistries(eq(SEARCH_CRITERIA), any(), any()) }.thenAnswer {
                it.getArgument<OnSearchResultReturned>(1).invoke(searchResults)
                latch.countDown()
            }
        }

        mockSchemaViewer.stub {
            on { downloadPrettySchema(eq(SCHEMA1), eq(REGISTRY1), any(), any()) }.thenReturn(completedFuture(SCHEMA1_CONTENTS))
        }

        val dialog = runInEdtAndGet {
            initAllRegistriesDialog().also {
                it.searchTextField.text = SEARCH_CRITERIA
            }
        }

        latch.wait()

        runInEdtAndWait {
            dialog.resultsList.selectedIndex = 0
        }

        assertSearchResultSelectedDialogState(
            dialog,
            SCHEMA1_CONTENTS,
            VERSIONS,
            FIRST_VERSION
        )
    }

    private fun initSingleRegistryDialog(state: SchemaSearchDialogState? = null): SchemaSearchSingleRegistryDialog {
        val dialog = SchemaSearchSingleRegistryDialog(
            REGISTRY1,
            projectRule.project,
            mockSchemaSearchExecutor,
            mockSchemaViewer,
            onCancelCallback = { }
        )

        if (state == null) {
            dialog.initializeNew()
        } else {
            dialog.initializeFromState(state)
        }

        return dialog
    }

    private fun initAllRegistriesDialog(state: SchemaSearchDialogState? = null): SchemaSearchAllRegistriesDialog {
        val dialog = SchemaSearchAllRegistriesDialog(
            projectRule.project,
            mockSchemaSearchExecutor,
            mockSchemaViewer,
            onCancelCallback = { }
        )

        if (state == null) {
            dialog.initializeNew()
        } else {
            dialog.initializeFromState(state)
        }

        return dialog
    }

    private fun assertInitialDialogState(dialog: SchemasSearchDialogBase) {
        assertThat(dialog.getDownloadButton()?.isEnabled).isFalse()
        assertThat(dialog.searchTextField.text).isEmpty()
        assertThat(dialog.resultsList.isEmpty).isTrue()
        assertThat(dialog.resultsList.emptyText.text).isEqualTo(message("schemas.search.no_results"))
        assertThat(dialog.versionsCombo.isEditable).isFalse()
        assertThat(dialog.versionsCombo.isEnabled).isFalse()
        assertThat(dialog.previewText.isEditable).isFalse()
        assertThat(dialog.currentSearchErrors).isEmpty()
    }

    private fun assertSearchingDialogState(dialog: SchemasSearchDialogBase) {
        assertThat(dialog.getDownloadButton()?.isEnabled).isFalse()
        assertThat(dialog.previewText.text.isEmpty()).isTrue()
        assertThat(dialog.resultsList.itemsCount).isEqualTo(0)
        assertThat(dialog.resultsList.emptyText.text).isEqualTo(message("schemas.search.searching"))
        assertThat(dialog.versionsCombo.itemCount).isEqualTo(0)
        assertThat(dialog.versionsCombo.isEnabled).isFalse()
        assertThat(dialog.currentSearchErrors).isEmpty()
    }

    private fun assertSearchResultsDialogState(
        dialog: SchemasSearchDialogBase,
        expectedResults: Collection<SchemaSearchResultWithRegistry>,
        searchErrors: Collection<SchemaSearchError>
    ) {
        assertThat(dialog.getDownloadButton()?.isEnabled).isFalse()
        assertThat(dialog.previewText.text.isEmpty()).isTrue()
        assertThat(dialog.resultsList.emptyText.text).isEqualTo(message("schemas.search.no_results"))
        assertThat(dialog.resultsModel.elements().toList()).hasSameElementsAs(expectedResults)
        assertThat(dialog.versionsCombo.itemCount).isEqualTo(0)
        assertThat(dialog.versionsCombo.isEnabled).isFalse()
        assertThat(dialog.currentSearchErrors).hasSameElementsAs(searchErrors)
    }

    private fun assertSearchResultSelectedDialogState(
        dialog: SchemasSearchDialogBase,
        schemaContents: String,
        versions: Collection<String>,
        selectedVersion: String
    ) {
        assertThat(dialog.getDownloadButton()?.isEnabled).isTrue()
        assertThat(dialog.previewText.text).isEqualTo(schemaContents)
        assertThat(dialog.versionsCombo.itemCount).isEqualTo(VERSIONS.size)
        assertThat(dialog.versionsCombo.isEnabled).isTrue()
        assertThat(dialog.versionsModel.size).isEqualTo(versions.size)
        // JComboBox and ComboBox model both don't expose a property of all elements
        versions.forEach { version -> assertThat(dialog.versionsModel.getIndexOf(version)).isGreaterThanOrEqualTo(0) }
        assertThat(dialog.versionsCombo.selectedItem).isEqualTo(selectedVersion)

        assertThat(dialog.previewText.highlighter.highlights).isNotEmpty
        assertThat(dialog.previewText.caretPosition).isGreaterThan(0)
    }

    private fun CountDownLatch.wait() {
        this.await(3, TimeUnit.SECONDS)
    }

    private companion object {
        const val REGISTRY1 = "Registry1"
        const val REGISTRY2 = "Registry2"
        const val SCHEMA1 = "Schema1"
        const val SCHEMA1_CONTENTS = "Schema1_contents"
        const val SCHEMA2 = "Schema2"
        const val SCHEMA2_CONTENTS = "Schema2_contents"
        const val FIRST_VERSION = "3"
        const val LAST_VERSION = "1"
        val VERSIONS = listOf(FIRST_VERSION, "2", LAST_VERSION)
        const val SEARCH_CRITERIA = "contents"
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.sqs

import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndWait
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.stub
import software.amazon.awssdk.services.iam.IamClient
import software.amazon.awssdk.services.lambda.LambdaClient
import software.amazon.awssdk.services.lambda.model.CreateEventSourceMappingRequest
import software.amazon.awssdk.services.lambda.model.CreateEventSourceMappingResponse
import software.amazon.awssdk.services.lambda.model.InvalidParameterValueException
import software.amazon.awssdk.services.lambda.model.ResourceConflictException
import software.amazon.awssdk.services.sqs.SqsClient
import software.aws.toolkits.core.region.AwsRegion
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.core.region.getDefaultRegion

class ConfigureLambdaDialogTest {
    lateinit var sqsClient: SqsClient
    lateinit var lambdaClient: LambdaClient
    lateinit var iamClient: IamClient
    lateinit var region: AwsRegion
    lateinit var queue: Queue

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val mockClientManagerRule = MockClientManagerRule()

    @Before
    fun setup() {
        sqsClient = mockClientManagerRule.create()
        lambdaClient = mockClientManagerRule.create()
        iamClient = mockClientManagerRule.create()
        queue = Queue("https://sqs.us-east-1.amazonaws.com/123456789012/test", getDefaultRegion())
    }

    @Test
    fun `No function selected`() {
        runInEdtAndWait {
            val dialog = ConfigureLambdaDialog(projectRule.project, queue)
            val validationInfo = dialog.validate()
            assertThat(validationInfo).isNotNull()
        }
    }

    @Test
    fun `Function configuration succeeds`() {
        val configureCaptor = argumentCaptor<CreateEventSourceMappingRequest>()
        lambdaClient.stub {
            on { createEventSourceMapping(configureCaptor.capture()) } doReturn CreateEventSourceMappingResponse.builder().build()
        }

        runInEdtAndWait {
            ConfigureLambdaDialog(projectRule.project, queue).apply {
                configureLambda(TEST_FUNCTION_NAME)
            }
        }
        assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME)
        assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn)
    }

    @Test
    fun `Error configuring function`() {
        val configureCaptor = argumentCaptor<CreateEventSourceMappingRequest>()
        lambdaClient.stub {
            on { createEventSourceMapping(configureCaptor.capture()) } doThrow InvalidParameterValueException.builder().message(ERROR_MESSAGE).build()
        }

        runInEdtAndWait {
            val dialog = ConfigureLambdaDialog(projectRule.project, queue)
            assertThatThrownBy { dialog.configureLambda(TEST_FUNCTION_NAME) }.hasMessage(ERROR_MESSAGE)
        }
        assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME)
        assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn)
    }

    @Test
    fun `Error configuring after policy added`() {
        val configureCaptor = argumentCaptor<CreateEventSourceMappingRequest>()
        lambdaClient.stub {
            on { createEventSourceMapping(configureCaptor.capture()) } doThrow ResourceConflictException.builder().message(ERROR_MESSAGE).build()
        }

        runInEdtAndWait {
            val dialog = ConfigureLambdaDialog(projectRule.project, queue)
            runBlocking {
                val identifier = dialog.waitUntilConfigured(TEST_FUNCTION_NAME)
                assertThat(identifier).isNull()
            }
        }
        assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME)
        assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn)
    }

    @Test
    fun `Success configuring after policy added`() {
        val configureCaptor = argumentCaptor<CreateEventSourceMappingRequest>()
        lambdaClient.stub {
            on { createEventSourceMapping(configureCaptor.capture()) } doReturn
                CreateEventSourceMappingResponse.builder().eventSourceArn(queue.arn).uuid(EVENT_IDENTIFIER).build()
        }

        runInEdtAndWait {
            val dialog = ConfigureLambdaDialog(projectRule.project, queue)
            runBlocking {
                val identifier = dialog.waitUntilConfigured(TEST_FUNCTION_NAME)
                assertThat(identifier).isEqualTo(EVENT_IDENTIFIER)
            }
        }
        assertThat(configureCaptor.firstValue.functionName()).isEqualTo(TEST_FUNCTION_NAME)
        assertThat(configureCaptor.firstValue.eventSourceArn()).isEqualTo(queue.arn)
    }

    private companion object {
        const val TEST_FUNCTION_NAME = "Function"
        const val EVENT_IDENTIFIER = "abc"
        const val ERROR_MESSAGE = "Function has invalid permission"
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.sqs

import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.stub
import software.amazon.awssdk.services.sqs.SqsClient
import software.amazon.awssdk.services.sqs.model.CreateQueueRequest
import software.amazon.awssdk.services.sqs.model.CreateQueueResponse
import software.amazon.awssdk.services.sqs.model.QueueNameExistsException
import software.aws.toolkits.core.utils.RuleUtils
import software.aws.toolkits.jetbrains.core.MockClientManagerRule

class CreateQueueDialogTest {
    lateinit var client: SqsClient

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val mockClientManagerRule = MockClientManagerRule()

    @Before
    fun setup() {
        client = mockClientManagerRule.create()
    }

    @Test
    fun `Empty queue name fails`() {
        runInEdtAndWait {
            val dialog = CreateQueueDialog(projectRule.project, client).apply {
                view.queueName.text = ""
            }

            val validationInfo = dialog.validate()
            assertThat(validationInfo).isNotNull()
        }
    }

    @Test
    fun `Invalid queue name fails`() {
        runInEdtAndWait {
            val dialog = CreateQueueDialog(projectRule.project, client).apply {
                view.queueName.text = INVALID_NAME
            }

            val validationInfo = dialog.validate()
            assertThat(validationInfo).isNotNull()
        }
    }

    @Test
    fun `Too long standard queue name fails`() {
        runInEdtAndWait {
            val dialog = CreateQueueDialog(projectRule.project, client).apply {
                view.queueName.text = RuleUtils.randomName(length = MAX_LENGTH_OF_QUEUE_NAME + 1)
            }

            val validationInfo = dialog.validate()
            assertThat(validationInfo).isNotNull()
        }
    }

    @Test
    fun `Too long fifo queue name fails`() {
        runInEdtAndWait {
            val dialog = CreateQueueDialog(projectRule.project, client).apply {
                view.queueName.text = RuleUtils.randomName(length = MAX_LENGTH_OF_FIFO_QUEUE_NAME + 1)
            }

            val validationInfo = dialog.validate()
            assertThat(validationInfo).isNotNull()
        }
    }

    @Test
    fun `Standard queue created`() {
        val createQueueCaptor = argumentCaptor<CreateQueueRequest>()
        client.stub {
            on { createQueue(createQueueCaptor.capture()) } doReturn CreateQueueResponse.builder().build()
        }

        runInEdtAndWait {
            val dialog = CreateQueueDialog(projectRule.project, client).apply {
                view.queueName.text = VALID_NAME
            }
            dialog.createQueue()
        }

        assertThat(createQueueCaptor.firstValue.queueName()).isEqualTo(VALID_NAME)
    }

    @Test
    fun `Fifo queue created`() {
        val createQueueCaptor = argumentCaptor<CreateQueueRequest>()
        client.stub {
            on { createQueue(createQueueCaptor.capture()) } doReturn CreateQueueResponse.builder().build()
        }

        runInEdtAndWait {
            val dialog = CreateQueueDialog(projectRule.project, client).apply {
                view.queueName.text = VALID_NAME
                view.fifoType.isSelected = true
            }
            dialog.createQueue()
        }

        assertThat(createQueueCaptor.firstValue.queueName()).isEqualTo(VALID_NAME_FIFO)
    }

    @Test
    fun `Error creating queue`() {
        val createQueueCaptor = argumentCaptor<CreateQueueRequest>()
        client.stub {
            on { createQueue(createQueueCaptor.capture()) } doThrow QueueNameExistsException.builder().message(ERROR_MESSAGE).build()
        }

        runInEdtAndWait {
            val dialog = CreateQueueDialog(projectRule.project, client).apply {
                view.queueName.text = VALID_NAME
            }
            assertThatThrownBy { dialog.createQueue() }.hasMessage(ERROR_MESSAGE)
        }

        assertThat(createQueueCaptor.firstValue.queueName()).isEqualTo(VALID_NAME)
    }

    private companion object {
        const val INVALID_NAME = "Hello_World!"
        const val VALID_NAME = "Hello-World"
        const val VALID_NAME_FIFO = "Hello-World.fifo"
        const val ERROR_MESSAGE = "Queue already exists."
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.sqs

import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.replaceService
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import software.amazon.awssdk.services.sqs.SqsClient
import software.amazon.awssdk.services.sqs.model.DeleteQueueRequest
import software.aws.toolkits.core.region.anAwsRegion
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.core.MockToolWindow
import software.aws.toolkits.jetbrains.services.sqs.actions.DeleteQueueAction
import software.aws.toolkits.jetbrains.services.sqs.toolwindow.SqsWindow
import java.util.function.Consumer

class DeleteQueueActionTest {
    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @Rule
    @JvmField
    val mockClientManagerRule = MockClientManagerRule()

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    private lateinit var client: SqsClient

    @Before
    fun setup() {
        client = mockClientManagerRule.create()

        val mockToolWindow = MockToolWindow(projectRule.project)
        val mockToolWindowManager = mock<ToolWindowManager> {
            on { getToolWindow(any()) } doReturn mockToolWindow
        }
        projectRule.project.replaceService(ToolWindowManager::class.java, mockToolWindowManager, disposableRule.disposable)
    }

    @Test
    fun `Delete queue succeeds`() {
        val mockNode = SqsQueueNode(projectRule.project, QUEUE_URL)
        DeleteQueueAction().performDelete(mockNode)
        verify(client).deleteQueue(any<Consumer<DeleteQueueRequest.Builder>>())
    }

    @Test
    fun `Delete queue closes tool window`() {
        val mockNode = SqsQueueNode(projectRule.project, QUEUE_URL)
        runInEdtAndWait {
            SqsWindow.getInstance(projectRule.project).pollMessage(Queue(QUEUE_URL, anAwsRegion()))
        }
        DeleteQueueAction().performDelete(mockNode)
        runInEdtAndWait {
            assertThat(SqsWindow.getInstance(projectRule.project).findQueue(QUEUE_URL)).isNull()
        }
    }

    @Test
    fun `Delete queue returns error`() {
        whenever(client.deleteQueue(Mockito.any<DeleteQueueRequest>())).thenThrow(RuntimeException(ERROR_MESSAGE))
        val mockNode = SqsQueueNode(projectRule.project, QUEUE_URL)
        assertThatThrownBy { DeleteQueueAction().performDelete(mockNode) }.hasMessage(ERROR_MESSAGE)
    }

    private companion object {
        const val QUEUE_URL = "https://sqs.us-east-1.amazonaws.com/123456789012/test1"
        const val ERROR_MESSAGE = "Network Error"
    }
}