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

package software.aws.toolkits.jetbrains.services.lambda.execution.local

import com.intellij.execution.configurations.RuntimeConfigurationError
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.psi.PsiDocumentManager
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.services.lambda.model.Runtime
import software.aws.toolkits.core.lambda.LambdaArchitecture
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.core.rules.EnvironmentVariableHelper
import software.aws.toolkits.jetbrains.core.credentials.MockCredentialManagerRule
import software.aws.toolkits.jetbrains.core.executables.ExecutableManager
import software.aws.toolkits.jetbrains.services.lambda.execution.sam.SamRunningState
import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommon
import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommonTestUtils
import software.aws.toolkits.jetbrains.services.lambda.sam.SamExecutable
import software.aws.toolkits.jetbrains.utils.getState
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.addClass
import software.aws.toolkits.jetbrains.utils.rules.addModule
import software.aws.toolkits.jetbrains.utils.value
import software.aws.toolkits.jetbrains.utils.xmlElement
import software.aws.toolkits.resources.message
import java.nio.file.Paths

class LocalLambdaRunConfigurationTest {
    @Rule
    @JvmField
    val projectRule = HeavyJavaCodeInsightTestFixtureRule()

    @Rule
    @JvmField
    val envHelper = EnvironmentVariableHelper()

    @Rule
    @JvmField
    val tempDir = TemporaryFolder()

    @Rule
    @JvmField
    val credentialManager = MockCredentialManagerRule()

    private val mockId = "MockCredsId"
    private val mockCreds = AwsBasicCredentials.create("Access", "ItsASecret")
    private val runtime = Runtime.JAVA8
    private val defaultHandler = "com.example.LambdaHandler::handleRequest"

    @Before
    fun setUp() {
        val validSam = SamCommonTestUtils.makeATestSam(SamCommonTestUtils.getMinVersionAsJson())
        preWarmSamVersionCache(validSam.toString())
        ExecutableManager.getInstance().setExecutablePath(SamExecutable(), validSam).value

        credentialManager.addCredentials(mockId, mockCreds)

        val fixture = projectRule.fixture
        val module = fixture.addModule("main")
        fixture.addClass(
            module,
            """
            package com.example;

            public class LambdaHandler {
                public String handleRequest(String request) {
                    return request.toUpperCase();
                }
            }
            """
        )
    }

    @After
    fun tearDown() {
        ExecutableManager.getInstance().removeExecutable(SamExecutable())
    }

    @Test
    fun validConfiguration() {
        val project = projectRule.project
        preWarmLambdaHandlerValidation(project)

        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                credentialsProviderId = mockId
            )

