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

package software.aws.toolkits.jetbrains.services.codewhisperer.importadder

import com.intellij.openapi.command.WriteCommandAction
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule

class CodeWhispererFallbackImportAdderTest : CodeWhispererImportAdderTestBase(
    CodeWhispererFallbackImportAdder(),
    PythonCodeInsightTestFixtureRule(),
    "txt"
) {
    private lateinit var psiFile: PsiFile

    @Before
    fun setUp() {
        runInEdtAndWait {
            psiFile = projectRule.fixture.configureByText("test.$fileExtension", "")
        }
    }

    @Test
    fun `test createNewImportPsiElement()`() {
        testCreateNewImportPsiElementReturnValueForStatements(
            false,
            listOf("import my.library\n", "import a from b\n", "from { a } import 'b'\n"),
            PsiElement::class.java
        )
    }

    @Test
    fun `test createNewImportPsiElement() adds newline char if it's not there`() {
        val statementWithoutNewLine = "import my.library"
        val expectedStatementWithNewLine = "import my.library\n"

        runInEdtAndWait {
            val newImportPsi = importAdder.createNewImportPsiElement(psiFile, statementWithoutNewLine)
            assertThat(newImportPsi).isNotNull
            assertThat(newImportPsi?.text).isEqualTo(expectedStatementWithNewLine)
        }
    }

    @Test
    fun `test hasDuplicatedImportsHelper()`() {
        val newImport = mock(PsiElement::class.java)
        val existingImports = listOf<PsiElement>()

        val result = importAdder.hasDuplicatedImportsHelper(newImport, existingImports)
        assertThat(result).isNull()
    }

    @Test
    fun `test getTopLevelImports()`() {
        val topLevelImports = importAdder.getTopLevelImports(psiFile, projectRule.fixture.editor)
        assertThat(topLevelImports).isEqualTo(emptyList<PsiElement>())
    }

    @Test
    fun `test getLocalImports()`() {
        val localImports = importAdder.getLocalImports(psiFile, projectRule.fixture.editor)
        assertThat(localImports).isEqualTo(emptyList<PsiElement>())
    }

    @Test
    fun `test addImport()`() {
        val statement = "import my.library\n"
        runInEdtAndWait {
            val newImport = importAdder.createNewImportPsiElement(psiFile, statement) ?: return@runInEdtAndWait
            WriteCommandAction.runWriteCommandAction(psiFile.project) {
                val result = importAdder.addImport(psiFile, projectRule.fixture.editor, newImport)
                assertThat(result).isTrue
            }
            assertThat(psiFile.children[0].text).isEqualTo(statement)
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.codewhisperer.importadder

import com.intellij.openapi.command.WriteCommandAction
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiImportStatementBase
import com.intellij.psi.PsiJavaFile
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule
import kotlin.test.fail

open class CodeWhispererImportAdderTestBase(
    val importAdder: CodeWhispererImportAdder,
    @Rule @JvmField val projectRule: CodeInsightTestFixtureRule,
    internal val fileExtension: String
) {

    fun <T> testCreateNewImportPsiElementReturnValueForStatements(
        returnNull: Boolean,
        statements: List<String>,
        classType: Class<T>
    ) {
        val psiFile = projectRule.fixture.configureByText("test.$fileExtension", "")
        for (statement in statements) {
            runInEdtAndWait {
                val psiElement = importAdder.createNewImportPsiElement(psiFile, statement)
                assertThat(psiElement == null).isEqualTo(returnNull)
                if (!returnNull) {
                    assertThat(psiElement).isInstanceOf(classType)
                    assertThat(psiElement?.text).isEqualToIgnoringNewLines(statement.trim())
                }
            }
        }
    }

    fun assertHasDuplicates(newImport: PsiElement, existingImports: List<PsiElement>) {
        runInEdtAndWait {
            val result = importAdder.hasDuplicatedImportsHelper(newImport, existingImports)
            assertThat(result).isNotNull
            if (result == null) return@runInEdtAndWait
            assertThat(result.text).isEqualTo(newImport.text)
        }
    }

    fun assertHasNoDuplicates(newImport: PsiElement, existingImports: List<PsiElement>) {
        runInEdtAndWait {
            val result = importAdder.hasDuplicatedImportsHelper(newImport, existingImports)
            assertThat(result).isNull()
        }
    }

    open fun assertSameImport(import1: PsiElement, import2: PsiElement) {
        fail("Implement this")
    }

    fun <T> assertImportAdded(statement: String, isAdded: Boolean, classType: Class<T>, file: PsiFile? = null) {
        runInEdtAndWait {
            val psiFile = file ?: projectRule.fixture.configureByText("test.$fileExtension", "")
            val importElement = runInEdtAndGet {
                val importElement = importAdder.createNewImportPsiElement(psiFile, statement) ?: return@runInEdtAndGet null
                WriteCommandAction.runWriteCommandAction<PsiElement>(psiFile.project) {
                    val result = importAdder.addImport(psiFile, projectRule.fixture.editor, importElement)
                    assertThat(result).isEqualTo(isAdded)
                    importElement
                }
            }
            assertThat(importElement).isNotNull
            if (importElement == null) return@runInEdtAndWait

            if (isAdded) {
                runInEdtAndWait {
                    val children =
                        if (fileExtension == "java" && classType == PsiImportStatementBase::class.java) {
                            (psiFile as PsiJavaFile).importList?.children
                        } else {
                            psiFile.children
                        }
                    val imports = children?.filter { classType.isInstance(it) }?.toList().orEmpty()
                    assertThat(imports).hasSize(1)
                    assertSameImport(imports[0], importElement)
                }
            }
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.codewhisperer.importadder

import com.intellij.lang.java.JavaLanguage
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiImportStatement
import com.intellij.psi.PsiImportStatementBase
import com.intellij.psi.PsiJavaFile
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule

class CodeWhispererJavaImportAdderTest : CodeWhispererImportAdderTestBase(
    CodeWhispererJavaImportAdder(),
    JavaCodeInsightTestFixtureRule(),
    "java"
) {

    @Test
    fun `test createNewImportPsiElement creates a new import statement`() {
        testCreateNewImportPsiElementReturnValueForStatements(
            false,
            listOf(
                "import java.util.list;",
                "import static java.util.stream.Collectors.toList;"
            ),
            PsiImportStatementBase::class.java
        )
    }

    @Test
    fun `test createNewImportPsiElement returns null for a non-import statement`() {
        testCreateNewImportPsiElementReturnValueForStatements(
            true,
            listOf(
                "public static void ",
                "int a = 1"
            ),
            PsiImportStatementBase::class.java
        )
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns a duplicate import`() {
        val newImport = createImport("import java.util.List;") ?: return
        val existingImports = listOf(
            createImport("import java.util.List;") ?: return,
            createImport("import java.util.ArrayList;") ?: return
        )
        assertHasDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null for a non-duplicate import`() {
        val newImport = createImport("import java.lang.String;") ?: return
        val existingImports = listOf(
            createImport("import java.util.List;") ?: return,
            createImport("import java.util.ArrayList;") ?: return
        )
        assertHasNoDuplicates(newImport, existingImports)
    }

    @Test
    fun `test getTopLevelImports returns a list of top-level imports`() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText(
                "test.java",
                "import java.util.List;\nimport java.util.ArrayList;\npublic class Test {}"
            )
            val imports = importAdder.getTopLevelImports(psiFile, projectRule.fixture.editor)
            assertThat(2).isEqualTo(imports.size)
            assertThat(imports.all { it is PsiImportStatement }).isTrue
        }
    }

    @Test
    fun `test getLocalImports returns an empty list`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.java",
            "import java.util.List;\nimport java.util.ArrayList;\npublic class Test {}"
        )
        val imports = importAdder.getLocalImports(psiFile, projectRule.fixture.editor)
        assertThat(imports).isEmpty()
    }

    @Test
    fun `test addImport adds a new import statement to the file`() {
        val statement = "import java.util.List;"
        assertImportAddedForJava(statement, true)
    }

    @Test
    fun `test addImport returns false for non-Java files`() {
        val psiFile = projectRule.fixture.configureByText("test.py", "")
        val statement = "import java.util.List;"
        assertImportAddedForJava(statement, false, psiFile)
    }

    @Test
    fun `test isSupportedImportStyle returns true for all import statements`() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText("test.java", "")
            val statement1 = "import java.util.List;"
            val importElement1 = importAdder.createNewImportPsiElement(psiFile, statement1)
            if (importElement1 != null) {
                assertThat(importAdder.isSupportedImportStyle(importElement1)).isTrue
            }

            val statement2 = "import static java.util.stream.Collectors.toList;"
            val importElement2 = importAdder.createNewImportPsiElement(psiFile, statement2)
            if (importElement2 != null) {
                assertThat(importAdder.isSupportedImportStyle(importElement2)).isTrue
            }
        }
    }

    private fun assertImportAddedForJava(statement: String, isAdded: Boolean, psiFile: PsiFile? = null) {
        assertImportAdded(statement, isAdded, PsiImportStatementBase::class.java, psiFile)
    }

    private fun createImport(statement: String): PsiElement? =
        runInEdtAndGet {
            val fileFactory = PsiFileFactory.getInstance(projectRule.project)
            val dummyFile = fileFactory.createFileFromText("dummy.java", JavaLanguage.INSTANCE, statement) as PsiJavaFile
            dummyFile.importList?.children?.get(0)
        }

    override fun assertSameImport(import1: PsiElement, import2: PsiElement) {
        import1 as PsiImportStatementBase; import2 as PsiImportStatementBase
        assertThat(import1.text).isEqualTo(import1.text)
    }
}


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

package software.aws.toolkits.jetbrains.services.codewhisperer.importadder

import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import com.jetbrains.python.psi.LanguageLevel
import com.jetbrains.python.psi.PyElementGenerator
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.psi.PyFromImportStatement
import com.jetbrains.python.psi.PyImportStatement
import com.jetbrains.python.psi.PyImportStatementBase
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule

class CodeWhispererPythonImportAdderTest : CodeWhispererImportAdderTestBase(
    CodeWhispererPythonImportAdder(),
    PythonCodeInsightTestFixtureRule(),
    "py"
) {
    @Test
    fun `test createNewImportPsiElement returns PyImportStatementBase for an valid statement`() {
        testCreateNewImportPsiElementReturnValueForStatements(
            false,
            listOf(
                "from os import path",
                "import os"
            ),
            PyImportStatementBase::class.java
        )
    }

    @Test
    fun `test createNewImportPsiElement returns null for an invalid statement`() {
        testCreateNewImportPsiElementReturnValueForStatements(
            true,
            listOf(
                "foo bar baz\n",
                ""
            ),
            PyImportStatementBase::class.java
        )
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns existing import for identical import`() {
        val newImport = createImport("import os")
        val existingImports = listOf(createImport("import os"))
        assertHasDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns existing import for identical import with 'as' name`() {
        val newImport = createImport("import os as my_os")
        val existingImports = listOf(createImport("import os as my_os"))
        assertHasDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns existing import statement when there is an exact duplicate`() {
        val import = createImport("import os")
        val existingImports = listOf(
            createImport("import os"),
            createImport("import sys")
        )
        assertHasDuplicates(import, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null when there is a import with everything same but alias`() {
        val import = createImport("import os as myos")
        val existingImports = listOf(
            createImport("import os"),
            createImport("import sys")
        )
        assertHasNoDuplicates(import, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null when there is a duplicate import from another module`() {
        val newImport = createImport("from os import path")
        val existingImports = listOf(
            createImport("import os"),
            createImport("import sys")
        )
        assertHasNoDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null if there are no duplicates`() {
        val importElement = createImport("import re")
        val existingImports = listOf(createImport("import sys"), createImport("import os"))
        assertHasNoDuplicates(importElement, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null for empty list`() {
        val newImport = createImport("import os")
        val existingImports = emptyList<PsiElement>()
        assertHasNoDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null for different import type`() {
        val newImport = createImport("from os import path")
        val existingImports = listOf(createImport("import os"))
        assertHasNoDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null for different import statement`() {
        val newImport = createImport("import sys")
        val existingImports = listOf(createImport("import os"))
        assertHasNoDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null for different 'as' name`() {
        val newImport = createImport("import os as my_os")
        val existingImports = listOf(createImport("import os as os_alias"))
        assertHasNoDuplicates(newImport, existingImports)
    }

    @Test
    fun `test hasDuplicatedImportsHelper returns null for different from import`() {
        val newImport = createImport("from os.path import join")
        val existingImports = listOf(createImport("from os import path"))
        assertHasNoDuplicates(newImport, existingImports)
    }

    @Test
    fun `test getTopLevelImports returns top level import statements`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.py",
            """
        import os
        from typing import List
        import math

        def foo():
            pass
            """.trimIndent()
        )
        val imports = importAdder.getTopLevelImports(psiFile, projectRule.fixture.editor)
        assertThat(imports).hasSize(3)
        runInEdtAndWait {
            assertThat(imports.map { it.text }).containsExactlyInAnyOrder("import os", "from typing import List", "import math")
        }
    }

    @Test
    fun `test getTopLevelImports returns an empty list when there are no imports`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.py",
            """
        def foo():
            pass
            """.trimIndent()
        )
        val imports = importAdder.getTopLevelImports(psiFile, projectRule.fixture.editor)
        assertThat(imports).isEmpty()
    }

    @Test
    fun `test getTopLevelImports returns an empty list when the PSI file is not a PyFile`() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText("test.txt", "Hello world")
            val imports = importAdder.getTopLevelImports(psiFile, projectRule.fixture.editor)
            assertThat(imports).isEmpty()
        }
    }

    @Test
    fun `test getLocalImports when there are no local imports`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.py",
            """
            import os
            def foo():
                print('Hello, world!')
            """.trimIndent()
        )

        runInEdtAndWait {
            val localImports = importAdder.getLocalImports(psiFile, projectRule.fixture.editor)
            assertThat(localImports).isEmpty()
        }
    }

    @Test
    fun `test getLocalImports when the caret is inside a local import`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.py",
            """
            def foo():
                from math import pi
                print("Hello world")
            """.trimIndent()
        )

        runInEdtAndWait {
            val editor = projectRule.fixture.editor
            editor.caretModel.moveToOffset(psiFile.text.indexOf("print"))

            val localImports = importAdder.getLocalImports(psiFile, editor)

            assertThat(localImports).hasSize(1)
            assertThat(localImports[0].text).isEqualTo("from math import pi")
        }
    }

    @Test
    fun `test getLocalImports when the caret is inside a block with local imports`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.py",
            """
            def foo():
                from math import pi
                print(f'Pi is approximately {pi:.2f}')
                def bar():
                    from datetime import datetime
                    print(f'Today is {datetime.now():%Y-%m-%d}')
            """.trimIndent()
        )

        runInEdtAndWait {
            val editor = projectRule.fixture.editor
            editor.caretModel.moveToOffset(psiFile.text.indexOf("Today"))

            val localImports = importAdder.getLocalImports(psiFile, editor)

            assertThat(localImports).hasSize(2)
            assertThat(localImports.map { it.text }).containsExactlyInAnyOrder("from math import pi", "from datetime import datetime")
        }
    }

    @Test
    fun `test getLocalImports when the caret is outside of any local import block`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.py",
            """
            def foo():
                print(f'Pi is approximately {pi:.2f}')
                def bar():
                    from datetime import datetime
                    print(f'Today is {datetime.now():%Y-%m-%d}')
            """.trimIndent()
        )

        runInEdtAndWait {
            val editor = projectRule.fixture.editor
            editor.caretModel.moveToOffset(psiFile.text.indexOf("f'Pi"))
            val localImports = importAdder.getLocalImports(psiFile, editor)
            assertThat(localImports).isEmpty()
        }
    }

    @Test
    fun `test addImport adds a new import statement to the file`() {
        val statement = "import os"
        assertImportAddedForPython(statement, true)
    }

    @Test
    fun `test addImport does not add duplicate import statements`() {
        val psiFile = projectRule.fixture.configureByText(
            "test.py",
            "from os import path"
        ) as PyFile
        val statement = "from os import path"
        assertImportAddedForPython(statement, true, psiFile)
    }

    @Test
    fun `test addImport handles import with 'as' keyword`() {
        val statement = "import os as my_os"
        assertImportAddedForPython(statement, true)
    }

    @Test
    fun `test addImport handles 'from xxx import yyy as zzz' import statements`() {
        val statement = "from os.path import join as path_join"
        assertImportAddedForPython(statement, true)
    }

    @Test
    fun `test addImport handles 'from xxx import all' import statements`() {
        val statement = "from os.path import *"
        assertImportAddedForPython(statement, true)
    }

    private fun assertImportAddedForPython(statement: String, isAdded: Boolean, psiFile: PsiFile? = null) {
        assertImportAdded(statement, isAdded, PyImportStatementBase::class.java, psiFile)
    }

    // only support 'from' or 'import' imports
    private fun createImport(importString: String): PsiElement {
        val importParts = importString.split(" ")
        val classType = if (importParts[0] == "from") PyFromImportStatement::class.java else PyImportStatement::class.java
        return runInEdtAndGet {
            return@runInEdtAndGet PyElementGenerator.getInstance(projectRule.project)
                .createFromText(LanguageLevel.getDefault(), classType, importString)
        }
    }

    override fun assertSameImport(import1: PsiElement, import2: PsiElement) {
        import1 as PyImportStatementBase; import2 as PyImportStatementBase
        assertThat(import1::class.java).isEqualTo(import2::class.java)
        if (import1 is PyImportStatement) {
            import2 as PyImportStatement
        } else {
            import1 as PyFromImportStatement; import2 as PyFromImportStatement
            assertThat(import1.importSourceQName.toString()).isEqualTo(import2.importSourceQName.toString())
        }
        assertThat(import1.fullyQualifiedObjectNames.toString()).isEqualTo(import2.fullyQualifiedObjectNames.toString())
        assertThat(import1.importElements.map { it.text }).containsExactlyInAnyOrder(*import2.importElements.map { it.text }.toTypedArray())
    }
}


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