            assertThat(runConfiguration).isNotNull
            runConfiguration.checkConfiguration()
        }
    }

    @Test
    fun `Valid image configuration`() {
        setupMinSamImage()
        val project = projectRule.project
        preWarmLambdaHandlerValidation(project)

        val template = createImageTemplate()
        tempDir.newFolder("hello-world")
        tempDir.newFile("hello-world/Dockerfile3")
        runInEdtAndWait {
            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                isImage = true,
                credentialsProviderId = mockId,
                templateFile = template,
                runtime = LambdaRuntime.JAVA11,
                logicalId = "SomeFunction"
            )

            assertThat(runConfiguration).isNotNull
            runConfiguration.checkConfiguration()
        }
    }

    @Test
    fun `Invalid image-based configuration, no runtime set`() {
        setupMinSamImage()
        runInEdtAndWait {
            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                isImage = true,
                credentialsProviderId = mockId,
                templateFile = createImageTemplate(),
                logicalId = "SomeFunction",
                runtime = null
            )

            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.image.missing_debugger", "null"))
        }
    }

    @Test
    fun `Invalid image-based configuration, unsupported runtime set`() {
        setupMinSamImage()
        runInEdtAndWait {
            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                isImage = true,
                credentialsProviderId = mockId,
                templateFile = createImageTemplate(),
                logicalId = "SomeFunction",
                runtime = null
            )

            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                // "null" comes from runtime UNKNOWN_TO_SDK_VERSION we use since we know it will never be supported
                .hasMessage(message("lambda.image.missing_debugger", "null"))
        }
    }

    @Test
    fun `Invalid configuration, sam version too low for images`() {
        val invalidSam = SamCommonTestUtils.makeATestSam(SamCommonTestUtils.getVersionAsJson("1.0.0"))
        preWarmSamVersionCache(invalidSam.toString())
        ExecutableManager.getInstance().setExecutablePath(SamExecutable(), invalidSam).value

        runInEdtAndWait {
            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                isImage = true,
                input = "TestInput",
                inputIsFile = false,
                templateFile = createImageTemplate(),
                logicalId = "SomeFunction",
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.image.sam_version_too_low", "1.0.0", SamCommon.minImageVersion))
        }
    }

    @Test
    fun samIsNotSet() {
        val fakeSamPath = "NotValid"
        ExecutableManager.getInstance().removeExecutable(SamExecutable())
        ExecutableManager.getInstance().setExecutablePath(SamExecutable(), Paths.get(fakeSamPath))
        Thread.sleep(1000)

        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                credentialsProviderId = mockId
            )

            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("executableCommon.missing_executable", "sam", message("executableCommon.not_installed")))
        }
    }

    @Test
    fun handlerNotSet() {
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                handler = null,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_handler_specified"))
        }
    }

    @Test
    fun runtimeIsNotSet() {
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                runtime = null,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_runtime_specified"))
        }
    }

    @Test
    fun handlerDoesNotExist() {
        val project = projectRule.project
        val invalidHandler = "Fake"
        preWarmLambdaHandlerValidation(project, handler = invalidHandler)

        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = project,
                handler = invalidHandler,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.handler_not_found", invalidHandler))
        }
    }

    @Test
    fun templateFileNotSet() {
        runInEdtAndWait {
            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = null,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.sam.no_template_specified"))
        }
    }

    @Test
    fun logicalFunctionNotSet() {
        runInEdtAndWait {
            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = "test",
                logicalId = null,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.sam.no_function_specified"))
        }
    }

    @Test
    fun templateFileDoesNotExist() {
        runInEdtAndWait {
            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = "IAmFake",
                logicalId = "Function",
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.sam.template_file_not_found"))
        }
    }

    @Test
    fun functionDoesNotExist() {
        runInEdtAndWait {
            val template = tempDir.newFile("template.yaml").also {
                it.writeText(
                    """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Runtime: java8
                      Timeout: 900
                    """.trimIndent()
                )
            }.canonicalPath
            val logicalName = "NotSomeFunction"

            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = template,
                logicalId = logicalName,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.sam.no_such_function", logicalName, FileUtil.normalize(template)))
        }
    }

    @Test
    fun runtimeNotSetInTemplate() {
        runInEdtAndWait {
            val template = tempDir.newFile("template.yaml").also {
                it.writeText(
                    """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Timeout: 900
                    """.trimIndent()
                )
            }.canonicalPath
            val logicalName = "SomeFunction"

            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = template,
                logicalId = logicalName,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("cloudformation.missing_property", "Runtime", logicalName))
        }
    }

    @Test
    fun unsupportedRuntime() {
        runInEdtAndWait {
            val template = tempDir.newFile("template.yaml").also {
                it.writeText(
                    """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Runtime: FAKE
                      Timeout: 900
                    """.trimIndent()
                )
            }.canonicalPath
            val logicalName = "SomeFunction"

            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = template,
                logicalId = logicalName,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.unsupported_runtime", "FAKE"))
        }
    }

    @Test
    fun unsupportedArchitecture() {
        runInEdtAndWait {
            val template = tempDir.newFile("template.yaml").also {
                it.writeText(
                    """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Runtime: java8
                      Architectures:
                        - FAKE
                      Timeout: 900
                    """.trimIndent()
                )
            }.canonicalPath
            val logicalName = "SomeFunction"

            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = template,
                logicalId = logicalName,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.unsupported_architecture", "FAKE"))
        }
    }

    @Test
    fun handlerNotSetInTemplate() {
        runInEdtAndWait {
            val template = tempDir.newFile("template.yaml").also {
                it.writeText(
                    """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Runtime: java8
                      CodeUri: /some/dummy/code/location
                      Timeout: 900
                    """.trimIndent()
                )
            }.canonicalPath
            val logicalName = "SomeFunction"

            val runConfiguration = createTemplateRunConfiguration(
                project = projectRule.project,
                templateFile = template,
                logicalId = logicalName,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_handler_specified", logicalName, template))
        }
    }

    @Test
    fun invalidRegion() {
        val project = projectRule.project
        preWarmLambdaHandlerValidation(project)
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = project,
                region = null,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("configure.validate.no_region_specified"))
        }
    }

    @Test
    fun noCredentials() {
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                credentialsProviderId = null
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_credentials_specified"))
        }
    }

    @Test
    fun invalidCredentials() {
        val credentialName = "DNE"
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                credentialsProviderId = credentialName
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.credential_not_found_error", credentialName))
        }
    }

    @Test
    fun inputIsSet() {
        val project = projectRule.project
        preWarmLambdaHandlerValidation(project)
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = project,
                credentialsProviderId = mockId,
                input = "{}"
            )
            assertThat(runConfiguration).isNotNull
            runConfiguration.checkConfiguration()
        }
    }

    @Test
    fun inputTextIsNotSet() {
        val project = projectRule.project

        preWarmLambdaHandlerValidation(project)

        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = project,
                credentialsProviderId = mockId,
                input = null
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_input_specified"))
        }
    }

    @Test
    fun inputFileDoesNotExist() {
        val project = projectRule.project
        preWarmLambdaHandlerValidation(project)

        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = project,
                input = "DoesNotExist",
                inputIsFile = true,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_input_specified"))
        }
    }

    @Test
    fun inputFileDoeExist() {
        val project = projectRule.project
        val eventFile = projectRule.fixture.addFileToProject("event.json", "TestInputFile")
        preWarmLambdaHandlerValidation(project)

        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = project,
                input = eventFile.virtualFile.path,
                inputIsFile = true,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            runConfiguration.checkConfiguration()
        }
    }

    @Test
    fun inputTextIsResolved() {
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                input = "TestInput",
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThat(getRunConfigState(runConfiguration).settings.input).isEqualTo("TestInput")
        }
    }

    @Test
    fun inputFileIsResolved() {
        val eventFile = projectRule.fixture.addFileToProject("event.json", "TestInputFile")

        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                input = eventFile.virtualFile.path,
                inputIsFile = true,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThat(getRunConfigState(runConfiguration).settings.input).isEqualTo("TestInputFile")
        }
    }

    @Test
    fun inputFileIsSaved() {
        val eventFile = projectRule.fixture.addFileToProject("event.json", "TestInputFile")

        runInEdtAndWait {
            WriteAction.run<Throwable> {
                PsiDocumentManager.getInstance(projectRule.project).getDocument(eventFile)!!.setText("UpdatedTestInputFile")
            }

            assertThat(VfsUtilCore.loadText(eventFile.virtualFile)).isEqualTo("TestInputFile")

            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                input = eventFile.virtualFile.path,
                inputIsFile = true,
                credentialsProviderId = mockId
            )
            assertThat(runConfiguration).isNotNull
            assertThat(getRunConfigState(runConfiguration).settings.input).isEqualTo("UpdatedTestInputFile")
        }
    }

    @Test
    fun readExternalHandlerBasedDoesNotThrowException() {
        // This tests for backwards compatibility, data should not be changed except in backwards compatible ways
        val element = xmlElement(
            """
            <configuration name="HelloWorldFunction" type="aws.lambda" factoryName="Local" temporary="true" nameIsGenerated="true">
              <option name="credentialProviderId" value="profile:default" />
              <option name="architecture" value="arm64" />
              <option name="environmentVariables">
                <map>
                  <entry key="Foo" value="Bar" />
                </map>
              </option>
              <option name="handler" value="helloworld.App::handleRequest" />
              <option name="input" value="""" />
              <option name="inputIsFile" value="false" />
              <option name="logicalFunctionName" />
              <option name="regionId" value="us-west-2" />
              <option name="runtime" value="python3.10" />
              <option name="templateFile" />
              <option name="useTemplate" value="false" />
              <method v="2" />
            </configuration>
        """
        )

        runInEdtAndWait {
            val runConfiguration = samRunConfiguration(projectRule.project)

            runConfiguration.readExternal(element)

            assertThat(runConfiguration.isUsingTemplate()).isFalse()
            assertThat(runConfiguration.templateFile()).isNull()
            assertThat(runConfiguration.logicalId()).isNull()
            assertThat(runConfiguration.handler()).isEqualTo("helloworld.App::handleRequest")
            assertThat(runConfiguration.architecture()).isEqualTo(LambdaArchitecture.ARM64.toString())
            assertThat(runConfiguration.runtime()).isEqualTo(LambdaRuntime.PYTHON3_10)
            assertThat(runConfiguration.environmentVariables()).containsAllEntriesOf(mapOf("Foo" to "Bar"))
            assertThat(runConfiguration.regionId()).isEqualTo("us-west-2")
            assertThat(runConfiguration.credentialProviderId()).isEqualTo("profile:default")
        }
    }

    @Test
    fun readExternalTemplateBasedDoesNotThrowException() {
        // This tests for backwards compatibility, data should not be changed except in backwards compatible ways
        val element = xmlElement(
            """
                <configuration name="HelloWorldFunction" type="aws.lambda" factoryName="Local" temporary="true" nameIsGenerated="true">
                  <option name="credentialProviderId" value="profile:default" />
                  <option name="environmentVariables">
                    <map>
                      <entry key="Foo" value="Bar" />
                    </map>
                  </option>
                  <option name="handler" />
                  <option name="input" value="""" />
                  <option name="inputIsFile" value="false" />
                  <option name="logicalFunctionName" value="HelloWorldFunction" />
                  <option name="regionId" value="us-west-2" />
                  <option name="runtime" />
                  <option name="templateFile" value="template.yaml" />
                  <option name="useTemplate" value="true" />
                  <method v="2" />
                </configuration>
        """
        )

        runInEdtAndWait {
            val runConfiguration = samRunConfiguration(projectRule.project)

            runConfiguration.readExternal(element)

            assertThat(runConfiguration.isUsingTemplate()).isTrue()
            assertThat(runConfiguration.templateFile()).isEqualTo("template.yaml")
            assertThat(runConfiguration.logicalId()).isEqualTo("HelloWorldFunction")
            assertThat(runConfiguration.handler()).isNull()
            assertThat(runConfiguration.runtime()).isNull()
            assertThat(runConfiguration.environmentVariables()).containsAllEntriesOf(mapOf("Foo" to "Bar"))
            assertThat(runConfiguration.regionId()).isEqualTo("us-west-2")
            assertThat(runConfiguration.credentialProviderId()).isEqualTo("profile:default")
        }
    }

    @Test
    fun readInputFileBasedDoesNotThrowException() {
        // This tests for backwards compatibility, data should not be changed except in backwards compatible ways
        val element = xmlElement(
            """
                <configuration name="HelloWorldFunction" type="aws.lambda" factoryName="Local" temporary="true" nameIsGenerated="true">
                  <option name="credentialProviderId" value="profile:default" />
                  <option name="environmentVariables">
                    <map>
                      <entry key="Foo" value="Bar" />
                    </map>
                  </option>
                  <option name="handler" />
                  <option name="input" value="event.json" />
                  <option name="inputIsFile" value="true" />
                  <option name="logicalFunctionName" value="HelloWorldFunction" />
                  <option name="regionId" value="us-west-2" />
                  <option name="runtime" />
                  <option name="templateFile" value="template.yaml" />
                  <option name="useTemplate" value="true" />
                  <method v="2" />
                </configuration>
        """
        )

        runInEdtAndWait {
            val runConfiguration = samRunConfiguration(projectRule.project)

            runConfiguration.readExternal(element)

            assertThat(runConfiguration.isUsingInputFile()).isTrue()
            assertThat(runConfiguration.inputSource()).isEqualTo("event.json")
        }
    }

    @Test
    fun readInputTextBasedDoesNotThrowException() {
        // This tests for backwards compatibility, data should not be changed except in backwards compatible ways
        val element = xmlElement(
            """
                <configuration name="HelloWorldFunction" type="aws.lambda" factoryName="Local" temporary="true" nameIsGenerated="true">
                  <option name="credentialProviderId" value="profile:default" />
                  <option name="environmentVariables">
                    <map>
                      <entry key="Foo" value="Bar" />
                    </map>
                  </option>
                  <option name="handler" />
                  <option name="input" value="{}" />
                  <option name="inputIsFile" value="false" />
                  <option name="logicalFunctionName" value="HelloWorldFunction" />
                  <option name="regionId" value="us-west-2" />
                  <option name="runtime" />
                  <option name="templateFile" value="template.yaml" />
                  <option name="useTemplate" value="true" />
                  <method v="2" />
                </configuration>
        """
        )

        runInEdtAndWait {
            val runConfiguration = samRunConfiguration(projectRule.project)

            runConfiguration.readExternal(element)

            assertThat(runConfiguration.isUsingInputFile()).isFalse()
            assertThat(runConfiguration.inputSource()).isEqualTo("{}")
        }
    }

    @Test
    fun readSamSettings() {
        // This tests for backwards compatibility, data should not be changed except in backwards compatible ways
        val element = xmlElement(
            """
                <configuration name="HelloWorldFunction" type="aws.lambda" factoryName="Local" temporary="true" nameIsGenerated="true">
                  <option name="credentialProviderId" value="profile:default" />
                  <option name="environmentVariables">
                    <map>
                      <entry key="Foo" value="Bar" />
                    </map>
                  </option>
                  <option name="handler" />
                  <option name="input" value="{}" />
                  <option name="inputIsFile" value="false" />
                  <option name="logicalFunctionName" value="HelloWorldFunction" />
                  <option name="regionId" value="us-west-2" />
                  <option name="runtime" />
                  <option name="templateFile" value="template.yaml" />
                  <option name="useTemplate" value="true" />
                  <sam>
                    <option name="buildInContainer" value="true" />
                    <option name="dockerNetwork" value="aws-lambda" />
                    <option name="skipImagePull" value="true" />
                    <option name="additionalBuildArgs" value="--debug" />
                    <option name="additionalLocalArgs" value="--skip-pull-image" />
                  </sam>
                  <method v="2" />
                </configuration>
        """
        )

        runInEdtAndWait {
            val runConfiguration = samRunConfiguration(projectRule.project)

            runConfiguration.readExternal(element)

            assertThat(runConfiguration.skipPullImage).isTrue()
            assertThat(runConfiguration.buildInContainer).isTrue()
            assertThat(runConfiguration.dockerNetwork).isEqualTo("aws-lambda")
            assertThat(runConfiguration.additionalBuildArgs).isEqualTo("--debug")
            assertThat(runConfiguration.additionalLocalArgs).isEqualTo("--skip-pull-image")
        }
    }

    @Test // https://github.com/aws/aws-toolkit-jetbrains/issues/1072
    fun creatingACopyDoesNotAliasFields() {
        runInEdtAndWait {
            val runConfiguration = createHandlerBasedRunConfiguration(
                project = projectRule.project,
                credentialsProviderId = mockId,
                input = "{}"
            )

            val clonedConfiguration = runConfiguration.clone() as LocalLambdaRunConfiguration
            clonedConfiguration.name = "Cloned"

            clonedConfiguration.useInputText("Changed input")

            assertThat(clonedConfiguration.inputSource()).isNotEqualTo(runConfiguration.inputSource())
        }
    }

    private fun preWarmLambdaHandlerValidation(project: Project, handler: String = defaultHandler) =
        preWarmLambdaHandlerValidation(project, runtime, handler)

    private fun getRunConfigState(runConfiguration: LocalLambdaRunConfiguration) = getState(runConfiguration) as SamRunningState

    private fun createImageTemplate(): String = tempDir.newFile("template.yaml").also {
        it.writeText(
            """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      PackageType: Image
                      Timeout: 900
                    Metadata:
                      DockerTag: v1
                      DockerContext: ./hello-world
                      Dockerfile: Dockerfile3
            """.trimIndent()
        )
    }.canonicalPath

    private fun setupMinSamImage() {
        val validSam = SamCommonTestUtils.makeATestSam(SamCommonTestUtils.getVersionAsJson(SamCommon.minImageVersion.toString()))
        preWarmSamVersionCache(validSam.toString())
        ExecutableManager.getInstance().setExecutablePath(SamExecutable(), validSam).value
    }
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.remote

import com.intellij.execution.RunManager
import com.intellij.openapi.project.Project
import software.aws.toolkits.core.region.AwsRegion
import software.aws.toolkits.jetbrains.core.region.getDefaultRegion
import software.aws.toolkits.jetbrains.services.lambda.execution.LambdaRunConfigurationType

fun createRunConfiguration(
    project: Project,
    input: String? = "",
    inputIsFile: Boolean = false,
    regionId: AwsRegion? = getDefaultRegion(),
    credentialId: String? = "MockCredentials",
    functionName: String? = "DummyFunction"
): RemoteLambdaRunConfiguration {
    val runManager = RunManager.getInstance(project)
    val factory = LambdaRunConfigurationType.getInstance()
        .configurationFactories
        .first { it is RemoteLambdaRunConfigurationFactory }
    val runConfigurationAndSettings = runManager.createConfiguration("Test", factory)
    val runConfiguration = runConfigurationAndSettings.configuration as RemoteLambdaRunConfiguration
    runManager.addConfiguration(runConfigurationAndSettings)

    runConfiguration.credentialProviderId(credentialId)
    runConfiguration.regionId(regionId?.id)
    runConfiguration.functionName(functionName)
    if (inputIsFile) {
        runConfiguration.useInputFile(input)
    } else {
        runConfiguration.useInputText(input)
    }

    return runConfiguration
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.remote

import com.intellij.execution.Output
import com.intellij.openapi.util.io.FileUtil
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.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.stub
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.lambda.LambdaClient
import software.amazon.awssdk.services.lambda.model.InvokeRequest
import software.amazon.awssdk.services.lambda.model.InvokeResponse
import software.amazon.awssdk.services.lambda.model.LambdaException
import software.amazon.awssdk.services.lambda.model.LogType
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.core.credentials.MockCredentialManagerRule
import software.aws.toolkits.jetbrains.core.region.getDefaultRegion
import software.aws.toolkits.jetbrains.utils.executeRunConfigurationAndWait
import java.nio.charset.StandardCharsets
import java.util.Base64

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

    @Rule
    @JvmField
    val mockClientManager = MockClientManagerRule()

    @Rule
    @JvmField
    val credentialManager = MockCredentialManagerRule()

    @Before
    fun setUp() {
        credentialManager.addCredentials(CREDENTIAL_ID, AwsBasicCredentials.create("Access", "Secret"))
    }

    @Test
    fun happyCase() {
        val logMessage = "Some Logs"
        val responsePayload = "Hello World"
        val functionError = "Some error"

        val requestCaptor = argumentCaptor<InvokeRequest>()
        mockClientManager.create<LambdaClient>().stub {
            on { invoke(requestCaptor.capture()) } doReturn InvokeResponse.builder()
                .logResult(Base64.getEncoder().encodeToString(logMessage.toByteArray()))
                .payload(SdkBytes.fromString(responsePayload, StandardCharsets.UTF_8))
                .functionError(functionError)
                .build()
        }

        val output = executeLambda()

        val request = requestCaptor.firstValue
        assertThat(request.functionName()).isEqualTo(FUNCTION_NAME)
        assertThat(request.logType()).isEqualTo(LogType.TAIL)

        assertThat(output.stdout).contains(logMessage)
        assertThat(output.stdout).contains(responsePayload)

        assertThat(output.stderr).contains(functionError)
    }

    @Test
    fun inputText() {
        val input = "InputText"

        val requestCaptor = argumentCaptor<InvokeRequest>()
        mockClientManager.create<LambdaClient>().stub {
            on { invoke(requestCaptor.capture()) } doReturn InvokeResponse.builder().build()
        }

        executeLambda(inputText = input)
        val request = requestCaptor.firstValue
        assertThat(request.payload()).isEqualTo(SdkBytes.fromString(input, StandardCharsets.UTF_8))
    }

    @Test
    fun inputFile() {
        val input = "InputText"
        val inputFile = FileUtil.createTempFile("inputFile", "tmp")
        inputFile.writeText(input)

        val requestCaptor = argumentCaptor<InvokeRequest>()
        mockClientManager.create<LambdaClient>().stub {
            on { invoke(requestCaptor.capture()) } doReturn InvokeResponse.builder().build()
        }

        executeLambda(inputText = inputFile.absolutePath, inputFile = true)
        val request = requestCaptor.firstValue
        assertThat(request.payload()).isEqualTo(SdkBytes.fromString(input, StandardCharsets.UTF_8))
    }

    @Test
    fun serviceException() {
        val dummyMessage = "Dummy Exception"
        mockClientManager.create<LambdaClient>().stub {
            on { invoke(any<InvokeRequest>()) } doThrow LambdaException.builder().message(dummyMessage).build()
        }

        val output = executeLambda()

        assertThat(output.stderr).contains(dummyMessage)
    }

    private fun executeLambda(inputText: String = "Input", inputFile: Boolean = false): Output {
        val runConfiguration = createRunConfiguration(
            project = projectRule.project,
            input = inputText,
            inputIsFile = inputFile,
            credentialId = CREDENTIAL_ID,
            functionName = FUNCTION_NAME,
            regionId = getDefaultRegion()
        )

        return executeRunConfigurationAndWait(runConfiguration)
    }

    private companion object {
        const val CREDENTIAL_ID = "MockCredentials"
        const val FUNCTION_NAME = "DummyFunction"
    }
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.remote

import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.testFramework.MapDataContext
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 software.amazon.awssdk.services.lambda.model.PackageType
import software.amazon.awssdk.services.lambda.model.Runtime
import software.aws.toolkits.core.region.AwsRegion
import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager
import software.aws.toolkits.jetbrains.services.iam.IamRole
import software.aws.toolkits.jetbrains.services.lambda.LambdaFunction

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

    @Test
    fun validRunConfigurationIsCreated() {
        val functionName = "SomeFunction"
        val region = AwsRegion("us-east-1", "us-east-1", "aws")
        val credentialProviderId = MockAwsConnectionManager.getInstance(projectRule.project).connectionSettings()?.credentials

        val lambdaLocation = LambdaFunction(
            name = functionName,
            description = null,
            packageType = PackageType.ZIP,
            arn = "arn",
            lastModified = "someDate",
            handler = "someHandler",
            runtime = Runtime.values().first(),
            envVariables = emptyMap(),
            timeout = 60,
            memorySize = 128,
            xrayEnabled = false,
            role = IamRole("DummyRoleArn")
        )

        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(lambdaLocation)
            assertThat(runConfiguration).isNotNull
            val configuration = runConfiguration?.configuration as RemoteLambdaRunConfiguration
            assertThat(configuration.functionName()).isEqualTo(functionName)
            assertThat(configuration.credentialProviderId()).isEqualTo(credentialProviderId?.id)
            assertThat(configuration.regionId()).isEqualTo(region.id)
            assertThat(configuration.name).isEqualTo("[Remote] $functionName")
        }
    }

    private fun createRunConfiguration(function: LambdaFunction): ConfigurationFromContext? {
        val dataContext = MapDataContext()
        val context = createContext(function, dataContext)
        val producer = RunConfigurationProducer.getInstance(RemoteLambdaRunConfigurationProducer::class.java)
        return producer.createConfigurationFromContext(context)
    }

    private fun createContext(function: LambdaFunction, dataContext: MapDataContext): ConfigurationContext {
        dataContext.put(CommonDataKeys.PROJECT, projectRule.project)
        dataContext.put(Location.DATA_KEY, RemoteLambdaLocation(projectRule.project, function))
        return ConfigurationContext.getFromContext(dataContext)
    }
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.remote