package software.aws.toolkits.jetbrains.services.codewhisperer.util

import com.intellij.openapi.application.ApplicationManager
import com.intellij.testFramework.DisposableExtension
import com.intellij.testFramework.ProjectExtension
import com.intellij.testFramework.replaceService
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verify
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import software.aws.toolkits.core.utils.test.aString
import software.aws.toolkits.jetbrains.core.credentials.ManagedBearerSsoConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitAuthManager
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.loginSso
import software.aws.toolkits.jetbrains.core.credentials.reauthConnection
import software.aws.toolkits.jetbrains.core.region.MockRegionProviderExtension

class CodeWhispererUtilTest {
    companion object {
        @JvmField
        @RegisterExtension
        val projectExtension = ProjectExtension()
    }

    @JvmField
    @RegisterExtension
    val disposableExtension = DisposableExtension()

    @JvmField
    @RegisterExtension
    val mockRegionProviderExtension = MockRegionProviderExtension()

    @Test
    fun `reconnectCodeWhisperer respects connection settings`() {
        mockkStatic(::loginSso)
        mockkStatic(::reauthConnection)
        val mockConnectionManager = mockk<ToolkitConnectionManager>(relaxed = true)
        val mockConnection = mockk<ManagedBearerSsoConnection>()
        projectExtension.project.replaceService(ToolkitConnectionManager::class.java, mockConnectionManager, disposableExtension.disposable)
        ApplicationManager.getApplication().replaceService(ToolkitAuthManager::class.java, mockk(relaxed = true), disposableExtension.disposable)
        val startUrl = aString()
        val region = mockRegionProviderExtension.createAwsRegion().id
        val scopes = listOf(aString(), aString())

        every { mockConnectionManager.activeConnectionForFeature(any()) } returns mockConnection
        every { mockConnection.startUrl } returns startUrl
        every { mockConnection.region } returns region
        every { mockConnection.scopes } returns scopes

        CodeWhispererUtil.reconnectCodeWhisperer(projectExtension.project)

        verify {
            loginSso(projectExtension.project, startUrl, region, scopes)
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamic.explorer

import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.core.id
import software.aws.toolkits.jetbrains.services.dynamic.CloudControlApiResources
import software.aws.toolkits.jetbrains.services.s3.resources.S3Resources

class CloudControlApiResourcesTest {

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @Test
    fun `Correct resource name to be displayed is returned depending on the resource identifier name`() {
        val expectedDisplayName = "sampleIdentifier"
        assertThat(CloudControlApiResources.getResourceDisplayName("sampleIdentifier")).isEqualTo(expectedDisplayName)
        assertThat(CloudControlApiResources.getResourceDisplayName("arn:aws:sqs:us-west-2:1234567890:sampleIdentifier")).isEqualTo(expectedDisplayName)
    }

    @Test
    fun `S3 buckets use the S3 bucket list others use cloud API`() {
        val ecs = CloudControlApiResources.listResources("AWS::ECS::Service")
        val s3 = CloudControlApiResources.listResources("AWS::S3::Bucket")

        assertThat(ecs.id).startsWith("cloudcontrolapi.dynamic.resources")
        assertThat(s3.id).isEqualTo(S3Resources.LIST_BUCKETS.id)
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamic.explorer

import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.testFramework.ProjectRule
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.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.stub
import software.amazon.awssdk.services.cloudcontrol.CloudControlClient
import software.amazon.awssdk.services.cloudcontrol.model.GetResourceRequest
import software.amazon.awssdk.services.cloudcontrol.model.GetResourceResponse
import software.amazon.awssdk.services.cloudcontrol.model.Operation
import software.amazon.awssdk.services.cloudcontrol.model.OperationStatus
import software.amazon.awssdk.services.cloudcontrol.model.ResourceDescription
import software.aws.toolkits.core.ConnectionSettings
import software.aws.toolkits.core.credentials.aToolkitCredentialsProvider
import software.aws.toolkits.core.region.anAwsRegion
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.services.dynamic.CreateDynamicResourceVirtualFile
import software.aws.toolkits.jetbrains.services.dynamic.CreateResourceFileStatusHandler
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResource
import software.aws.toolkits.jetbrains.services.dynamic.ResourceMutationState
import software.aws.toolkits.jetbrains.services.dynamic.ResourceType
import software.aws.toolkits.jetbrains.services.dynamic.ViewEditableDynamicResourceVirtualFile
import java.time.Instant

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

    @JvmField
    @Rule
    val mockClientManager = MockClientManagerRule()

    private lateinit var cloudControlClient: CloudControlClient
    private lateinit var createResourceHandler: CreateResourceFileStatusHandler
    private lateinit var connectionSettings: ConnectionSettings
    private lateinit var fileEditorManager: FileEditorManager
    private val resource = DynamicResource(ResourceType("AWS::SampleService::Type", "SampleService", "Type"), "sampleIdentifier")

    @Before
    fun setup() {
        fileEditorManager = FileEditorManager.getInstance(projectRule.project)
        connectionSettings = ConnectionSettings(aToolkitCredentialsProvider(), anAwsRegion())
        cloudControlClient = mockClientManager.create(connectionSettings.region, connectionSettings.credentials)
    }

    @Test
    fun `If resource creation succeeds, an updated view of file opens`() {
        createResourceHandler = CreateResourceFileStatusHandler.getInstance(projectRule.project)

        cloudControlClient.stub {
            on { getResource(any<GetResourceRequest>()) } doAnswer {
                GetResourceResponse.builder().typeName(resource.type.fullName).resourceDescription(
                    ResourceDescription
                        .builder()
                        .identifier(resource.identifier)
                        .properties("{\"LogGroupName\":\"testRes5\",\"Arn\":\"sampleArn:*\"}")
                        .build()
                )
                    .build()
            }
        }
        val sampleFile = CreateDynamicResourceVirtualFile(connectionSettings, resource.type.fullName)
        runInEdtAndWait {
            fileEditorManager.openFile(sampleFile, false)
        }
        createResourceHandler.recordResourceBeingCreated("sampleToken", sampleFile)
        assertThat(fileEditorManager.openFiles.filterIsInstance<CreateDynamicResourceVirtualFile>().size).isEqualTo(1)

        val mutationState = ResourceMutationState(
            connectionSettings,
            "sampleToken",
            Operation.CREATE,
            resource.type.fullName,
            OperationStatus.SUCCESS,
            resource.identifier,
            "",
            Instant.now()
        )
        createResourceHandler.mutationStatusChanged(mutationState)

        assertThat(fileEditorManager.openFiles.filterIsInstance<CreateDynamicResourceVirtualFile>().size).isEqualTo(0)
        assertThat(createResourceHandler.getNumberOfResourcesBeingCreated()).isEqualTo(0)
        assertThat(fileEditorManager.openFiles.filterIsInstance<ViewEditableDynamicResourceVirtualFile>().size).isEqualTo(1)
    }

    @After
    fun closeFile() {
        runInEdtAndWait {
            fileEditorManager.closeFile(FileEditorManager.getInstance(projectRule.project).openFiles.first())
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamic.explorer

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.cloudcontrol.model.UnsupportedActionException
import software.aws.toolkits.core.utils.test.aString
import software.aws.toolkits.core.utils.test.hasOnlyOneElementOfType
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerErrorNode
import software.aws.toolkits.jetbrains.core.id
import software.aws.toolkits.jetbrains.services.dynamic.CloudControlApiResources
import software.aws.toolkits.resources.message

class DynamicResourceResourceTypeNodeTest {

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @Rule
    @JvmField
    val resourceCache = MockResourceCacheRule()

    @Test
    fun returnsListFromProvider() {
        val type = "AWS::${aString()}::${aString()}"
        val identifier = aString()
        resourceCache.addEntry(projectRule.project, CloudControlApiResources.listResources(type).id, listOf(identifier))

        val sut = DynamicResourceResourceTypeNode(projectRule.project, type)

        assertThat(sut.children).hasOnlyOneElementOfType<DynamicResourceNode>().satisfies {
            assertThat(it.displayName()).isEqualTo(identifier)
        }
    }

    @Test
    fun unsupportedExceptionResultsInEmptyNode() {
        val type = aString()
        resourceCache.addEntry(projectRule.project, CloudControlApiResources.listResources(type).id, throws = UnsupportedActionException.builder().build())

        val sut = DynamicResourceResourceTypeNode(projectRule.project, type)

        assertThat(sut.children).hasOnlyOneElementOfType<AwsExplorerEmptyNode>().satisfies {
            assertThat(it.displayName()).startsWith(message("dynamic_resources.unavailable_in_region", ""))
        }
    }

    @Test
    fun otherExceptionsBubble() {
        val type = aString()
        resourceCache.addEntry(projectRule.project, CloudControlApiResources.listResources(type).id, throws = RuntimeException())

        val sut = DynamicResourceResourceTypeNode(projectRule.project, type)

        assertThat(sut.children).hasOnlyOneElementOfType<AwsExplorerErrorNode>()
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamic.explorer

import com.intellij.ide.util.treeView.AbstractTreeNode
import com.intellij.openapi.application.runInEdt
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.ProjectRule
import com.intellij.ui.tree.AsyncTreeModel
import com.intellij.ui.tree.StructureTreeModel
import com.intellij.ui.treeStructure.Tree
import com.intellij.util.concurrency.Invoker
import com.intellij.util.ui.tree.TreeUtil
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.AwsExplorerTreeStructure
import software.aws.toolkits.jetbrains.core.fillResourceCache
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResourceSupportedTypes
import software.aws.toolkits.jetbrains.settings.DynamicResourcesSettings
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import javax.swing.tree.TreeModel
import javax.swing.tree.TreeNode

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

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Before
    fun setUp() {
        resourceCache.fillResourceCache(projectRule.project)
    }

    @Test
    fun `dynamic resources settings node is first in subtree`() {
        DynamicResourcesSettings.getInstance().selected = DynamicResourceSupportedTypes.getInstance().getSupportedTypes().toSet()

        val countDownLatch = CountDownLatch(1)

        val model = Tree(createTreeModel())

        runInEdt {
            TreeUtil.expand(model, 2) {
                countDownLatch.countDown()
            }
        }
        countDownLatch.await(1, TimeUnit.SECONDS)

        val children = TreeUtil.listChildren(model.model.root as TreeNode)
            .last()
            .let { TreeUtil.listChildren(it as TreeNode) }
            .map { TreeUtil.getUserObject(it) as AbstractTreeNode<*> }

        assertThat(children).hasSizeGreaterThan(1)
        assertThat(children.first()).isInstanceOf(DynamicResourceSelectorNode::class.java)
        assertThat(children).containsOnlyOnce(children.first())
    }

    @Test
    fun `dynamic resources root node is last in service list`() {
        val countDownLatch = CountDownLatch(1)

        val model = Tree(createTreeModel())

        runInEdt {
            TreeUtil.expand(model, 1) {
                countDownLatch.countDown()
            }
        }
        countDownLatch.await(1, TimeUnit.SECONDS)

        val children = TreeUtil.listChildren(model.model.root as TreeNode)
            .map { TreeUtil.getUserObject(it) as AbstractTreeNode<*> }

        assertThat(children.last()).isInstanceOf(OtherResourcesNode::class.java)
        assertThat(children).containsOnlyOnce(children.last())
    }

    private fun createTreeModel(): TreeModel {
        val awsTreeModel = AwsExplorerTreeStructure(projectRule.project)
        val structureTreeModel =
            StructureTreeModel(awsTreeModel, null, Invoker.forBackgroundThreadWithoutReadAction(disposableRule.disposable), disposableRule.disposable)
        return AsyncTreeModel(structureTreeModel, false, disposableRule.disposable)
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamic.explorer

import com.intellij.notification.Notification
import com.intellij.notification.Notifications
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import software.amazon.awssdk.services.cloudcontrol.CloudControlClient
import software.amazon.awssdk.services.cloudcontrol.model.DeleteResourceRequest
import software.amazon.awssdk.services.cloudcontrol.model.DeleteResourceResponse
import software.amazon.awssdk.services.cloudcontrol.model.GetResourceRequestStatusRequest
import software.amazon.awssdk.services.cloudcontrol.model.GetResourceRequestStatusResponse
import software.amazon.awssdk.services.cloudcontrol.model.Operation
import software.amazon.awssdk.services.cloudcontrol.model.OperationStatus
import software.amazon.awssdk.services.cloudcontrol.model.ProgressEvent
import software.amazon.awssdk.services.cloudcontrol.model.RequestTokenNotFoundException
import software.aws.toolkits.core.ConnectionSettings
import software.aws.toolkits.core.credentials.aToolkitCredentialsProvider
import software.aws.toolkits.core.region.anAwsRegion
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResource
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResourceIdentifier
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResourceStateMutationHandler
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResourceUpdateManager
import software.aws.toolkits.jetbrains.services.dynamic.ResourceMutationState
import software.aws.toolkits.jetbrains.services.dynamic.ResourceType
import software.aws.toolkits.resources.message
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

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

    @JvmField
    @Rule
    val mockClientManager = MockClientManagerRule()

    @JvmField
    @Rule
    val disposableRule = DisposableRule()

    private lateinit var cloudControlClient: CloudControlClient
    private lateinit var dynamicResourceUpdateManager: DynamicResourceUpdateManager
    private lateinit var connectionSettings: ConnectionSettings
    private val resource = DynamicResource(ResourceType("AWS::SampleService::Type", "SampleService", "Type"), "sampleIdentifier")

    @Before
    fun setup() {
        connectionSettings = ConnectionSettings(aToolkitCredentialsProvider(), anAwsRegion())
        cloudControlClient = mockClientManager.create(connectionSettings.region, connectionSettings.credentials)
    }

    @Test
    fun `Resource State Change Triggers are correctly reflected`() {
        val testOperationState: MutableList<ResourceMutationState> = mutableListOf()
        dynamicResourceUpdateManager = DynamicResourceUpdateManager.getInstance(projectRule.project)

        cloudControlClient.stub {
            on { deleteResource(any<DeleteResourceRequest>()) } doAnswer {
                DeleteResourceResponse.builder().progressEvent(
                    ProgressEvent.builder()
                        .requestToken("sampleToken")
                        .typeName(resource.type.fullName)
                        .operation(Operation.DELETE)
                        .operationStatus(OperationStatus.IN_PROGRESS)
                        .statusMessage("Message in progress")
                        .build()
                ).build()
            }
            on { getResourceRequestStatus(any<GetResourceRequestStatusRequest>()) } doAnswer {
                GetResourceRequestStatusResponse.builder().progressEvent(
                    ProgressEvent.builder()
                        .requestToken("sampleToken")
                        .operation(Operation.DELETE)
                        .operationStatus(OperationStatus.SUCCESS)
                        .statusMessage("Completed successfully")
                        .build()
                )
                    .build()
            }
        }

        projectRule.project.messageBus.connect(projectRule.project)
            .subscribe(
                DynamicResourceUpdateManager.DYNAMIC_RESOURCE_STATE_CHANGED,
                object : DynamicResourceStateMutationHandler {
                    override fun mutationStatusChanged(state: ResourceMutationState) {
                        testOperationState.add(state)
                    }

                    override fun statusCheckComplete() {}
                }
            )

        dynamicResourceUpdateManager.deleteResource(DynamicResourceIdentifier(connectionSettings, resource.type.fullName, resource.identifier))
        CountDownLatch(1).await(400, TimeUnit.MILLISECONDS)
        assertThat(testOperationState.size).isEqualTo(1)
        assertThat(testOperationState.first().message).isEqualTo("Completed successfully")
        assertThat(testOperationState.first().status).isEqualTo(OperationStatus.SUCCESS)
    }

    @Test
    fun `Resource State Change Triggers are updates status text`() {
        val testOperationStatus: MutableList<OperationStatus> = mutableListOf()
        dynamicResourceUpdateManager = DynamicResourceUpdateManager.getInstance(projectRule.project)

        cloudControlClient.stub {
            on { deleteResource(any<DeleteResourceRequest>()) } doAnswer {
                DeleteResourceResponse.builder().progressEvent(
                    ProgressEvent.builder()
                        .requestToken("sampleToken")
                        .typeName(resource.type.fullName)
                        .operation(Operation.DELETE)
                        .operationStatus(OperationStatus.IN_PROGRESS)
                        .build()
                ).build()
            }
            on { getResourceRequestStatus(any<GetResourceRequestStatusRequest>()) } doAnswer {
                GetResourceRequestStatusResponse.builder().progressEvent(
                    ProgressEvent.builder()
                        .requestToken("sampleToken")
                        .operation(Operation.DELETE)
                        .operationStatus(OperationStatus.IN_PROGRESS)
                        .build()
                )
                    .build()
            }
        }

        projectRule.project.messageBus.connect(projectRule.project)
            .subscribe(
                DynamicResourceUpdateManager.DYNAMIC_RESOURCE_STATE_CHANGED,
                object : DynamicResourceStateMutationHandler {

                    override fun mutationStatusChanged(state: ResourceMutationState) {
                        testOperationStatus.add(state.status)
                    }

                    override fun statusCheckComplete() {
                        testOperationStatus.add(OperationStatus.SUCCESS)
                    }
                }
            )
        dynamicResourceUpdateManager.deleteResource(DynamicResourceIdentifier(connectionSettings, resource.type.fullName, resource.identifier))
        CountDownLatch(1).await(400, TimeUnit.MILLISECONDS)
        assertThat(testOperationStatus.size).isEqualTo(1)
        assertThat(testOperationStatus.first()).isEqualTo(OperationStatus.SUCCESS)
    }

    @Test
    fun `Notifies user if request token is invalid and stops polling for it`() {
        val notificationMock = mock<Notifications>()
        val dynamicResourceIdentifier = DynamicResourceIdentifier(connectionSettings, resource.type.fullName, resource.identifier)

        projectRule.project.messageBus.connect(disposableRule.disposable).subscribe(Notifications.TOPIC, notificationMock)
        dynamicResourceUpdateManager = DynamicResourceUpdateManager.getInstance(projectRule.project)

        cloudControlClient.stub {
            on { deleteResource(any<DeleteResourceRequest>()) } doAnswer {
                DeleteResourceResponse.builder().progressEvent(
                    ProgressEvent.builder()
                        .requestToken("sampleToken")
                        .typeName(resource.type.fullName)
                        .operation(Operation.DELETE)
                        .operationStatus(OperationStatus.IN_PROGRESS)
                        .build()
                ).build()
            }
            on { getResourceRequestStatus(any<GetResourceRequestStatusRequest>()) } doAnswer {
                throw RequestTokenNotFoundException.builder().message("sampleMessage").build()
            }
        }

        dynamicResourceUpdateManager.deleteResource(dynamicResourceIdentifier)
        CountDownLatch(1).await(400, TimeUnit.MILLISECONDS)

        argumentCaptor<Notification>().apply {
            verify(notificationMock).notify(capture())
            assertThat(firstValue.content).contains("sampleMessage")
        }
        assertThat(dynamicResourceUpdateManager.getUpdateStatus(dynamicResourceIdentifier)).isNull()
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamic.explorer

import com.intellij.testFramework.LightVirtualFile
import com.intellij.testFramework.RuleChain
import com.intellij.testFramework.runInEdtAndWait
import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.core.ConnectionSettings
import software.aws.toolkits.core.credentials.aToolkitCredentialsProvider
import software.aws.toolkits.core.region.anAwsRegion
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.services.dynamic.CloudControlApiResources
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResource
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResourceIdentifier
import software.aws.toolkits.jetbrains.services.dynamic.DynamicResourceSchemaMapping
import software.aws.toolkits.jetbrains.services.dynamic.ResourceType
import software.aws.toolkits.jetbrains.services.dynamic.ViewEditableDynamicResourceVirtualFile
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
import java.util.concurrent.CompletableFuture

class ResourceSchemaProviderFactoryTest {
    @Rule
    @JvmField
    val projectRule = JavaCodeInsightTestFixtureRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Rule
    @JvmField
    val ruleChain = RuleChain(
        projectRule,
        resourceCache
    )

    @Before
    fun setup() {
        val schema = "{\n" +
            "  \"properties\": {\n" +
            "    \"RetentionInDays\": {\n" +
            "      \"description\": \"The number of days to retain the log events " +
            "in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653.\",\n" +
            "      \"type\": \"integer\",\n" +
            "      \"enum\": [\n" +
            "        1,\n" +
            "        3,\n" +
            "        5,\n" +
            "        7,\n" +
            "        14,\n" +
            "        30,\n" +
            "        60,\n" +
            "        90,\n" +
            "        120,\n" +
            "        150,\n" +
            "        180,\n" +
            "        365,\n" +
            "        400,\n" +
            "        545,\n" +
            "        731,\n" +
            "        1827,\n" +
            "        3653\n" +
            "      ]\n" +
            "    }" +
            "  }\n" +
            "}\n"

        val schemaFile = LightVirtualFile("AWSLogLogGroupSchema.json", schema)
        resourceCache.addEntry(
            projectRule.project,
            CloudControlApiResources.getResourceSchema(resource.type.fullName),
            CompletableFuture.completedFuture(schemaFile)
        )
    }

    private val resource = DynamicResource(ResourceType("AWS::Log::LogGroup", "Log", "LogGroup"), "sampleIdentifier")

    @Test
    fun `Check whether schema is applied`() {
        val fixture = projectRule.fixture
        val jsonSchemaComplianceInspection = JsonSchemaComplianceInspection()

        try {
            fixture.enableInspections(jsonSchemaComplianceInspection)
            val file = ViewEditableDynamicResourceVirtualFile(
                DynamicResourceIdentifier(ConnectionSettings(aToolkitCredentialsProvider(), anAwsRegion()), resource.type.fullName, resource.identifier),
                """
                {"RetentionInDays":<warning descr="Value should be one of: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653">18</warning>}
                """.trimIndent()
            )
            DynamicResourceSchemaMapping.getInstance().addResourceSchemaMapping(projectRule.project, file)
            runInEdtAndWait {
                fixture.openFileInEditor(file)
                fixture.checkHighlighting()
            }
        } finally {
            fixture.disableInspections(jsonSchemaComplianceInspection)
            DynamicResourceSchemaMapping.getInstance().removeCurrentlyActiveResourceTypes(projectRule.project)
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamodb

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.dynamodb.model.AttributeValue
import kotlin.reflect.KClass

@RunWith(Enclosed::class)
class AttributesTest {
    @RunWith(Parameterized::class)
    class ParameterizedTests(
        private val dataType: String,
        private val attributeValue: AttributeValue,
        private val attributeType: KClass<out DynamoAttribute<*>>,
        private val strFormat: String
    ) {
        companion object {
            @JvmStatic
            @Parameterized.Parameters(name = "{0}")
            fun data() = arrayOf(
                arrayOf(
                    "BOOL",
                    AttributeValue.builder().bool(true).build(),
                    BooleanAttribute::class,
                    "true"
                ),
                arrayOf(
                    "S",
                    AttributeValue.builder().s("hello").build(),
                    StringAttribute::class,
                    "\"hello\""
                ),
                arrayOf(
                    "N",
                    AttributeValue.builder().n("1.2").build(),
                    NumberAttribute::class,
                    "1.2"
                ),
                arrayOf(
                    "B",
                    AttributeValue.builder().b(SdkBytes.fromUtf8String("hi")).build(),
                    BinaryAttribute::class,
                    "aGk="
                ),
                arrayOf(
                    "NUL",
                    AttributeValue.builder().nul(true).build(),
                    NullAttribute::class,
                    "<null>"
                ),
                arrayOf(
                    "SS",
                    AttributeValue.builder().ss("hello", "\"bob\"").build(),
                    StringSetAttribute::class,
                    "{\"hello\", \"\"bob\"\"}"
                ),
                arrayOf(
                    "NS",
                    AttributeValue.builder().ns("1", "2").build(),
                    NumberSetAttribute::class,
                    "{1, 2}"
                ),
                arrayOf(
                    "BS",
                    AttributeValue.builder().bs(SdkBytes.fromUtf8String("hi"), SdkBytes.fromUtf8String("bye")).build(),
                    BinarySetAttribute::class,
                    "{aGk=, Ynll}"
                ),
                arrayOf(
                    "M",
                    AttributeValue.builder().m(mapOf("foo" to AttributeValue.builder().bool(false).build())).build(),
                    MapAttribute::class,
                    "{\"foo\": {\"BOOL\": false}}"
                ),
                arrayOf(
                    "L",
                    AttributeValue.builder().l(AttributeValue.builder().s("hi").build(), AttributeValue.builder().n("1").build()).build(),
                    ListAttribute::class,
                    "[{\"S\": \"hi\"}, {\"N\": 1}]"
                )
            )
        }

        @Test
        fun `correct attribute type`() {
            assertThat(attributeValue.toAttribute()).isInstanceOf(attributeType.java)
        }

        @Test
        fun `string representation`() {
            assertThat(attributeValue.toAttribute().stringRepresentation()).isEqualTo(strFormat)
        }

        @Test
        fun `correct type string`() {
            assertThat(attributeValue.toAttribute().dataType).isEqualTo(dataType)
        }
    }

    @Test
    fun `unknown attribute value throws`() {
        assertThatThrownBy {
            AttributeValue.builder().build().toAttribute()
        }.isInstanceOf(UnsupportedOperationException::class.java)
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamodb

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import software.amazon.awssdk.services.dynamodb.DynamoDbClient
import software.amazon.awssdk.services.dynamodb.model.ExecuteStatementRequest
import software.amazon.awssdk.services.dynamodb.model.ExecuteStatementResponse
import software.aws.toolkits.core.utils.delegateMock
import software.aws.toolkits.jetbrains.services.dynamodb.DynamoDbUtils.executeStatementPaginator

class CustomDynamoPaginatorsTest {
    @Test
    fun `execute statement paginator works`() {
        val statement = "statement"

        val mockClient = delegateMock<DynamoDbClient> {
            on { executeStatement(any<ExecuteStatementRequest>()) }
                .thenReturn(ExecuteStatementResponse.builder().nextToken("token").build())
                .thenReturn(ExecuteStatementResponse.builder().nextToken("token2").build())
                .thenReturn(ExecuteStatementResponse.builder().build())
        }

        assertThat(mockClient.executeStatementPaginator(ExecuteStatementRequest.builder().statement(statement).build()).toList()).hasSize(3)

        argumentCaptor<ExecuteStatementRequest> {
            verify(mockClient, times(3)).executeStatement(capture())

            assertThat(firstValue.nextToken()).isNull()
            assertThat(firstValue.statement()).isEqualTo(statement)

            assertThat(secondValue.nextToken()).isEqualTo("token")
            assertThat(secondValue.statement()).isEqualTo(statement)

            assertThat(thirdValue.nextToken()).isEqualTo("token2")
            assertThat(thirdValue.statement()).isEqualTo(statement)
        }
    }

    @Test
    fun `execute statement paginator bubbles errors`() {
        val error = IllegalStateException("Dummy error")
        val mockClient = delegateMock<DynamoDbClient> {
            on { executeStatement(any<ExecuteStatementRequest>()) }
                .thenThrow(error)
        }

        assertThatThrownBy {
            mockClient.executeStatementPaginator(ExecuteStatementRequest.builder().statement("statement").build()).toList()
        }.isEqualTo(error)

        verify(mockClient, times(1)).executeStatement(any<ExecuteStatementRequest>())
    }

    @Test
    fun `execute statement paginator bubbles errors on subsequent pages`() {
        val error = IllegalStateException("Dummy error")
        val mockClient = delegateMock<DynamoDbClient> {
            on { executeStatement(any<ExecuteStatementRequest>()) }
                .thenReturn(ExecuteStatementResponse.builder().nextToken("token").build())
                .thenReturn(ExecuteStatementResponse.builder().nextToken("token2").build())
                .thenThrow(error)
        }

        assertThatThrownBy {
            mockClient.executeStatementPaginator(ExecuteStatementRequest.builder().statement("statement").build()).toList()
        }.isEqualTo(error)

        verify(mockClient, times(3)).executeStatement(any<ExecuteStatementRequest>())
    }
}


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

package software.aws.toolkits.jetbrains.services.dynamodb.actions

import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.fileTypes.FileTypes
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.verify
import software.amazon.awssdk.services.dynamodb.DynamoDbClient
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest
import software.aws.toolkits.core.utils.test.aString
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.services.dynamodb.editor.DynamoDbTableEditorProvider
import software.aws.toolkits.jetbrains.services.dynamodb.explorer.DynamoDbTableNode
import software.aws.toolkits.jetbrains.utils.associateFilePattern

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

    @JvmField
    @Rule
    val disposableRule = DisposableRule()

    @JvmField
    @Rule
    val mockClientManager = MockClientManagerRule()

    @Test
    fun deleteTable() {
        val tableName = aString()
        val dynamoDbClient = mockClientManager.create<DynamoDbClient>()
        val tableNode = DynamoDbTableNode(projectRule.project, tableName)

        val deleteBucketAction = DeleteTableAction()
        deleteBucketAction.performDelete(tableNode)

        argumentCaptor<DeleteTableRequest>().apply {
            verify(dynamoDbClient).deleteTable(capture())

            assertThat(firstValue.tableName()).isEqualTo(tableName)
        }
    }

    @Test
    fun deleteTableClosesEditors() {
        val tableName = aString()
        val tableNode = DynamoDbTableNode(projectRule.project, tableName)
        mockClientManager.create<DynamoDbClient>()

        val fileEditorManager = FileEditorManagerEx.getInstanceEx(projectRule.project)

        runInEdtAndWait {
            // Silly hack because test file editor impl has a bunch of asserts about the document/psi that don't exist in the real impl
            associateFilePattern(FileTypes.PLAIN_TEXT, tableNode.resourceArn(), disposableRule.disposable)

            assertThat(fileEditorManager.openFiles).isEmpty()
            DynamoDbTableEditorProvider.openViewer(projectRule.project, tableNode.resourceArn())
            assertThat(fileEditorManager.openFiles).isNotEmpty
        }

        val deleteBucketAction = DeleteTableAction()
        deleteBucketAction.performDelete(tableNode)

        assertThat(fileEditorManager.openFiles).isEmpty()
    }
}


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

package software.aws.toolkits.jetbrains.services.ecr

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.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import software.amazon.awssdk.services.ecr.EcrClient
import software.amazon.awssdk.services.ecr.model.CreateRepositoryRequest
import software.amazon.awssdk.services.ecr.model.CreateRepositoryResponse
import software.amazon.awssdk.services.ecr.model.RepositoryAlreadyExistsException
import software.amazon.awssdk.services.ecr.model.SetRepositoryPolicyRequest
import software.amazon.awssdk.services.ecr.model.SetRepositoryPolicyResponse
import software.aws.toolkits.core.utils.RuleUtils
import software.aws.toolkits.jetbrains.core.MockClientManagerRule

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

    @Rule
    @JvmField
    val mockClientManagerRule = MockClientManagerRule()

    @Test
    fun `Empty repo fails validation`() {
        val client: EcrClient = mockClientManagerRule.create()
        runInEdtAndWait {
            val dialog = CreateEcrRepoDialog(project = projectRule.project, ecrClient = client)
            dialog.repoName = "  "

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

    @Test
    fun `Create repo succeeds`() {
        val createRepoCaptor = argumentCaptor<CreateRepositoryRequest>()
        val repoName = RuleUtils.randomName()
        val client: EcrClient = mockClientManagerRule.create()

        client.stub {
            on { createRepository(createRepoCaptor.capture()) } doReturn CreateRepositoryResponse.builder().build()
            on { setRepositoryPolicy(any<SetRepositoryPolicyRequest>()) } doReturn SetRepositoryPolicyResponse.builder().build()
        }

        runInEdtAndWait {
            val dialog = CreateEcrRepoDialog(project = projectRule.project, ecrClient = client)

            dialog.repoName = repoName

            dialog.createRepo()
        }

        assertThat(createRepoCaptor.firstValue.repositoryName()).isEqualTo(repoName)
        verify(client, times(0)).setRepositoryPolicy(any<SetRepositoryPolicyRequest>())
    }

    @Test
    fun `Create repo trims and succeeds`() {
        val createRepoCaptor = argumentCaptor<CreateRepositoryRequest>()
        val repoName = "  " + RuleUtils.randomName() + "       "
        val client: EcrClient = mockClientManagerRule.create()

        client.stub {
            on { createRepository(createRepoCaptor.capture()) } doReturn CreateRepositoryResponse.builder().build()
        }

        runInEdtAndWait {
            val dialog = CreateEcrRepoDialog(project = projectRule.project, ecrClient = client)

            dialog.repoName = repoName

            dialog.createRepo()
        }

        assertThat(createRepoCaptor.firstValue.repositoryName()).isEqualTo(repoName.trim())
    }

    @Test
    fun `Create repo fails`() {
        val repoName = RuleUtils.randomName()
        val message = RuleUtils.randomName()
        val client: EcrClient = mockClientManagerRule.create()

        client.stub {
            on { createRepository(any<CreateRepositoryRequest>()) } doThrow RepositoryAlreadyExistsException.builder().message(message).build()
        }
        runInEdtAndWait {
            val dialog = CreateEcrRepoDialog(project = projectRule.project, ecrClient = client)

            dialog.repoName = repoName

            assertThatThrownBy { dialog.createRepo() }.hasMessage(message)
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.ecr

import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.core.utils.test.aString
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerErrorNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.EcrExplorerRootNode
import software.aws.toolkits.jetbrains.services.ecr.resources.EcrResources
import software.aws.toolkits.jetbrains.services.ecr.resources.Repository
import java.util.concurrent.CompletableFuture

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

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Test
    fun `Ecr repositories are listed`() {
        resourceCache.addEntry(
            projectRule.project,
            EcrResources.LIST_REPOS,
            listOf(
                Repository("repo1", "arn3", ""),
                Repository("repo2", "arn2", "")
            )
        )

        val children = EcrServiceNode(projectRule.project, ECR_EXPLORER_NODE).children

        assertThat(children).allMatch { it is EcrRepositoryNode }
        assertThat(children.map { it.displayName() }).containsExactlyInAnyOrder("repo1", "repo2")
        assertThat(children.filterIsInstance<EcrRepositoryNode>().map { it.resourceArn() }).containsExactlyInAnyOrder("arn2", "arn3")
    }

    @Test
    fun `No repositories listed`() {
        resourceCache.addEntry(projectRule.project, EcrResources.LIST_REPOS, listOf())
        val children = EcrServiceNode(projectRule.project, ECR_EXPLORER_NODE).children
        assertThat(children).singleElement().isInstanceOf(AwsExplorerEmptyNode::class.java)
    }

    @Test
    fun `Error loading repositories`() {
        resourceCache.addEntry(projectRule.project, EcrResources.LIST_REPOS, CompletableFuture.failedFuture(RuntimeException("network broke")))
        val children = EcrServiceNode(projectRule.project, ECR_EXPLORER_NODE).children
        assertThat(children).singleElement().isInstanceOf(AwsExplorerErrorNode::class.java)
    }

    @Test
    fun `Repository has tags as children`() {
        val tags = listOf(aString(), aString())
        val repo = Repository(aString(), aString(), aString())
        val node = EcrRepositoryNode(projectRule.project, repo)
        resourceCache.addEntry(projectRule.project, EcrResources.listTags(repo.repositoryName), tags)
        val children = node.children
        assertThat(children).hasSize(2)
        assertThat(children.map { (it as EcrTagNode).tag }).containsExactlyInAnyOrder(*tags.toTypedArray())
    }

    @Test
    fun `Repository has no tags`() {
        val repo = Repository(aString(), aString(), aString())
        val node = EcrRepositoryNode(projectRule.project, repo)
        resourceCache.addEntry(projectRule.project, EcrResources.listTags(repo.repositoryName), listOf())
        val children = node.children
        assertThat(children).singleElement().isInstanceOf(AwsExplorerEmptyNode::class.java)
    }

    @Test
    fun `Repository lists tags fails`() {
        val repo = Repository(aString(), aString(), aString())
        val node = EcrRepositoryNode(projectRule.project, repo)
        resourceCache.addEntry(
            projectRule.project,
            EcrResources.listTags(repo.repositoryName),
            CompletableFuture.failedFuture(RuntimeException("network broke"))
        )
        val children = node.children
        assertThat(children).singleElement().isInstanceOf(AwsExplorerErrorNode::class.java)
    }

    private companion object {
        val ECR_EXPLORER_NODE = EcrExplorerRootNode()
    }
}


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

package software.aws.toolkits.jetbrains.services.ecr

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import software.amazon.awssdk.services.ecr.model.AuthorizationData
import software.aws.toolkits.core.utils.RuleUtils
import java.util.Base64

class EcrUtilsTest {
    @Test
    fun getDockerLogin() {
        val authData: AuthorizationData = mock()
        val user = RuleUtils.randomName()
        val encoder = Base64.getEncoder()

        whenever(authData.authorizationToken()).thenReturn(encoder.encodeToString("$user:password".toByteArray()))
        val (user1, pass1) = authData.getDockerLogin()
        assertThat(user1).isEqualTo(user)
        assertThat(pass1).isEqualTo("password")

        whenever(authData.authorizationToken()).thenReturn(encoder.encodeToString("$user:::::::password".toByteArray()))
        val (user2, pass2) = authData.getDockerLogin()
        assertThat(user2).isEqualTo(user)
        assertThat(pass2).isEqualTo("::::::password")

        whenever(authData.authorizationToken()).thenReturn(encoder.encodeToString("$user:aGVsbG8=".toByteArray()))
        val (user3, pass3) = authData.getDockerLogin()
        assertThat(user3).isEqualTo(user)
        assertThat(pass3).isEqualTo("aGVsbG8=")
    }
}


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

package software.aws.toolkits.jetbrains.services.ecr.actions

import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.TestActionEvent
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.services.ecr.EcrTagNode
import software.aws.toolkits.jetbrains.services.ecr.resources.Repository

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

    @Test
    fun `Hides when selected nodes have different repositories`() {
        val action = TestActionEvent()
        DeleteTagAction().update(
            listOf(
                EcrTagNode(projectRule.project, Repository("name", "", ""), "tag1"),
                EcrTagNode(projectRule.project, Repository("differentName", "", ""), "tag1")
            ),
            action
        )
        assertThat(action.presentation.isVisible).isFalse()
    }

    @Test
    fun `Shows when multiple nodes selected with the same repository`() {
        val action = TestActionEvent()
        DeleteTagAction().update(
            listOf(
                EcrTagNode(projectRule.project, Repository("name", "", ""), "tag1"),
                EcrTagNode(projectRule.project, Repository("name", "", ""), "tag1")
            ),
            action
        )
        assertThat(action.presentation.isVisible).isTrue()
    }
}


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

package software.aws.toolkits.jetbrains.services.ecs

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.ecs.model.Service
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerErrorNode
import software.aws.toolkits.jetbrains.services.ecs.resources.EcsResources
import java.util.concurrent.CompletableFuture

class EcsClusterNodeTest {

    @JvmField
    @Rule
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    private val clusterArn = "arn:aws:ecs:us-west-2:1234567890:cluster/clusterName"

    @Test
    fun arnIsParsedIntoGoodName() {
        val node = aEcsClusterNode(clusterArn)

        assertThat(node.displayName()).isEqualTo("clusterName")
    }

    @Test
    fun invalidArnFormatDoesntBreakDisplay() {
        val badClusterArn = "arn:aws:ecs:us-west-2:1234567890:clusterArnIsIncorrect/clusterName"
        val node = aEcsClusterNode(badClusterArn)

        assertThat(node.displayName()).isEqualTo(badClusterArn)
    }

    @Test
    fun failedCallShowsErrorNode() {
        val node = aEcsClusterNode()

        resourceCache.addEntry(
            projectRule.project,
            EcsResources.listServiceArns(clusterArn),
            CompletableFuture.failedFuture(RuntimeException("Simulated error"))
        )

        assertThat(node.children).hasSize(1)
        assertThat(node.children).hasOnlyElementsOfType(AwsExplorerErrorNode::class.java)
    }

    @Test
    fun eachServiceArnGetsANode() {
        val node = aEcsClusterNode()

        mockServiceArns("arn1", "arn2")
        mockService("arn1", Service.builder().build())
        mockService("arn2", Service.builder().build())

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

    @Test
    fun noServicesShowsEmpty() {
        val node = aEcsClusterNode()

        mockServiceArns()

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

    private fun aEcsClusterNode(arn: String = clusterArn) = EcsClusterNode(projectRule.project, arn)

    private fun mockServiceArns(vararg arns: String) {
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.listServiceArns(clusterArn),
            CompletableFuture.completedFuture(arns.toList())
        )
    }

    private fun mockService(serviceArn: String, service: Service) {
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeService(clusterArn, serviceArn),
            CompletableFuture.completedFuture(service)
        )
    }
}


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

package software.aws.toolkits.jetbrains.services.ecs

import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerErrorNode
import software.aws.toolkits.jetbrains.services.ecs.resources.EcsResources
import java.util.concurrent.CompletableFuture

class EcsClusterParentNodeTest {

    @JvmField
    @Rule
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Test
    fun failedCallShowsErrorNode() {
        val node = aEcsClusterParentNode()

        resourceCache.addEntry(
            projectRule.project,
            EcsResources.LIST_CLUSTER_ARNS,
            CompletableFuture.failedFuture(RuntimeException("Simulated error"))
        )

        assertThat(node.children).hasSize(1)
        assertThat(node.children).hasOnlyElementsOfType(AwsExplorerErrorNode::class.java)
    }

    @Test
    fun eachArnGetsANode() {
        val node = aEcsClusterParentNode()

        mockClusterArns("arn1", "arn2")

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

    @Test
    fun noClusterShowsEmpty() {
        val node = aEcsClusterParentNode()

        mockClusterArns()

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

    private fun aEcsClusterParentNode() = EcsClusterParentNode(projectRule.project)

    private fun mockClusterArns(vararg arns: String) {
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.LIST_CLUSTER_ARNS,
            CompletableFuture.completedFuture(arns.toList())
        )
    }
}


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

package software.aws.toolkits.jetbrains.services.ecs

import com.intellij.openapi.application.PathManager
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import com.jetbrains.jsonSchema.ide.JsonSchemaService
import com.jetbrains.jsonSchema.impl.inspections.JsonSchemaComplianceInspection
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.openFile
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class TaskSchemaProviderFactoryTest {
    @Rule
    @JvmField
    val projectRule = JavaCodeInsightTestFixtureRule()

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    @Test
    fun testSchemaIsApplied() {
        VfsRootAccess.allowRootAccess(disposableRule.disposable, PathManager.getSystemPath())

        val fixture = projectRule.fixture

        val jsonSchemaComplianceInspection = JsonSchemaComplianceInspection()
        try {
            fixture.enableInspections(jsonSchemaComplianceInspection)

            fixture.openFile(
                "ecs-task-def.json",
                """
                <warning descr="Missing required properties 'containerDefinitions', 'family'">{}</warning>
                """.trimIndent()
            )

            val schemaService = JsonSchemaService.Impl.get(projectRule.project)

            // Get the schema and force the download since the framework disables HTTP based schemas
            val schemas = runInEdtAndGet {
                schemaService.getSchemaFilesForFile(fixture.file.virtualFile)
            }

            val wait = CountDownLatch(schemas.size)
            schemas.filterIsInstance<HttpVirtualFile>().forEach {
                it.refresh(false, false) {
                    wait.countDown()
                }
            }

            wait.await(5, TimeUnit.SECONDS)

            runInEdtAndWait {
                fixture.checkHighlighting()
            }
        } finally {
            fixture.disableInspections(jsonSchemaComplianceInspection)
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.ecs.exec

import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.Presentation
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
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.ecs.model.Service
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.ExplorerDataKeys
import software.aws.toolkits.jetbrains.services.ecs.EcsServiceNode
import software.aws.toolkits.jetbrains.services.ecs.resources.EcsResources
import java.util.concurrent.CompletableFuture

class DisableEcsExecuteCommandTest {
    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @Test
    fun `Disable Command Execution action is not visible if enableExecuteCommand flag is false`() {
        val clusterArn = "arn:aws:ecs:us-east-1:123456789012:cluster/cluster-name"
        val serviceArn = "arn:aws:ecs:us-east-1:123456789012:service/service-name"

        val ecsService = Service.builder()
            .clusterArn(clusterArn)
            .serviceArn(serviceArn)
            .enableExecuteCommand(false)
            .serviceName("service-name")
            .build()

        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeService(clusterArn, serviceArn),
            CompletableFuture.completedFuture(ecsService)
        )

        val sut = DisableEcsExecuteCommand()
        val node = listOf(EcsServiceNode(projectRule.project, ecsService, clusterArn))
        val actionVisibility = sut.updateAction(node).isVisible
        assertThat(actionVisibility).isFalse
    }

    fun AnAction.updateAction(node: List<EcsServiceNode>): Presentation {
        val event = createEventFor(this, node)
        update(event)
        return event.presentation
    }

    private fun createEventFor(action: AnAction, node: List<EcsServiceNode>): AnActionEvent {
        val projectContext = SimpleDataContext.getProjectContext(projectRule.project)
        val dataContext = SimpleDataContext.builder()
            .setParent(projectContext)
            .add(ExplorerDataKeys.SELECTED_NODES, node)
            .build()

        return AnActionEvent.createFromAnAction(action, null, ActionPlaces.UNKNOWN, dataContext)
    }
}