import com.intellij.execution.configurations.RuntimeConfigurationError
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.psi.PsiDocumentManager
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.junit.rules.TemporaryFolder
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.aws.toolkits.jetbrains.core.credentials.MockCredentialManagerRule
import software.aws.toolkits.jetbrains.utils.getState
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
import software.aws.toolkits.resources.message

class RemoteLambdaRunConfigurationTest {
    @Rule
    @JvmField
    val projectRule = HeavyJavaCodeInsightTestFixtureRule()

    @Rule
    @JvmField
    val tempDir = TemporaryFolder()

    @Rule
    @JvmField
    val credentialManager = MockCredentialManagerRule()

    private val mockCreds = AwsBasicCredentials.create("Access", "ItsASecret")

    @Before
    fun setUp() {
        credentialManager.addCredentials("MockCredentials", mockCreds)
    }

    @Test
    fun functionNotSet() {
        val runConfiguration = createRunConfiguration(
            project = projectRule.project,
            functionName = null
        )
        assertThat(runConfiguration).isNotNull
        assertThatThrownBy { runConfiguration.checkConfiguration() }
            .isInstanceOf(RuntimeConfigurationError::class.java)
            .hasMessage(message("lambda.run_configuration.no_function_specified"))
    }

    @Test
    fun invalidRegion() {
        val runConfiguration = createRunConfiguration(
            project = projectRule.project,
            regionId = null
        )
        assertThat(runConfiguration).isNotNull
        assertThatThrownBy { runConfiguration.checkConfiguration() }
            .isInstanceOf(RuntimeConfigurationError::class.java)
            .hasMessage(message("configure.validate.no_region_specified"))
    }

    @Test
    fun noCredentials() {
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                credentialId = null
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_credentials_specified"))
        }
    }

    @Test
    fun invalidCredentials() {
        val credentialName = "DNE"
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                credentialId = credentialName
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.credential_not_found_error", credentialName))
        }
    }

    @Test
    fun inputIsSet() {
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = "{}"
            )
            assertThat(runConfiguration).isNotNull
            runConfiguration.checkConfiguration()
        }
    }

    @Test
    fun inputTextIsNotSet() {
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = null
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_input_specified"))
        }
    }

    @Test
    fun inputFileDoesNotExist() {
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = "DoesNotExist",
                inputIsFile = true
            )
            assertThat(runConfiguration).isNotNull
            assertThatThrownBy { runConfiguration.checkConfiguration() }
                .isInstanceOf(RuntimeConfigurationError::class.java)
                .hasMessage(message("lambda.run_configuration.no_input_specified"))
        }
    }

    @Test
    fun inputFileDoeExist() {
        val eventFile = projectRule.fixture.addFileToProject("event.json", "TestInputFile")

        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = eventFile.virtualFile.path,
                inputIsFile = true
            )
            assertThat(runConfiguration).isNotNull
            runConfiguration.checkConfiguration()
        }
    }

    @Test
    fun inputTextIsResolved() {
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = "TestInput"
            )
            assertThat(runConfiguration).isNotNull
            assertThat(getRunConfigState(runConfiguration).settings.input).isEqualTo("TestInput")
        }
    }

    @Test
    fun inputFileIsResolved() {
        val eventFile = projectRule.fixture.addFileToProject("event.json", "TestInputFile")

        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = eventFile.virtualFile.path,
                inputIsFile = true
            )
            assertThat(runConfiguration).isNotNull
            assertThat(getRunConfigState(runConfiguration).settings.input).isEqualTo("TestInputFile")
        }
    }

    @Test
    fun inputFileIsSaved() {
        val eventFile = projectRule.fixture.addFileToProject("event.json", "TestInputFile")

        runInEdtAndWait {
            WriteAction.run<Throwable> {
                PsiDocumentManager.getInstance(projectRule.project).getDocument(eventFile)!!.setText("UpdatedTestInputFile")
            }

            assertThat(VfsUtilCore.loadText(eventFile.virtualFile)).isEqualTo("TestInputFile")

            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = eventFile.virtualFile.path,
                inputIsFile = true
            )
            assertThat(runConfiguration).isNotNull
            assertThat(getRunConfigState(runConfiguration).settings.input).isEqualTo("UpdatedTestInputFile")
        }
    }

    @Test // https://github.com/aws/aws-toolkit-jetbrains/issues/1072
    fun creatingACopyDoesNotAliasFields() {
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(
                project = projectRule.project,
                input = "{}"
            )

            val clonedConfiguration = runConfiguration.clone() as RemoteLambdaRunConfiguration
            clonedConfiguration.name = "Cloned"

            clonedConfiguration.useInputText("Changed input")

            assertThat(clonedConfiguration.inputSource()).isNotEqualTo(runConfiguration.inputSource())
        }
    }

    private fun getRunConfigState(runConfiguration: RemoteLambdaRunConfiguration) = getState(runConfiguration) as RemoteLambdaState
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.sam

import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.core.credentials.MockCredentialManagerRule
import software.aws.toolkits.jetbrains.services.lambda.execution.local.LocalLambdaRunConfiguration
import software.aws.toolkits.jetbrains.services.lambda.execution.local.createHandlerBasedRunConfiguration
import software.aws.toolkits.jetbrains.services.lambda.execution.local.createTemplateRunConfiguration
import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommonTestUtils.addSamTemplate
import software.aws.toolkits.jetbrains.utils.getState
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.addClass
import software.aws.toolkits.jetbrains.utils.rules.addModule
import software.aws.toolkits.jetbrains.utils.setUpGradleProject

class JavaSamRunningStateTest {
    @Rule
    @JvmField
    val projectRule = HeavyJavaCodeInsightTestFixtureRule()

    @Rule
    @JvmField
    val credentialManager = MockCredentialManagerRule()

    private val mockCredsId = "mockId"

    @Before
    fun setUp() {
        // force fixture to be created before write action in EDT
        projectRule.fixture
        credentialManager.addCredentials(mockCredsId)

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

    @Test
    fun addsJavaHomeEnvVarForJavaTemplate() {
        val logicalId = "SomeFunction"

        val template = projectRule.fixture.addSamTemplate(
            logicalName = logicalId,
            codeUri = "/some/dummy/code/location",
            runtime = LambdaRuntime.JAVA8,
            handler = "com.example.LambdaHandler::handleRequest"
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template.virtualFile.path,
            logicalId = logicalId,
            credentialsProviderId = mockCredsId
        )

        val settings = runInEdtAndGet {
            getRunConfigState(runConfig).settings
        }

        val request = SamRunningState.buildBuildLambdaRequest(projectRule.project, settings)
        assertThat(request.buildEnvVars).extractingByKey("JAVA_HOME").isEqualTo(IdeaTestUtil.getMockJdk18Path().absolutePath)
    }

    @Test
    fun addsJavaHomeEnvVarForJavaHandler() {
        val module = this.projectRule.fixture.addModule("main")
        this.projectRule.fixture.addClass(
            module,
            """
            package com.example;

            public class LambdaHandler {
                public String handleRequest(String request) {
                    return request.toUpperCase();
                }
            }
            """
        )
        this.projectRule.setUpGradleProject()

        val runConfig = createHandlerBasedRunConfiguration(
            project = projectRule.project,
            runtime = LambdaRuntime.JAVA8.toSdkRuntime(),
            input = "\"Hello World\"",
            credentialsProviderId = mockCredsId
        )

        val settings = runInEdtAndGet {
            getRunConfigState(runConfig).settings
        }

        val request = SamRunningState.buildBuildLambdaRequest(projectRule.project, settings)
        assertThat(request.buildEnvVars).extractingByKey("JAVA_HOME").isEqualTo(IdeaTestUtil.getMockJdk18Path().absolutePath)
    }

    private fun getRunConfigState(runConfiguration: LocalLambdaRunConfiguration) = getState(runConfiguration) as SamRunningState
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.sam

import com.intellij.execution.executors.DefaultDebugExecutor
import com.intellij.execution.executors.DefaultRunExecutor
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.junit.rules.TemporaryFolder
import org.mockito.kotlin.mock
import software.amazon.awssdk.services.lambda.model.Runtime
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.services.lambda.execution.local.createHandlerBasedRunConfiguration
import software.aws.toolkits.jetbrains.services.lambda.execution.local.createTemplateRunConfiguration
import software.aws.toolkits.jetbrains.services.lambda.execution.remote.RemoteLambdaRunConfiguration

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

    @Rule
    @JvmField
    val tempDir = TemporaryFolder()

    @Test
    fun canRunSupportedRuntimeHandler() {
        val runConfig =
            createHandlerBasedRunConfiguration(project = projectRule.project, runtime = Runtime.JAVA8)

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun cannotRunWrongConfiguration() {
        val runConfig: RemoteLambdaRunConfiguration = mock()
        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, runConfig)).isFalse()
        }
    }

    @Test
    fun canRunUnsupportedRuntimeHandler() {
        val runConfig =
            createHandlerBasedRunConfiguration(project = projectRule.project, runtime = Runtime.UNKNOWN_TO_SDK_VERSION)

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun canDebugSupportedRuntimeHandler() {
        val runConfig =
            createHandlerBasedRunConfiguration(project = projectRule.project, runtime = Runtime.JAVA8)

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultDebugExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun cannotDebugUnsupportedRuntimeHandler() {
        val runConfig =
            createHandlerBasedRunConfiguration(project = projectRule.project, runtime = Runtime.UNKNOWN_TO_SDK_VERSION)

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultDebugExecutor.EXECUTOR_ID, runConfig)).isFalse()
        }
    }

    @Test
    fun canRunSupportedRuntimeTemplate() {
        val template = addTemplate(
            """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Runtime: java8
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun canRunSupportedImageRuntimeTemplate() {
        val template = addTemplate(
            """
            Resources:
                MyFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                        PackageType: Image
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            isImage = true,
            runtime = LambdaRuntime.JAVA11,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun canRunUnsupportedRuntimeTemplate() {
        val template = addTemplate(
            """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Runtime: FAKE
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun canRunUnSupportedImageRuntimeTemplate() {
        val template = addTemplate(
            """
            Resources:
                MyFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                        PackageType: Image
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            isImage = true,
            runtime = null,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultRunExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun canDebugSupportedRuntimeTemplate() {
        val template = addTemplate(
            """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Runtime: java8
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultDebugExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun canDebugSupportedImageRuntimeTemplate() {
        val template = addTemplate(
            """
            Resources:
                MyFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                        PackageType: Image
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            isImage = true,
            runtime = LambdaRuntime.JAVA11,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultDebugExecutor.EXECUTOR_ID, runConfig)).isTrue()
        }
    }

    @Test
    fun cannotDebugUnsupportedRuntimeTemplate() {
        val template = addTemplate(
            """
                Resources:
                  SomeFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                      Handler: com.example.LambdaHandler::handleRequest
                      CodeUri: /some/dummy/code/location
                      Runtime: FAKE
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultDebugExecutor.EXECUTOR_ID, runConfig)).isFalse()
        }
    }

    @Test
    fun cannotDebugUnsupportedImageRuntimeTemplate() {
        val template = addTemplate(
            """
            Resources:
                MyFunction:
                    Type: AWS::Serverless::Function
                    Properties:
                        PackageType: Image
            """.trimIndent()
        )

        val runConfig = createTemplateRunConfiguration(
            project = projectRule.project,
            templateFile = template,
            isImage = true,
            runtime = null,
            logicalId = "SomeFunction"
        )

        runInEdtAndWait {
            assertThat(SamInvokeRunner().canRun(DefaultDebugExecutor.EXECUTOR_ID, runConfig)).isFalse()
        }
    }

    private fun addTemplate(template: String): String = tempDir.newFile("template.yaml").also {
        it.writeText(template)
    }.absolutePath
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.template

import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.psi.PsiElement
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.openFile

class LambdaRunLineMarkerContributorTest {

    @Rule
    @JvmField
    val projectRule = JavaCodeInsightTestFixtureRule()

    @Test
    fun testServerlessFunctionIsMarked() {
        projectRule.fixture.openFile(
            "template.yaml",
            """
Resources:
  ServerlessFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: target/HelloWorld-1.0.jar
      Handler: helloworld.ConcreteClass::handleRequest
      Runtime: java8
"""
        )

        runAndAssertionMarks(projectRule.fixture) { marks ->
            assertThat(marks).hasSize(1)
            assertThat(marks.first().lineMarkerInfo.element).isNotNull.satisfies {
                assertThat(it!!.text).isEqualTo("ServerlessFunction")
            }
        }
    }

    @Test
    fun testLambdaFunctionIsMarked() {
        projectRule.fixture.openFile(
            "template.yaml",
            """
Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      CodeUri: target/HelloWorld-1.0.jar
      Handler: helloworld.ConcreteClass::handleRequest
      Runtime: java8
"""
        )
        runAndAssertionMarks(projectRule.fixture) { marks ->
            assertThat(marks).hasSize(1)
            assertThat(marks.first().lineMarkerInfo.element).isNotNull.satisfies {
                assertThat(it!!.text).isEqualTo("LambdaFunction")
            }
        }
    }

    @Test
    fun testEmptyMarks() {
        projectRule.fixture.openFile(
            "template.yaml",
            """
Resources:
  FooApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: prod
      DefinitionUri: swagger.yml
"""
        )
        runAndAssertionMarks(projectRule.fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

    private fun runAndAssertionMarks(fixture: CodeInsightTestFixture, assertion: (List<LineMarkerInfo.LineMarkerGutterIconRenderer<PsiElement>>) -> Unit) {
        runInEdtAndWait {
            val marks = fixture.findAllGutters().filterIsInstance<LineMarkerInfo.LineMarkerGutterIconRenderer<PsiElement>>()
            assertion(marks)
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.lambda.execution.template

import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.yaml.psi.YAMLFile
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.services.lambda.sam.findByLocation
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule

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

    val sut = YamlLambdaRunLineMarkerContributor()

    @Test
    fun `finds AWS-Lambda-Function`() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText(
                "template.yaml",
                // language=YAML
                """
                Resources:
                  Function:
                    Type: AWS::Lambda::Function
                    Properties:
                      Code: foo.zip
                      Handler: foobar.App::handleRequest
                      Runtime: java8
                """.trimIndent()
            ) as YAMLFile
            val psiElement = psiFile.findByLocation("Resources.Function")?.key ?: throw RuntimeException("Can't find function")

            assertThat(sut.getInfo(psiElement)).isNotNull
        }
    }

    @Test
    fun `finds AWS-Serverless-Function`() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText(
                "template.yaml",
                // language=YAML
                """
                Resources:
                  Function:
                    Type: AWS::Serverless::Function
                    Properties:
                      Code: foo.zip
                      Handler: foobar.App::handleRequest
                      Runtime: java8
                """.trimIndent()
            ) as YAMLFile
            val psiElement = psiFile.findByLocation("Resources.Function")?.key ?: throw RuntimeException("Can't find function")

            assertThat(sut.getInfo(psiElement)).isNotNull
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.lambda.java

import org.junit.Assert.assertFalse
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.services.lambda.completion.HandlerCompletionProvider
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule

class JavaHandlerCompletionProviderTest {

    @Rule
    @JvmField
    val projectRule = JavaCodeInsightTestFixtureRule()

    @Test
    fun completionIsNotSupportedJava8() {
        val provider = HandlerCompletionProvider(projectRule.project, LambdaRuntime.JAVA8)
        assertFalse(provider.isCompletionSupported)
    }

    @Test
    fun completionIsNotSupportedJava11() {
        val provider = HandlerCompletionProvider(projectRule.project, LambdaRuntime.JAVA11)
        assertFalse(provider.isCompletionSupported)
    }

    @Test
    fun completionIsNotSupportedJava17() {
        val provider = HandlerCompletionProvider(projectRule.project, LambdaRuntime.JAVA17)
        assertFalse(provider.isCompletionSupported)
    }
}


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

package software.aws.toolkits.jetbrains.services.lambda.java

import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.roots.ModuleRootManagerEx
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.testFramework.IdeaTestUtil
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 software.aws.toolkits.core.rules.EnvironmentVariableHelper
import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommon
import software.aws.toolkits.jetbrains.services.lambda.sam.SamOptions
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.addModule
import software.aws.toolkits.jetbrains.utils.setSamExecutableFromEnvironment
import software.aws.toolkits.jetbrains.utils.setUpGradleProject
import software.aws.toolkits.jetbrains.utils.setUpMavenProject
import software.aws.toolkits.resources.message
import java.nio.file.Paths

class JavaLambdaBuilderTest {
    @Rule
    @JvmField
    val projectRule = HeavyJavaCodeInsightTestFixtureRule()

    @Rule
    @JvmField
    val envVarsRule = EnvironmentVariableHelper()

    private val sut = JavaLambdaBuilder()

    @Before
    fun setUp() {
        setSamExecutableFromEnvironment()

        projectRule.fixture.addModule("main")
    }

    @Test
    fun gradleRootProjectHandlerBaseDirIsCorrect() {
        val psiClass = projectRule.setUpGradleProject()

        val baseDir = sut.handlerBaseDirectory(projectRule.module, psiClass.methods.first())
        val moduleRoot = ModuleRootManagerEx.getInstanceEx(projectRule.module).contentRoots.first().path
        assertThat(baseDir).isEqualTo(Paths.get(moduleRoot))
    }

    @Test
    fun gradleRootProjectBuildDirectoryIsCorrect() {
        projectRule.setUpGradleProject()

        val baseDir = sut.getBuildDirectory(projectRule.module)
        val moduleRoot = ModuleRootManagerEx.getInstanceEx(projectRule.module).contentRoots.first().path
        assertThat(baseDir.toAbsolutePath()).isEqualTo(Paths.get(moduleRoot, SamCommon.SAM_BUILD_DIR, "build"))
    }

    @Test
    fun mavenRootPomHandlerBaseDirIsCorrect() {
        val psiClass = projectRule.setUpMavenProject()

        val module = ModuleManager.getInstance(projectRule.project).modules.first()
        val baseDir = sut.handlerBaseDirectory(module, psiClass.methods.first())
        val moduleRoot = ModuleRootManagerEx.getInstanceEx(module).contentRoots.first().path
        assertThat(baseDir).isEqualTo(Paths.get(moduleRoot))
    }

    @Test
    fun mavenRootPomBuildDirectoryIsCorrect() {
        projectRule.setUpMavenProject()

        val module = ModuleManager.getInstance(projectRule.project).modules.first()
        val baseDir = sut.getBuildDirectory(module)
        val moduleRoot = ModuleRootManagerEx.getInstanceEx(module).contentRoots.first().path
        assertThat(baseDir.toAbsolutePath()).isEqualTo(Paths.get(moduleRoot, SamCommon.SAM_BUILD_DIR, "build"))
    }

    @Test
    fun unsupportedBuildSystem() {
        val handlerPsi = projectRule.fixture.addClass(
            """
            package com.example;

            public class SomeClass {
                public static String upperCase(String input) {
                    return input.toUpperCase();
                }
            }
            """.trimIndent()
        )

        assertThatThrownBy {
            sut.handlerBaseDirectory(projectRule.module, handlerPsi)
        }.isInstanceOf(IllegalStateException::class.java)
            .hasMessageEndingWith(message("lambda.build.java.unsupported_build_system", projectRule.module.name))
    }

    @Test
    fun javaHomePassedWhenNotInContainer() {
        envVarsRule.remove("JAVA_HOME")

        ModuleRootModificationUtil.setModuleSdk(projectRule.module, IdeaTestUtil.getMockJdk18())
        assertThat(sut.additionalBuildEnvironmentVariables(projectRule.project, projectRule.module, SamOptions(buildInContainer = false)))
            .extractingByKey("JAVA_HOME").isEqualTo(IdeaTestUtil.getMockJdk18Path().absolutePath)
    }

    @Test
    fun javaHomeNotPassedWhenInContainer() {
        envVarsRule.remove("JAVA_HOME")

        assertThat(sut.additionalBuildEnvironmentVariables(projectRule.project, projectRule.module, SamOptions(buildInContainer = true)))
            .doesNotContainKey("JAVA_HOME")
    }
}


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

package software.aws.toolkits.jetbrains.services.lambda.java

import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.DumbServiceImpl
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import software.amazon.awssdk.services.lambda.model.Runtime
import software.aws.toolkits.jetbrains.services.lambda.BuiltInRuntimeGroups
import software.aws.toolkits.jetbrains.services.lambda.Lambda
import software.aws.toolkits.jetbrains.services.lambda.LambdaHandlerResolver
import software.aws.toolkits.jetbrains.services.lambda.RuntimeGroup
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.openClass

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

    @Before
    fun setUp() {
        projectRule.fixture.addClass(
            """
            package com.amazonaws.services.lambda.runtime;
            @SuppressWarnings("ALL")
            public interface Context {}
            """
        )

        projectRule.fixture.addClass(
            """
            package com.amazonaws.services.lambda.runtime;

            import com.amazonaws.services.lambda.runtime.Context;

            public interface RequestHandler<I, O> {
                O handleRequest(I input, Context context);
            }
            """
        )

        projectRule.fixture.addClass(
            """
            package com.amazonaws.services.lambda.runtime;

            import java.io.InputStream;
            import java.io.OutputStream;
            import java.io.IOException;

            public interface RequestStreamHandler {
                void handleRequest(InputStream input, OutputStream output, Context context) throws IOException;
            }
            """
        )
    }

    @Test
    fun testClassHandler() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;
            import com.amazonaws.services.lambda.runtime.RequestHandler;

            public class LambdaHandler implements RequestHandler {
                public String handleRequest(String request, Context context) {
                    return request.toUpperCase();
                }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler"
            )
            assertThat(elements).hasSize(1)
            assertThat(elements[0]).isInstanceOfSatisfying(PsiClass::class.java) {
                assertThat(it.qualifiedName).isEqualTo("com.example.LambdaHandler")
            }
        }
    }

    @Test
    fun testMethodReferenceWhenUsingHandlerInterface() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;
            import com.amazonaws.services.lambda.runtime.RequestHandler;

            public class LambdaHandler implements RequestHandler {
                public String handleRequest(String request, Context context) {
                    return request.toUpperCase();
                }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler::handleRequest"
            )
            assertThat(elements).hasSize(1)
            assertThat(elements[0]).isInstanceOfSatisfying(PsiMethod::class.java) {
                assertThat(it.containingClass?.qualifiedName).isEqualTo("com.example.LambdaHandler")
                assertThat(it.name).isEqualTo("handleRequest")
            }
        }
    }

    @Test
    fun testMethodHandler() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;

            public class LambdaHandler {
                public String handleRequest(String request, Context context) {
                    return request.toUpperCase();
                }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler::handleRequest"
            )
            assertThat(elements).hasSize(1)
            assertThat(elements[0]).isInstanceOfSatisfying(PsiMethod::class.java) {
                assertThat(it.containingClass?.qualifiedName).isEqualTo("com.example.LambdaHandler")
                assertThat(it.name).isEqualTo("handleRequest")
            }
        }
    }

    @Test
    fun testClassNotFound() {
        val fixture = projectRule.fixture

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler"
            )
            assertThat(elements).isEmpty()
        }
    }

    @Test
    fun testMethodNotFound() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;

            public class LambdaHandler {
                public String handleRequest(String request, Context context) {
                    return request.toUpperCase();
                }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler::someMethod"
            )
            assertThat(elements).isEmpty()
        }
    }

    @Test
    fun testBaseMethodsReferenced() {
        val fixture = projectRule.fixture

        fixture.addClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;

            public abstract class AbstractHandler {
                public void handleRequest(InputStream input, OutputStream output, Context context) { }

                protected abstract void internalHandle();
            }
            """
        )

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;
            import java.io.InputStream;
            import java.io.OutputStream;

            public class ConcreteHandler extends AbstractHandler {
                @Override
                protected  void internalHandle() { }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.ConcreteHandler::handleRequest"
            )
            assertThat(elements).hasSize(1)
            assertThat(elements[0]).isInstanceOfSatisfying(PsiMethod::class.java) {
                assertThat(it.containingClass?.qualifiedName).isEqualTo("com.example.AbstractHandler")
                assertThat(it.name).isEqualTo("handleRequest")
            }
        }
    }

    @Test
    fun `resolves handlers with 2 parameters`() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;
            import java.io.InputStream;
            import java.io.OutputStream;

            public class LambdaHandler {
                public static void streamsOnly(InputStream input, OutputStream output) { }

                public static void contextAndAnything(Object input, Context context) { }

                public static void doesNotResolve(Object input, OutputStream outputStream) { }

                public static void doesNotResolve2(InputStream input, Object outputStream) { }
            }
            """
        )

        runInEdtAndWait {
            val isHandlerValid: (String) -> Boolean = { handler -> Lambda.isHandlerValid(fixture.project, Runtime.JAVA17, handler) }

            assertThat(isHandlerValid("com.example.LambdaHandler::streamsOnly")).isTrue
            assertThat(isHandlerValid("com.example.LambdaHandler::contextAndAnything")).isTrue
            assertThat(isHandlerValid("com.example.LambdaHandler::doesNotResolve")).isFalse
            assertThat(isHandlerValid("com.example.LambdaHandler::doesNotResolve2")).isFalse
        }
    }

    @Test
    fun `resolves handlers with 1 parameter`() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;
            import java.io.InputStream;

            public class LambdaHandler {
                public static void works(InputStream input) { }

                public static void alsoWorks(Object input) { }

                public static void alsoAlsoWorks(Context context) { }
            }
            """
        )

        runInEdtAndWait {
            val isHandlerValid: (String) -> Boolean = { handler -> Lambda.isHandlerValid(fixture.project, Runtime.JAVA17, handler) }

            assertThat(isHandlerValid("com.example.LambdaHandler::works")).isTrue
            assertThat(isHandlerValid("com.example.LambdaHandler::alsoWorks")).isTrue
            assertThat(isHandlerValid("com.example.LambdaHandler::alsoAlsoWorks")).isTrue
        }
    }

    @Test
    fun `resolves handlers with 3 parameters`() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;
            import java.io.InputStream;
            import java.io.OutputStream;

            public class LambdaHandler {
                public static void works(InputStream input, OutputStream output, Context context) { }

                public static void doesntWork(Object input, OutputStream output, Context context) { }

                public static void doesntWork2(InputStream input, Object output, Context context) { }

                public static void doesntWork3(InputStream input, OutputStream output, Object context) { }
            }
            """
        )

        runInEdtAndWait {
            val isHandlerValid: (String) -> Boolean = { handler -> Lambda.isHandlerValid(fixture.project, Runtime.JAVA17, handler) }

            assertThat(isHandlerValid("com.example.LambdaHandler::works")).isTrue
            assertThat(isHandlerValid("com.example.LambdaHandler::doesntWork")).isFalse
            assertThat(isHandlerValid("com.example.LambdaHandler::doesntWork2")).isFalse
            assertThat(isHandlerValid("com.example.LambdaHandler::doesntWork3")).isFalse
        }
    }

    @Test
    fun testMultipleMethodsInSameClassParameterLength() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;

            public class LambdaHandler {
                // Should be picked due to more params
                public static void handleRequest(InputStream input, OutputStream output, Context context) { }

                public static void handleRequest(InputStream input, OutputStream output) { }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler::handleRequest"
            )
            assertThat(elements[0]).isInstanceOfSatisfying(PsiMethod::class.java) {
                assertThat(it.containingClass?.qualifiedName).isEqualTo("com.example.LambdaHandler")
                assertThat(it.name).isEqualTo("handleRequest")
                assertThat(it.parameterList.toString()).contains("InputStream input, OutputStream output, Context context")
            }
        }
    }

    @Test
    fun testMultipleMethodsInSameClassContextTakesPriority() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;

            public class LambdaHandler {
                public static void handleRequest(InputStream input, OutputStream output) { }

                // Should be picked due to ends in Context
                public static void handleRequest(InputStream input, Context context) { }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler::handleRequest"
            )
            assertThat(elements[0]).isInstanceOfSatisfying(PsiMethod::class.java) {
                assertThat(it.containingClass?.qualifiedName).isEqualTo("com.example.LambdaHandler")
                assertThat(it.name).isEqualTo("handleRequest")
                assertThat(it.parameterList.toString()).contains("InputStream input, Context context")
            }
        }
    }

    @Test
    fun testMultipleMethodsInSameClassUndefinedBehavior() {
        val fixture = projectRule.fixture

        fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;

            // This enters the realm of undefined behavior, return nothing?
            public class LambdaHandler {
                public static void handleRequest(Object input) { }

                public static void handleRequest(InputStream input) { }
            }
            """
        )

        runInEdtAndWait {
            val elements = Lambda.findPsiElementsForHandler(
                fixture.project,
                Runtime.JAVA17,
                "com.example.LambdaHandler::handleRequest"
            )
            assertThat(elements).isEmpty()
        }
    }

    @Test
    fun testFindWorksInDumbMode() {
        projectRule.fixture.openClass(
            """
            package com.example;

            import com.amazonaws.services.lambda.runtime.Context;

            // This enters the realm of undefined behavior, return nothing?
            public class LambdaHandler {
                public static void handleRequest(Object input) { }

                public static void handleRequest(InputStream input) { }
            }
            """
        )

        runInDumbMode {
            runInEdtAndWait {
                val elements = Lambda.findPsiElementsForHandler(
                    projectRule.project,
                    Runtime.JAVA17,
                    "com.example.LambdaHandler::handleRequest"
                )
                assertThat(elements).isEmpty()
            }
        }
    }

    @Test
    fun testDetermineHandlerWorksInDumbMode() {
        val psiClass = projectRule.fixture.openClass(
            """
            package com.example;

            public class LambdaHandler {
                public static void handleRequest(Object input) { }
            }
            """
        )

        runInDumbMode {
            runInEdtAndWait {
                val handler = JavaLambdaHandlerResolver()
                    .determineHandler(psiClass.findMethodsByName("handleRequest", false)[0])
                assertThat(handler).isEqualTo("com.example.LambdaHandler::handleRequest")
            }
        }
    }

    @Test
    fun testDetermineHandlersWorksInDumbMode() {
        val psiClass = projectRule.fixture.openClass(
            """
            package com.example;

            public class LambdaHandler {
                public static void handleRequest(Object input) { }
            }
            """
        )

        runInDumbMode {
            runInEdtAndWait {
                val handler = JavaLambdaHandlerResolver()
                    .determineHandlers(
                        psiClass.findMethodsByName("handleRequest", false)[0],
                        psiClass.containingFile.virtualFile
                    )
                assertThat(handler).containsExactly("com.example.LambdaHandler::handleRequest")
            }
        }
    }

    @Test
    fun handlerDisplayNames() {
        val sut = LambdaHandlerResolver.getInstance(RuntimeGroup.getById(BuiltInRuntimeGroups.Java))

        assertThat(sut.handlerDisplayName("com.example.LambdaHandler::handleRequest")).isEqualTo("LambdaHandler.handleRequest")
        assertThat(sut.handlerDisplayName("com.example.LambdaHandler")).isEqualTo("LambdaHandler")
        assertThat(sut.handlerDisplayName("LambdaHandler::handleRequest")).isEqualTo("LambdaHandler.handleRequest")
    }

    private fun runInDumbMode(block: () -> Unit) {
        val dumbServiceImpl = DumbService.getInstance(projectRule.project) as DumbServiceImpl
        try {
            runInEdtAndWait { dumbServiceImpl.isDumb = true }
            block()
        } finally {
            runInEdtAndWait { dumbServiceImpl.isDumb = false }
        }
    }
}