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

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

import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.ProjectRule
import kotlinx.coroutines.runBlocking
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.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
import software.amazon.awssdk.services.ec2.Ec2Client
import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest
import software.amazon.awssdk.services.ec2.model.DescribeInstancesResponse
import software.amazon.awssdk.services.ec2.model.IamInstanceProfile
import software.amazon.awssdk.services.ec2.model.Instance
import software.amazon.awssdk.services.ec2.model.Reservation
import software.amazon.awssdk.services.ecs.EcsClient
import software.amazon.awssdk.services.ecs.model.ContainerDefinition
import software.amazon.awssdk.services.ecs.model.ContainerInstance
import software.amazon.awssdk.services.ecs.model.Deployment
import software.amazon.awssdk.services.ecs.model.DeploymentRolloutState
import software.amazon.awssdk.services.ecs.model.DescribeContainerInstancesRequest
import software.amazon.awssdk.services.ecs.model.DescribeContainerInstancesResponse
import software.amazon.awssdk.services.ecs.model.DescribeServicesRequest
import software.amazon.awssdk.services.ecs.model.DescribeServicesResponse
import software.amazon.awssdk.services.ecs.model.DescribeTasksRequest
import software.amazon.awssdk.services.ecs.model.DescribeTasksResponse
import software.amazon.awssdk.services.ecs.model.ExecuteCommandRequest
import software.amazon.awssdk.services.ecs.model.ExecuteCommandResponse
import software.amazon.awssdk.services.ecs.model.LaunchType
import software.amazon.awssdk.services.ecs.model.Service
import software.amazon.awssdk.services.ecs.model.Task
import software.amazon.awssdk.services.ecs.model.TaskDefinition
import software.amazon.awssdk.services.ecs.model.TaskOverride
import software.amazon.awssdk.services.iam.IamClient
import software.amazon.awssdk.services.iam.model.EvaluationResult
import software.amazon.awssdk.services.iam.model.GetInstanceProfileRequest
import software.amazon.awssdk.services.iam.model.GetInstanceProfileResponse
import software.amazon.awssdk.services.iam.model.InstanceProfile
import software.amazon.awssdk.services.iam.model.PolicyEvaluationDecisionType
import software.amazon.awssdk.services.iam.model.Role
import software.amazon.awssdk.services.iam.model.SimulatePrincipalPolicyRequest
import software.amazon.awssdk.services.iam.model.SimulatePrincipalPolicyResponse
import software.aws.toolkits.core.ConnectionSettings
import software.aws.toolkits.core.credentials.aToolkitCredentialsProvider
import software.aws.toolkits.core.utils.test.aString
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.region.US_EAST_1
import software.aws.toolkits.jetbrains.core.tools.MockToolManagerRule
import software.aws.toolkits.jetbrains.core.tools.Tool
import software.aws.toolkits.jetbrains.services.ecs.ContainerDetails
import software.aws.toolkits.jetbrains.services.ecs.exec.EcsExecUtils.createCommand
import software.aws.toolkits.jetbrains.services.ecs.resources.EcsResources
import software.aws.toolkits.jetbrains.services.ssm.SsmPlugin
import java.nio.file.Path
import java.util.concurrent.CompletableFuture

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

    @JvmField
    @Rule
    val mockClientManager = MockClientManagerRule()

    @Rule
    @JvmField
    val disposableRule = DisposableRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @JvmField
    @Rule
    val toolManager = MockToolManagerRule()

    private lateinit var ecsClient: EcsClient
    private lateinit var iamClient: IamClient
    private lateinit var ec2Client: Ec2Client
    val clusterArn = "sample-cluster-arn-123"
    val serviceArn = "sample-service-arn-123"
    private val taskArn = "sample-task-arn-123"
    private val taskRoleArn = "sample-task-role-arn-123"
    private val taskDefinitionArn = "sample-task-definition-arn-123"

    @Before
    fun setup() {
        ecsClient = mockClientManager.create()
        iamClient = mockClientManager.create()
        ec2Client = mockClientManager.create()
    }

    @Test
    fun `Get the task role overriden in the task`() {
        val task = Task.builder()
            .taskArn(taskArn)
            .overrides(TaskOverride.builder().taskRoleArn(taskRoleArn).build())
            .build()
        ecsClient.stub {
            on { describeTasks(any<DescribeTasksRequest>()) } doAnswer { DescribeTasksResponse.builder().tasks(listOf(task)).build() }
        }
        val taskRoleArnResponse = EcsExecUtils.getTaskRoleArn(projectRule.project, clusterArn, taskArn)
        assertThat(taskRoleArnResponse).isEqualTo(taskRoleArn)
    }

    @Test
    fun `Get the task role arn attached to the Task Definition`() {
        val task = Task.builder()
            .taskArn(taskArn)
            .taskDefinitionArn(taskDefinitionArn)
            .overrides(TaskOverride.builder().build())
            .build()
        val taskDefinition = TaskDefinition.builder().taskDefinitionArn(taskDefinitionArn).taskRoleArn(taskRoleArn).build()

        ecsClient.stub {
            on { describeTasks(any<DescribeTasksRequest>()) } doAnswer { DescribeTasksResponse.builder().tasks(listOf(task)).build() }
        }
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeTaskDefinition(task.taskDefinitionArn()),
            CompletableFuture.completedFuture(taskDefinition)
        )
        val taskRoleArnResponse = EcsExecUtils.getTaskRoleArn(projectRule.project, clusterArn, taskArn)
        assertThat(taskRoleArnResponse).isEqualTo(taskRoleArn)
    }

    @Test
    fun `If no role specified in the task and launch type is Fargate role Arn is null`() {
        val task = Task.builder()
            .taskArn(taskArn)
            .taskDefinitionArn(taskDefinitionArn)
            .launchType(LaunchType.FARGATE)
            .overrides(TaskOverride.builder().build())
            .build()

        val taskDefinition = TaskDefinition.builder().taskDefinitionArn(taskDefinitionArn).build()
        ecsClient.stub {
            on { describeTasks(any<DescribeTasksRequest>()) } doAnswer { DescribeTasksResponse.builder().tasks(listOf(task)).build() }
        }
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeTaskDefinition(task.taskDefinitionArn()),
            CompletableFuture.completedFuture(taskDefinition)
        )

        val taskRoleArnResponse = EcsExecUtils.getTaskRoleArn(projectRule.project, clusterArn, taskArn)
        assertThat(taskRoleArnResponse).isNull()
    }

    @Test
    fun `If no role found in task get the role from EC2 Instance Profile`() {
        val task = Task.builder()
            .taskArn(taskArn)
            .taskDefinitionArn(taskDefinitionArn)
            .launchType(LaunchType.EC2)
            .containerInstanceArn("sample-container-123")
            .overrides(TaskOverride.builder().build())
            .build()
        val taskDefinition = TaskDefinition.builder().taskDefinitionArn(taskDefinitionArn).build()
        val containerInstance = ContainerInstance.builder().containerInstanceArn("sample-container-123").ec2InstanceId("sample-ec2-id").build()
        val iamInstanceProfile = IamInstanceProfile.builder().arn("sample:instance-profile/IamInstanceProfile123").build()
        val instanceProfile = InstanceProfile.builder().roles(Role.builder().arn("sample-task-role-arn-ec2").build()).build()
        val sampleInstance = Instance.builder().instanceId("sample-ec2-id").iamInstanceProfile(iamInstanceProfile).build()
        val reservation = Reservation.builder().instances(sampleInstance).build()

        ecsClient.stub {
            on { describeTasks(any<DescribeTasksRequest>()) } doAnswer { DescribeTasksResponse.builder().tasks(listOf(task)).build() }
            on { describeContainerInstances(any<DescribeContainerInstancesRequest>()) } doAnswer {
                DescribeContainerInstancesResponse.builder().containerInstances(containerInstance).build()
            }
        }

        ec2Client.stub {
            on { describeInstances(any<DescribeInstancesRequest>()) } doAnswer { DescribeInstancesResponse.builder().reservations(reservation).build() }
        }

        iamClient.stub {
            on { getInstanceProfile(any<GetInstanceProfileRequest>()) } doAnswer {
                GetInstanceProfileResponse.builder().instanceProfile(instanceProfile).build()
            }
        }

        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeTaskDefinition(task.taskDefinitionArn()),
            CompletableFuture.completedFuture(taskDefinition)
        )

        val taskRoleArnResponse = EcsExecUtils.getTaskRoleArn(projectRule.project, clusterArn, taskArn)
        assertThat(taskRoleArnResponse).isEqualTo("sample-task-role-arn-ec2")
    }

    @Test
    fun `If no role found in task or EC2 instance profile null is returned`() {
        val task = Task.builder()
            .taskArn(taskArn)
            .taskDefinitionArn(taskDefinitionArn)
            .launchType(LaunchType.EC2)
            .containerInstanceArn("sample-container-123")
            .overrides(TaskOverride.builder().build())
            .build()
        val taskDefinition = TaskDefinition.builder().taskDefinitionArn(taskDefinitionArn).build()
        val containerInstance = ContainerInstance.builder().containerInstanceArn("sample-container-123").ec2InstanceId("sample-ec2-id").build()
        val iamInstanceProfile = IamInstanceProfile.builder().arn("sample:instance-profile/IamInstanceProfile123").build()
        val instanceProfile = InstanceProfile.builder().roles(Role.builder().build()).build()
        val sampleInstance = Instance.builder().instanceId("sample-ec2-id").iamInstanceProfile(iamInstanceProfile).build()
        val reservation = Reservation.builder().instances(sampleInstance).build()

        ecsClient.stub {
            on { describeTasks(any<DescribeTasksRequest>()) } doAnswer { DescribeTasksResponse.builder().tasks(listOf(task)).build() }
            on { describeContainerInstances(any<DescribeContainerInstancesRequest>()) } doAnswer {
                DescribeContainerInstancesResponse.builder().containerInstances(containerInstance).build()
            }
        }
        ec2Client.stub {
            on { describeInstances(any<DescribeInstancesRequest>()) } doAnswer { DescribeInstancesResponse.builder().reservations(reservation).build() }
        }

        iamClient.stub {
            on { getInstanceProfile(any<GetInstanceProfileRequest>()) } doAnswer {
                GetInstanceProfileResponse.builder().instanceProfile(instanceProfile).build()
            }
        }
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeTaskDefinition(task.taskDefinitionArn()),
            CompletableFuture.completedFuture(taskDefinition)
        )

        val taskRoleArnResponse = EcsExecUtils.getTaskRoleArn(projectRule.project, clusterArn, taskArn)
        assertThat(taskRoleArnResponse).isNull()
    }

    @Test
    fun `Ensure that required permissions are present`() {
        val task = Task.builder()
            .taskArn(taskArn)
            .overrides(TaskOverride.builder().build())
            .taskDefinitionArn(taskDefinitionArn)
            .build()
        val evaluationResult = EvaluationResult.builder().evalDecision(PolicyEvaluationDecisionType.ALLOWED).build()
        val taskDefinition = TaskDefinition.builder().taskDefinitionArn(taskDefinitionArn).taskRoleArn(taskRoleArn).build()
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeTaskDefinition(task.taskDefinitionArn()),
            CompletableFuture.completedFuture(taskDefinition)
        )

        ecsClient.stub {
            on { describeTasks(any<DescribeTasksRequest>()) } doAnswer { DescribeTasksResponse.builder().tasks(listOf(task)).build() }
        }

        iamClient.stub {
            on { simulatePrincipalPolicy(any<SimulatePrincipalPolicyRequest>()) } doAnswer {
                SimulatePrincipalPolicyResponse.builder().evaluationResults(evaluationResult).build()
            }
        }

        val haveRequiredPermissions = EcsExecUtils.checkRequiredPermissions(projectRule.project, clusterArn, taskArn)
        assertThat(haveRequiredPermissions).isTrue
    }

    @Test
    fun `Task role doesn't have required permissions`() {
        val task = Task.builder()
            .taskArn(taskArn)
            .overrides(TaskOverride.builder().build())
            .taskDefinitionArn(taskDefinitionArn)
            .build()

        val evaluationResult = EvaluationResult.builder().evalDecision(PolicyEvaluationDecisionType.IMPLICIT_DENY).build()
        val taskDefinition = TaskDefinition.builder().taskDefinitionArn(taskDefinitionArn).taskRoleArn(taskRoleArn).build()
        resourceCache.addEntry(
            projectRule.project,
            EcsResources.describeTaskDefinition(task.taskDefinitionArn()),
            CompletableFuture.completedFuture(taskDefinition)
        )
        ecsClient.stub {
            on { describeTasks(any<DescribeTasksRequest>()) } doAnswer { DescribeTasksResponse.builder().tasks(listOf(task)).build() }
        }

        iamClient.stub {
            on { simulatePrincipalPolicy(any<SimulatePrincipalPolicyRequest>()) } doAnswer {
                SimulatePrincipalPolicyResponse.builder().evaluationResults(evaluationResult).build()
            }
        }

        val haveRequiredPermissions = EcsExecUtils.checkRequiredPermissions(projectRule.project, clusterArn, taskArn)
        assertThat(haveRequiredPermissions).isFalse
    }

    @Test
    fun `Service update in progress returns false`() {
        val ecsService = Service.builder()
            .clusterArn(clusterArn)
            .serviceArn(serviceArn)
            .enableExecuteCommand(true)
            .serviceName("service-name")
            .deployments(listOf(Deployment.builder().rolloutState(DeploymentRolloutState.IN_PROGRESS).build()))
            .build()
        ecsClient.stub {
            on {
                describeServices(any<DescribeServicesRequest>())
            } doAnswer {
                DescribeServicesResponse.builder().services(ecsService).build()
            }
        }
        val serviceStateStable = runBlocking {
            EcsExecUtils.ensureServiceIsInStableState(projectRule.project, ecsService)
        }
        assertThat(serviceStateStable).isFalse
    }

    @Test
    fun `Service is currently stable returns true`() {
        val ecsService = Service.builder()
            .clusterArn(clusterArn)
            .serviceArn(serviceArn)
            .enableExecuteCommand(true)
            .serviceName("service-name")
            .deployments(listOf(Deployment.builder().rolloutState(DeploymentRolloutState.COMPLETED).build()))
            .build()
        ecsClient.stub {
            on {
                describeServices(any<DescribeServicesRequest>())
            } doAnswer {
                DescribeServicesResponse.builder().services(ecsService).build()
            }
        }
        val serviceStateStable = runBlocking {
            EcsExecUtils.ensureServiceIsInStableState(projectRule.project, ecsService)
        }
        assertThat(serviceStateStable).isTrue
    }

    @Test
    fun `SSM command is created correctly`() {
        val credentials = AwsBasicCredentials.create(aString(), aString())
        val mockCredentialProvider = mock<AwsCredentialsProvider> {
            on {
                resolveCredentials()
            }.thenAnswer { credentials }
        }
        val connection = ConnectionSettings(aToolkitCredentialsProvider(delegate = mockCredentialProvider), US_EAST_1)
        val cluster = aString()
        val taskId = aString()
        val containerName = aString()
        val command = aString()
        val sessionId = aString()
        val token = aString()
        val streamUrl = aString()
        val cliPath = Path.of("dummy", "file", "path")

        val mockTool = mock<Tool<SsmPlugin>> {
            on {
                path
            }.doReturn(cliPath)
        }

        toolManager.registerTool(SsmPlugin, mockTool)

        ecsClient.stub {
            on {
                executeCommand(
                    ExecuteCommandRequest.builder()
                        .cluster(cluster)
                        .task(taskId)
                        .container(containerName)
                        .interactive(true)
                        .command(command)
                        .build()
                )
            }.thenReturn(
                ExecuteCommandResponse.builder().session {
                    it.sessionId(sessionId)
                    it.tokenValue(token)
                    it.streamUrl(streamUrl)
                }.build()
            )

            val cmd = createCommand(
                projectRule.project,
                connection,
                ContainerDetails(Service.builder().clusterArn(cluster).build(), ContainerDefinition.builder().name(containerName).build()),
                taskId,
                command
            )

            val expectedSession = """{\"sessionId\":\"$sessionId\",\"streamUrl\":\"$streamUrl\",\"tokenValue\":\"$token\"}"""

            assertThat(cmd.commandLineString).isEqualTo(
                """${cliPath.toAbsolutePath()} $expectedSession us-east-1 StartSession"""
            )

            assertThat(cmd.environment).containsEntry("AWS_REGION", "us-east-1")
                .containsEntry("AWS_ACCESS_KEY_ID", credentials.accessKeyId())
                .containsEntry("AWS_SECRET_ACCESS_KEY", credentials.secretAccessKey())
        }
    }
}


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

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

import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.Presentation
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.amazon.awssdk.services.ecs.model.Service
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.ExplorerDataKeys
import software.aws.toolkits.jetbrains.services.ecs.EcsServiceNode
import software.aws.toolkits.jetbrains.services.ecs.resources.EcsResources
import java.util.concurrent.CompletableFuture

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

    @Rule
    @JvmField
    val projectRule = ProjectRule()

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

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

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

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

    fun AnAction.updateAction(node: List<EcsServiceNode>): Presentation {
        val event = createEventFor(this, node)
        update(event)
        return event.presentation
    }
    private fun createEventFor(action: AnAction, node: List<EcsServiceNode>): AnActionEvent {
        val projectContext = SimpleDataContext.getProjectContext(projectRule.project)
        val dataContext = SimpleDataContext.builder()
            .setParent(projectContext)
            .add(ExplorerDataKeys.SELECTED_NODES, node)
            .build()

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


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

package software.aws.toolkits.jetbrains.services.federation

import com.github.tomakehurst.wiremock.client.WireMock.aResponse
import com.github.tomakehurst.wiremock.client.WireMock.requestMatching
import com.github.tomakehurst.wiremock.core.WireMockConfiguration
import com.github.tomakehurst.wiremock.junit.WireMockRule
import com.github.tomakehurst.wiremock.matching.MatchResult
import com.intellij.testFramework.ApplicationRule
import com.intellij.testFramework.RuleChain
import org.apache.http.HttpHost
import org.apache.http.conn.routing.HttpRoute
import org.apache.http.conn.routing.HttpRoutePlanner
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClientBuilder
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials
import software.amazon.awssdk.services.sts.StsClient
import software.amazon.awssdk.services.sts.model.GetFederationTokenRequest
import software.amazon.awssdk.services.sts.model.GetFederationTokenResponse
import software.aws.toolkits.core.ConnectionSettings
import software.aws.toolkits.core.utils.tryOrNull
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.core.credentials.MockCredentialManagerRule
import software.aws.toolkits.jetbrains.core.region.getDefaultRegion
import java.net.InetAddress

class AwsConsoleUrlFactoryTest {
    val applicationRule = ApplicationRule()
    val mockClientManager = MockClientManagerRule()
    val mockCredentialManager = MockCredentialManagerRule()

    @Rule
    @JvmField
    val ruleChain = RuleChain(applicationRule, mockClientManager, mockCredentialManager)

    @Rule
    @JvmField
    val wireMock = WireMockRule(WireMockConfiguration.wireMockConfig().dynamicPort())

    private lateinit var stsMock: StsClient
    private lateinit var httpBuilderMock: HttpClientBuilder
    private lateinit var httpClient: CloseableHttpClient
    private lateinit var sut: AwsConsoleUrlFactory

    @Before
    fun setUp() {
        httpBuilderMock = mock<HttpClientBuilder>()
        httpClient = HttpClientBuilder.create()
            .setRoutePlanner(HttpRoutePlanner { _, _, _ -> HttpRoute(HttpHost(InetAddress.getLoopbackAddress(), wireMock.port(), "http")) })
            .build()
        whenever(httpBuilderMock.setUserAgent(any())).thenReturn(httpBuilderMock)
        whenever(httpBuilderMock.build()).thenReturn(httpClient)

        stsMock = mockClientManager.create()

        wireMock.stubFor(
            requestMatching { request ->
                val action = "getSigninToken"
                val matches = tryOrNull { request.queryParameter("Action").values().any { it == action } }
                    ?: request.bodyAsString.contains("Action=$action")

                MatchResult.of(matches)
            }.willReturn(
                aResponse().withStatus(200).withBody("""{"SigninToken": "signinToken"}""")
            )
        )

        sut = AwsConsoleUrlFactory
    }

    @After
    fun tearDown() {
        tryOrNull { httpClient.close() }
    }

    @Test
    fun `getSigninToken uses temporary credentials as-is`() {
        val tempCreds = AwsSessionCredentials.create("accessKey", "secretKey", "sessionToken")
        val connectionSettings = ConnectionSettings(mockCredentialManager.createCredentialProvider("tempCreds", tempCreds), getDefaultRegion())

        verifyNoMoreInteractions(stsMock)
        assertThat(sut.getSigninToken(connectionSettings, httpBuilderMock)).isEqualTo("signinToken")
    }

    @Test
    fun `getSigninToken requests federation token for long-term credentials`() {
        val longCreds = AwsBasicCredentials.create("basicKeyId", "basicSecretKey")
        val connectionSettings = ConnectionSettings(mockCredentialManager.createCredentialProvider("longCreds", longCreds), getDefaultRegion())

        whenever(stsMock.getFederationToken(any<GetFederationTokenRequest>())).thenReturn(
            GetFederationTokenResponse.builder()
                .credentials {
                    it.accessKeyId("federatedAccessKey")
                    it.secretAccessKey("federatedSecretAccessKey")
                    it.sessionToken("federatedSessionToken")
                }
                .build()
        )

        assertThat(sut.getSigninToken(connectionSettings, httpBuilderMock)).isEqualTo("signinToken")
    }

    @Test
    fun `build sign-in url`() {
        val tempCreds = AwsSessionCredentials.create("accessKey", "secretKey", "sessionToken")
        val connectionSettings = ConnectionSettings(mockCredentialManager.createCredentialProvider("tempCreds", tempCreds), getDefaultRegion())

        assertThat(sut.getSigninUrl(connectionSettings = connectionSettings, destination = "/something", httpClientBuilder = httpBuilderMock))
            .isEqualTo(
                """
                https://signin.aws.amazon.com/federation?Action=login&SigninToken=signinToken&Destination=https%3A%2F%2Fus-east-1.console.aws.amazon.com%2Fsomething
                """.trimIndent()
            )
    }
}


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

package software.aws.toolkits.jetbrains.services.federation.psireferences

import com.intellij.ide.highlighter.JavaFileType
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.runReadAction
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.impl.source.resolve.reference.CommentsReferenceContributor
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
import com.intellij.psi.javadoc.PsiDocToken
import com.intellij.psi.search.PsiElementProcessor
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.testFramework.runInEdtAndGet
import com.jetbrains.extensions.python.toPsi
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule

class ArnPsiReferenceProviderTest {
    @Rule
    @JvmField
    val projectRule = CodeInsightTestFixtureRule()

    @Test
    fun `matches valid arns`() {
        val valid = listOf(
            "arn:aws:lambda::123456789012:function:adsfadfsa",
            "arn:aws:lambda:us-west-2:123456789012:function:adsfadfsa",
            "arn:aws:s3:::bucket_name",
            "arn:aws-cn:s3:::bucket_name",
            "arn:aws-us-gov:s3:::bucket_name"
        )

        valid.forEach {
            assertThat(ArnPsiReferenceProvider.ARN_REGEX.findAll(it).toList())
                .withFailMessage { "Input should have matched regex with single result, but did not: $it" }
                .hasSize(1)
        }
    }

    @Test
    fun `doesn't match invalid arns`() {
        val invalid = listOf(
            "arn:asdfadsfadfsfdsafdas",
            "arn:::::function:adsfadfsa"
        )

        invalid.forEach {
            assertThat(ArnPsiReferenceProvider.ARN_REGEX.findAll(it).toList()).withFailMessage { "Input should not have matched regex, but did: $it" }
                .isEmpty()
        }
    }

    @Test
    fun `matches subset if arn is partially valid`() {
        val pairs = listOf(
            "arn:arn:aws:lambda::123456789012:function:ad\"sfadfsa" to "arn:aws:lambda::123456789012:function:ad",
            "arn:arn:aws:lambda::123456789012:function:ad   adsfa\"sfadfsa" to "arn:aws:lambda::123456789012:function:ad",
            "arn:aws:iam::123456789012:user/Development/product_1234/*" to "arn:aws:iam::123456789012:user/Development/product_1234/*",
            "arn:aws:s3:::my_corporate_bucket/Development/*" to "arn:aws:s3:::my_corporate_bucket/Development/*",
            "arn:aws:s3:::my_corporate_bucket/Development/dasfa " to "arn:aws:s3:::my_corporate_bucket/Development/dasfa"
        )

        pairs.forEach { pair ->
            val (str, match) = pair
            assertThat(ArnPsiReferenceProvider.ARN_REGEX.findAll(str).toList())
                .withFailMessage { "Input should have partially matched regex with single result but did not: $str" }
                .satisfies {
                    assertThat(it).hasSize(1)
                    assertThat(it.first().value).isEqualTo(match)
                }
        }
    }

    @Test
    fun `attaches annotation to ARN-like PsiElements`() {
        // language=TEXT
        val expected = "arn:aws:lambda::123456789012:function"
        // language=Java
        val contents = """
            class TestClass {
                // an amazing comment with arn: $expected
                /*
                * a C-style $expected comment
                */
                String single = "$expected";
                /**
                * a very good javadoc with $expected and things
                */
                String partialMatch = "\"$expected\"";
            }
        """.trimIndent()

        // we don't have access to [JavaReferenceContributor] in our sandbox (it comes from the Java Internationalization plugin),
        // so register the PsiDocToken contributor manually to be able to test JavaDoc ARN resolution
        ReferenceProvidersRegistry.getInstance().getRegistrar(JavaLanguage.INSTANCE).registerReferenceProvider(
            PlatformPatterns.psiElement(
                PsiDocToken::class.java
            ),
            CommentsReferenceContributor.COMMENTS_REFERENCE_PROVIDER_TYPE.provider
        )

        val file = runInEdtAndGet {
            projectRule.fixture.configureByText(JavaFileType.INSTANCE, contents)
        }.virtualFile

        val elements = mutableListOf<ArnReference>()
        runReadAction {
            PsiTreeUtil.processElements(
                file.toPsi(projectRule.project),
                PsiElementProcessor { child ->
                    elements.addAll(child.references.filterIsInstance<ArnReference>())

                    return@PsiElementProcessor true
                }
            )

            assertThat(elements).hasSize(5).allSatisfy {
                assertThat(it.value)
                    .withFailMessage { "Expected ArnReference with value of '$expected' from PsiElement: $it" }
                    .isEqualTo(expected)
            }
        }
    }

    @Test
    fun `attaches single reference when multiple PSI elements are applicable`() {
        // we assume that ElementManipulators do the correct thing for us
        // language=YAML
        val contents = """
            Hello:
              Some:
                Nesting: 1 # arn:aws:lambda::123456789012:function
        """.trimIndent()

        val file = runInEdtAndGet {
            projectRule.fixture.configureByText("yaml.yaml", contents)
        }.virtualFile

        val elements = mutableListOf<ArnReference>()
        runReadAction {
            PsiTreeUtil.processElements(
                file.toPsi(projectRule.project),
                PsiElementProcessor { child ->
                    elements.addAll(child.references.filterIsInstance<ArnReference>())

                    return@PsiElementProcessor true
                }
            )

            assertThat(elements).hasSize(1).allSatisfy {
                assertThat(it).isInstanceOf(ArnReference::class.java)
            }
        }
    }

    @Test
    fun `attaches reference to JSON values`() {
        // language=JSON
        val contents = """
            {
              "hello": "arn:aws:lambda::123456789012:function"
            }
        """.trimIndent()

        val file = runInEdtAndGet {
            projectRule.fixture.configureByText("json.json", contents)
        }.virtualFile

        val elements = mutableListOf<ArnReference>()
        runReadAction {
            PsiTreeUtil.processElements(
                file.toPsi(projectRule.project),
                PsiElementProcessor { child ->
                    elements.addAll(child.references.filterIsInstance<ArnReference>())

                    return@PsiElementProcessor true
                }
            )

            assertThat(elements).hasSize(1).allSatisfy {
                assertThat(it).isInstanceOf(ArnReference::class.java)
            }
        }
    }
}


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

package software.aws.toolkits.jetbrains.services.iam

import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.reset
import org.mockito.kotlin.stub
import software.amazon.awssdk.services.iam.IamClient
import software.amazon.awssdk.services.iam.model.CreateRoleRequest
import software.amazon.awssdk.services.iam.model.CreateRoleResponse
import software.amazon.awssdk.services.iam.model.DeleteRoleRequest
import software.amazon.awssdk.services.iam.model.DeleteRoleResponse
import software.amazon.awssdk.services.iam.model.MalformedPolicyDocumentException
import software.amazon.awssdk.services.iam.model.PutRolePolicyRequest
import software.amazon.awssdk.services.iam.model.PutRolePolicyResponse
import software.aws.toolkits.core.utils.delegateMock

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

    private val iamMock = delegateMock<IamClient>()

    @Before
    fun setUp() {
        reset(iamMock)
    }

    @Test
    fun roleIsCreated() {
        val createRoleCaptor = argumentCaptor<CreateRoleRequest>()
        val putRoleCaptor = argumentCaptor<PutRolePolicyRequest>()

        iamMock.stub {
            on { createRole(createRoleCaptor.capture()) } doReturn CreateRoleResponse.builder().role { role ->
                role.arn(TEST_ROLE_ARN)
                role.roleName(TEST_ROLE_NAME)
            }.build()

            on { putRolePolicy(putRoleCaptor.capture()) } doReturn PutRolePolicyResponse.builder().build()
        }

        runInEdtAndWait {
            val roleDialog = CreateIamRoleDialog(
                project = projectRule.project,
                iamClient = iamMock,
                defaultPolicyDocument = "",
                defaultAssumeRolePolicyDocument = ""
            )
            val rolePanel = roleDialog.getViewForTesting()
            rolePanel.roleName.text = TEST_ROLE_NAME
            rolePanel.policyDocument.text = TEST_POLICY
            rolePanel.assumeRolePolicyDocument.text = TEST_ASSUME_ROLE

            roleDialog.createIamRoleForTesting()

            val iamRole = roleDialog.iamRole
            assertThat(iamRole?.arn()).isEqualTo(TEST_ROLE_ARN)
            assertThat(iamRole?.roleName()).isEqualTo(TEST_ROLE_NAME)
        }

        assertThat(createRoleCaptor.firstValue.roleName()).isEqualTo(TEST_ROLE_NAME)
        assertThat(createRoleCaptor.firstValue.assumeRolePolicyDocument()).isEqualTo(TEST_ASSUME_ROLE)
        assertThat(putRoleCaptor.firstValue.roleName()).isEqualTo(TEST_ROLE_NAME)
        assertThat(putRoleCaptor.firstValue.policyDocument()).isEqualTo(TEST_POLICY)
    }

    @Test
    fun roleIsDeletedIfCreationFails() {
        val deleteRolCaptor = argumentCaptor<DeleteRoleRequest>()

        iamMock.stub {
            on { createRole(any<CreateRoleRequest>()) } doReturn CreateRoleResponse.builder().role { role ->
                role.arn(TEST_ROLE_ARN)
                role.roleName(TEST_ROLE_NAME)
            }.build()

            on { putRolePolicy(any<PutRolePolicyRequest>()) } doThrow MalformedPolicyDocumentException.builder().build()

            on { deleteRole(deleteRolCaptor.capture()) } doReturn DeleteRoleResponse.builder().build()
        }

        runInEdtAndWait {
            val roleDialog = CreateIamRoleDialog(
                project = projectRule.project,
                iamClient = iamMock,
                defaultPolicyDocument = "",
                defaultAssumeRolePolicyDocument = ""
            )
            val rolePanel = roleDialog.getViewForTesting()
            rolePanel.roleName.text = TEST_ROLE_NAME
            rolePanel.policyDocument.text = TEST_POLICY
            rolePanel.assumeRolePolicyDocument.text = TEST_ASSUME_ROLE

            assertThatThrownBy { roleDialog.createIamRoleForTesting() }

            assertThat(roleDialog.iamRole).isNull()
        }

        assertThat(deleteRolCaptor.firstValue.roleName()).isEqualTo(TEST_ROLE_NAME)
    }

    private companion object {
        const val TEST_ROLE_NAME = "TetRole"
        const val TEST_ROLE_ARN = "arn:aws:iam::123456789012:role/TetRole"
        const val TEST_POLICY = "{\"hello\": \"world\"}"
        const val TEST_ASSUME_ROLE = "{\"foo\": \"bar\"}"
    }
}


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

package software.aws.toolkits.jetbrains.services.iam

import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import software.amazon.awssdk.services.iam.IamClient
import software.amazon.awssdk.services.iam.model.AttachRolePolicyRequest
import software.amazon.awssdk.services.iam.model.AttachRolePolicyResponse
import software.amazon.awssdk.services.iam.model.CreateRoleRequest
import software.amazon.awssdk.services.iam.model.CreateRoleResponse
import software.amazon.awssdk.services.iam.model.DeleteRoleRequest
import software.amazon.awssdk.services.iam.model.DeleteRoleResponse
import software.aws.toolkits.core.utils.RuleUtils
import software.aws.toolkits.jetbrains.core.MockClientManagerRule

class CreateServiceRoleDialogTest {
    private lateinit var client: IamClient
    private lateinit var dialog: CreateIamServiceRoleDialog
    private val name = RuleUtils.randomName()
    private val serviceUri = RuleUtils.randomName()
    private val managedPolicy = RuleUtils.randomName()

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val mockClientManagerRule = MockClientManagerRule()

    @Before
    fun setup() {
        client = mockClientManagerRule.create()
        runInEdtAndWait {
            dialog = CreateIamServiceRoleDialog(projectRule.project, client, serviceUri, managedPolicy, name)
        }
    }

    @Test
    fun `Role is created`() {
        client.stub {
            on { createRole(any<CreateRoleRequest>()) } doAnswer { CreateRoleResponse.builder().role { it.roleName(name).arn("arn") }.build() }
            on { attachRolePolicy(any<AttachRolePolicyRequest>()) } doAnswer { AttachRolePolicyResponse.builder().build() }
        }
        dialog.createIamRole()
        verify(client).createRole(CreateRoleRequest.builder().roleName(name).assumeRolePolicyDocument(assumeRolePolicy(serviceUri)).build())
        verify(client).attachRolePolicy(AttachRolePolicyRequest.builder().roleName(name).policyArn(managedPolicyNameToArn(managedPolicy)).build())
    }

    @Test
    fun `Role is deleted when attach fails then throws`() {
        client.stub {
            on { createRole(any<CreateRoleRequest>()) } doAnswer { CreateRoleResponse.builder().role { it.roleName(name).arn("arn") }.build() }
            on { attachRolePolicy(any<AttachRolePolicyRequest>()) } doAnswer { throw RuntimeException("Attach failed!") }
            on { deleteRole(any<DeleteRoleRequest>()) } doAnswer { DeleteRoleResponse.builder().build() }
        }
        assertThatThrownBy { dialog.createIamRole() }.hasMessage("Attach failed!")
        verify(client).createRole(CreateRoleRequest.builder().roleName(name).assumeRolePolicyDocument(assumeRolePolicy(serviceUri)).build())
        verify(client).attachRolePolicy(AttachRolePolicyRequest.builder().roleName(name).policyArn(managedPolicyNameToArn(managedPolicy)).build())
        verify(client).deleteRole(DeleteRoleRequest.builder().roleName(name).build())
    }

    @Test
    fun `Role deletion fails`() {
        client.stub {
            on { createRole(any<CreateRoleRequest>()) } doAnswer { CreateRoleResponse.builder().role { it.roleName(name).arn("arn") }.build() }
            on { attachRolePolicy(any<AttachRolePolicyRequest>()) } doAnswer { throw RuntimeException("Attach failed!") }
            on { deleteRole(any<DeleteRoleRequest>()) } doAnswer { throw RuntimeException("Delete role failed!") }
        }
        assertThatThrownBy { dialog.createIamRole() }.hasMessage("Attach failed!")
        verify(client).createRole(CreateRoleRequest.builder().roleName(name).assumeRolePolicyDocument(assumeRolePolicy(serviceUri)).build())
        verify(client).attachRolePolicy(AttachRolePolicyRequest.builder().roleName(name).policyArn(managedPolicyNameToArn(managedPolicy)).build())
        verify(client).deleteRole(DeleteRoleRequest.builder().roleName(name).build())
    }
}


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

package software.aws.toolkits.jetbrains.services.iam

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class IamRoleTest {
    @Test
    fun testNameIsExtracted() {
        val arn = "arn:aws:iam::123456789012:role/HelloWorldRole"
        val role = IamRole(arn)
        assertThat(role.arn).isEqualTo(arn)
        assertThat(role.name).isEqualTo("HelloWorldRole")
    }

    @Test
    fun testArnIsUsedWhenInvalidFormat() {
        val arn = "role/HelloWorldRole"
        val role = IamRole(arn)
        assertThat(role.arn).isEqualTo(arn)
        assertThat(role.name).isNull()
    }
}


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

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

import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModuleRootManager
import org.assertj.core.api.Assertions.assertThat
import software.aws.toolkits.jetbrains.services.PathMapping

fun verifyPathMappings(module: Module, actualMappings: List<PathMapping>, expectedMappings: List<PathMapping>) {
    val basePath = ModuleRootManager.getInstance(module).contentRoots[0].path
    val updatedPaths = expectedMappings
        .map {
            PathMapping(
                it.localRoot.replace("%PROJECT_ROOT%", basePath),
                it.remoteRoot
            )
        }
    // Path mapping order matters so we do not just check content, we also check order
    assertThat(actualMappings).containsExactly(*updatedPaths.toTypedArray())
}


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

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

import com.intellij.testFramework.ProjectRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.amazon.awssdk.services.lambda.model.FunctionConfiguration
import software.amazon.awssdk.services.lambda.model.Runtime
import software.amazon.awssdk.services.lambda.model.TracingConfigResponse
import software.amazon.awssdk.services.lambda.model.TracingMode
import software.aws.toolkits.jetbrains.core.MockResourceCacheRule
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerEmptyNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.AwsExplorerErrorNode
import software.aws.toolkits.jetbrains.core.explorer.nodes.LambdaExplorerRootNode
import software.aws.toolkits.jetbrains.services.lambda.resources.LambdaResources
import java.util.concurrent.CompletableFuture

class LambdaServiceNodeTest {

    @JvmField
    @Rule
    val projectRule = ProjectRule()

    @JvmField
    @Rule
    val resourceCache = MockResourceCacheRule()

    @Test
    fun lambdaFunctionsAreListed() {
        lambdaFunctions(listOf("bcd", "abc", "zzz", "AEF"))

        val children = LambdaServiceNode(projectRule.project, LAMBDA_EXPLORER_SERVICE_NODE).children

        assertThat(children).allMatch { it is LambdaFunctionNode }
        assertThat(children.filterIsInstance<LambdaFunctionNode>().map { it.functionName() }).containsExactlyInAnyOrder("abc", "AEF", "bcd", "zzz")
    }

    @Test
    fun noFunctionsShowsEmptyList() {
        lambdaFunctions(emptyList())

        val children = LambdaServiceNode(projectRule.project, LAMBDA_EXPLORER_SERVICE_NODE).children

        assertThat(children).hasSize(1)
        assertThat(children).allMatch { it is AwsExplorerEmptyNode }
    }

    @Test
    fun exceptionLeadsToErrorNode() {
        resourceCache.addEntry(
            projectRule.project,
            LambdaResources.LIST_FUNCTIONS,
            CompletableFuture<List<FunctionConfiguration>>().also {
                it.completeExceptionally(RuntimeException("Simulated error"))
            }
        )

        val children = LambdaServiceNode(projectRule.project, LAMBDA_EXPLORER_SERVICE_NODE).children

        assertThat(children).hasSize(1)
        assertThat(children).allMatch { it is AwsExplorerErrorNode }
    }

    private fun lambdaFunctions(names: List<String>) {
        resourceCache.addEntry(
            projectRule.project,
            LambdaResources.LIST_FUNCTIONS,
            CompletableFuture.completedFuture(names.map(::functionConfiguration))
        )
    }

    private fun functionConfiguration(functionName: String) =
        FunctionConfiguration.builder()
            .functionName(functionName)
            .functionArn("arn:aws:lambda:us-west-2:0123456789:function:$functionName")
            .lastModified("A ways back")
            .handler("blah:blah")
            .runtime(Runtime.JAVA8)
            .role("SomeRoleArn")
            .environment { it.variables(emptyMap()) }
            .timeout(60)
            .memorySize(128)
            .tracingConfig(TracingConfigResponse.builder().mode(TracingMode.PASS_THROUGH).build())
            .build()

    private companion object {
        val LAMBDA_EXPLORER_SERVICE_NODE = LambdaExplorerRootNode()
    }
}


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

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

import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.testFramework.runInEdtAndWait
import com.jetbrains.python.PythonLanguage
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.mock
import software.amazon.awssdk.services.lambda.model.Runtime
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.core.lambda.validOrNull
import software.aws.toolkits.jetbrains.utils.rules.PyTestSdk
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule

class RuntimeGroupTest {

    @Rule
    @JvmField
    val projectRule = PythonCodeInsightTestFixtureRule()

    @Test
    fun canDetermineRuntimeFromAnActionEventUsingModule() {
        val sdk = PyTestSdk("3.9.0")
        projectRule.setModuleSdk(projectRule.module, sdk)

        val event: AnActionEvent = mock {
            on { getData(LangDataKeys.LANGUAGE) }.thenReturn(PythonLanguage.INSTANCE)
            on { getData(LangDataKeys.MODULE) }.thenReturn(projectRule.module)
        }

        assertThat(event.runtime()).isEqualTo(LambdaRuntime.PYTHON3_9)
    }

    @Test
    fun canDetermineRuntimeFromAnActionEventUsingProject() {
        val sdk = PyTestSdk("3.9.0")

        val project = projectRule.project

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

            val event: AnActionEvent = mock {
                on { getData(LangDataKeys.LANGUAGE) }.thenReturn(PythonLanguage.INSTANCE)
                on { getData(LangDataKeys.MODULE) }.thenReturn(null)
                on { getData(LangDataKeys.PROJECT) }.thenReturn(projectRule.project)
            }

            assertThat(event.runtime()).isEqualTo(LambdaRuntime.PYTHON3_9)
        }
    }

    @Test
    fun unknownRuntimeIsNull() {
        assertThat(Runtime.fromValue("adsfadsffads")).isEqualTo(Runtime.UNKNOWN_TO_SDK_VERSION)
        assertThat(Runtime.fromValue("adsfadsffads").validOrNull).isNull()
    }
}


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

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

import com.intellij.openapi.ui.DialogPanel
import com.intellij.openapi.ui.ValidationInfo
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.testFramework.EdtRule
import com.intellij.testFramework.RuleChain
import com.intellij.testFramework.RunsInEdt
import com.intellij.testFramework.TemporaryDirectory
import com.intellij.testFramework.runInEdtAndWait
import com.intellij.util.io.writeChild
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.yaml.psi.YAMLSequence
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import software.amazon.awssdk.services.cloudformation.CloudFormationClient
import software.amazon.awssdk.services.cloudformation.model.StackSummary
import software.amazon.awssdk.services.ecr.EcrClient
import software.amazon.awssdk.services.s3.S3Client
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
import software.aws.toolkits.jetbrains.services.cloudformation.Parameter
import software.aws.toolkits.jetbrains.services.ecr.resources.Repository
import software.aws.toolkits.jetbrains.services.lambda.sam.ValidateSamParameters
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
import software.aws.toolkits.resources.message
import java.nio.file.Files

@RunsInEdt
class DeploySamApplicationValidatorTest {
    private val projectRule = JavaCodeInsightTestFixtureRule()

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

    @Rule
    @JvmField
    val tempDir = TemporaryDirectory()

    @JvmField
    @Rule
    val mockClientManagerRule = MockClientManagerRule()

    private lateinit var sut: DeployServerlessApplicationDialog
    private lateinit var sutPanel: DialogPanel

    private val parameters = listOf<Parameter>(
        TestParameter(logicalName = "param1", type = "String", defaultValue = "value1"),
        TestParameter(logicalName = "param2", type = "String", defaultValue = "value2")
    )

    @Before
    fun wireMocksTogetherWithValidOptions() {
        mockClientManagerRule.apply {
            create<CloudFormationClient>()
            create<S3Client>()
            create<EcrClient>()
        }

        val dir = Files.createDirectory(tempDir.newPath()).toAbsolutePath()

        runInEdtAndWait {
            sut = DeployServerlessApplicationDialog(
                projectRule.project,
                VfsUtil.findFileByIoFile(dir.writeChild("path.yaml", byteArrayOf()).toFile(), true)!!,
                loadResourcesOnCreate = false
            )
            sutPanel = sut.buildPanel()
        }

        val repo = Repository("repoName", "arn", "repositoryuri")
        sut.forceUi(
            sutPanel,
            isCreateStack = false,
            hasImageFunctions = false,
            stacks = listOf(StackSummary.builder().stackName("stack123").build()),
            buckets = listOf("bucket123"),
            ecrRepos = listOf(repo),
            stackName = "stack123",
            bucket = "bucket123",
            ecrRepo = repo.repositoryName,
            autoExecute = false,
            useContainer = true
        )
        sut.populateParameters(parameters, parameters)
    }

    @Test
    fun validInputsReturnsNull() {
        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun validInputsNoRepoReturnsNull() {
        sut.forceUi(sutPanel, forceEcrRepo = true, ecrRepo = null)
        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun validInputsWithNewStackReturnsNull() {
        sut.forceUi(sutPanel, isCreateStack = true, stackName = "createStack")
        assertThat(validateAll()).isEmpty()

        sut.forceUi(sutPanel, stackName = "n")
        assertThat(validateAll()).isEmpty()

        sut.forceUi(sutPanel, stackName = "n1")
        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun validInputsWithImageReturnsNull() {
        sut.forceUi(sutPanel, hasImageFunctions = true)
        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun stackMustBeSelected() {
        sut.forceUi(sutPanel, isCreateStack = false, forceStackName = true, stackName = null)
        assertThat(validateAll()).singleElement()
            .matches { it.message.contains(message("serverless.application.deploy.validation.stack.missing")) }
    }

    @Test
    fun newStackNameMustBeSpecified() {
        sut.forceUi(sutPanel, isCreateStack = true, forceStackName = true, stackName = null)
        assertThat(validateAll()).singleElement()
            .matches { it.message.contains(message("serverless.application.deploy.validation.new.stack.name.missing")) }
    }

    @Test
    fun invalidStackName_TooLong() {
        val maxLength = ValidateSamParameters.MAX_STACK_NAME_LENGTH
        sut.forceUi(sutPanel, isCreateStack = true, stackName = "x".repeat(maxLength + 1))

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains(message("serverless.application.deploy.validation.new.stack.name.too.long", maxLength)) }
    }

    @Test
    fun invalidStackName_Duplicate() {
        sut.forceUi(
            sutPanel,
            isCreateStack = true,
            stackName = "bar",
            stacks = listOf(
                StackSummary.builder().stackName("foo").build(),
                StackSummary.builder().stackName("bar").build(),
                StackSummary.builder().stackName("baz").build()
            )
        )

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains(message("serverless.application.deploy.validation.new.stack.name.duplicate")) }
    }

    @Test
    fun invalidStackName_InvalidChars() {
        val invalid = listOf(
            "stack_1",
            "stack#1",
            "1stack",
            " stack",
            "stack!@#$%^&*()_+-="
        )
        invalid.forEach {
            sut.forceUi(sutPanel, isCreateStack = true, stackName = it)
            assertThat(validateAll())
                .singleElement()
                .matches({ it.message.contains(message("serverless.application.deploy.validation.new.stack.name.invalid")) }, "for input $it")
        }
    }

    @Test
    fun templateParameterAllTypesValid_hasValues() {
        val parameters = listOf<Parameter>(
            TestParameter(logicalName = "param1", type = "String", defaultValue = "value1"),
            TestParameter(logicalName = "param2", type = "Number", defaultValue = "1"),
            TestParameter(logicalName = "param3", type = "Number", defaultValue = "1.2"),
            TestParameter(logicalName = "param4", type = "List<Number>", defaultValue = "10,20,1.2"),
            TestParameter(logicalName = "param5", type = "CommaDelimitedList", defaultValue = "param1,param2"),
            TestParameter(logicalName = "param6", type = "AWS::EC2::AvailabilityZone::Name", defaultValue = "us-fake-1"),
            TestParameter(logicalName = "param7", type = "List<AWS::EC2::AvailabilityZone::Name>", defaultValue = "us-fake-1a,us-fake-1b"),
            TestParameter(logicalName = "param8", type = "AWS::SSM::Parameter::Value<String>", defaultValue = "something"),

        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun templateParameterAllTypesValid_noValues() {
        val parameters = listOf<Parameter>(
            TestParameter(logicalName = "param1", type = "String", defaultValue = ""),
            TestParameter(logicalName = "param4", type = "List<Number>", defaultValue = ""),
            TestParameter(logicalName = "param5", type = "CommaDelimitedList", defaultValue = ""),
            TestParameter(logicalName = "param6", type = "AWS::EC2::AvailabilityZone::Name", defaultValue = ""),
            TestParameter(logicalName = "param7", type = "List<AWS::EC2::AvailabilityZone::Name>", defaultValue = ""),
            TestParameter(logicalName = "param8", type = "AWS::SSM::Parameter::Value<String>", defaultValue = ""),
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun templateParameter_stringRegex() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "goodRegex",
                type = "String",
                defaultValue = "example@example.com",
                additionalProperties = mapOf(
                    "AllowedPattern" to "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"
                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun templateParameter_stringTooShort() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "tooShort",
                type = "String",
                defaultValue = "",
                additionalProperties = mapOf(
                    "MinLength" to "1",
                    "MaxLength" to "5"
                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains("tooShort does not meet MinLength") }
    }

    @Test
    fun templateParameter_stringTooLong() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "tooLong",
                type = "String",
                defaultValue = "aaaaaaaaaa",
                additionalProperties = mapOf(
                    "MinLength" to "1",
                    "MaxLength" to "5"
                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains("tooLong exceeds MaxLength") }
    }

    @Test
    fun templateParameter_stringFailsRegex() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "regexFail",
                type = "String",
                defaultValue = "aaaaaaaaaa",
                additionalProperties = mapOf(
                    "AllowedPattern" to "b*"

                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains("regexFail does not match AllowedPattern") }
    }

    @Test
    fun templateParameter_stringConstraintsInvalid() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "badRegex",
                type = "String",
                defaultValue = "",
                additionalProperties = mapOf(
                    "AllowedPattern" to ")]]]]]totallyValidRegex([[[["
                )
            ),
            TestParameter(
                logicalName = "badLengthConstraints",
                type = "String",
                defaultValue = "",
                additionalProperties = mapOf(
                    "MinLength" to "-42",
                    "MaxLength" to "3.14"
                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains("AllowedPattern for badRegex is not valid") }
    }

    @Test
    fun templateParameter_numberInvalid() {
        val parameters = listOf<Parameter>(
            TestParameter(logicalName = "notANumber", type = "Number", defaultValue = "f"),
            TestParameter(logicalName = "notANumber2", type = "Number", defaultValue = "")
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains("not a number") }
    }

    @Test
    fun templateParameter_numberTooSmall() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "tooSmall",
                type = "Number",
                defaultValue = "0",
                additionalProperties = mapOf(
                    "MinValue" to "0.1",
                    "MaxValue" to "5"
                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains("tooSmall is smaller than MinValue") }
    }

    @Test
    fun templateParameter_numberTooBig() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "tooBig",
                type = "Number",
                defaultValue = "${Float.MAX_VALUE}",
                additionalProperties = mapOf(
                    "MinValue" to "0.1",
                    "MaxValue" to "5"
                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains("tooBig is larger than MaxValue") }
    }

    @Test
    fun templateParameter_numberConstraintsInvalid() {
        val parameters = listOf<Parameter>(
            TestParameter(
                logicalName = "badValueConstraints",
                type = "Number",
                defaultValue = "0",
                additionalProperties = mapOf(
                    "MinValue" to "--3",
                    "MaxValue" to "++3"
                )
            )
        )
        sut.populateParameters(parameters, parameters)

        assertThat(validateAll()).isEmpty()
    }

    @Test
    fun s3BucketMustBeSpecified() {
        sut.forceUi(sutPanel, forceBucket = true, bucket = null)
        assertThat(validateAll()).singleElement()
            .matches { it.message.contains(message("serverless.application.deploy.validation.s3.bucket.empty")) }
    }

    @Test
    fun ecrRepoMustBeSpecifiedWithImages() {
        sut.forceUi(sutPanel, hasImageFunctions = true, forceEcrRepo = true, ecrRepo = null)

        assertThat(validateAll()).singleElement()
            .matches { it.message.contains(message("serverless.application.deploy.validation.ecr.repo.empty")) }
    }

    private fun validateAll(): List<ValidationInfo> =
        sutPanel.validateCallbacks.mapNotNull { it.invoke() }

    private class TestParameter(
        override val logicalName: String,
        private val type: String,
        private val defaultValue: String?,
        private val additionalProperties: Map<String, String> = emptyMap()
    ) : Parameter {
        override fun getScalarProperty(key: String): String = getOptionalScalarProperty(key)!!

        override fun getOptionalScalarProperty(key: String): String? {
            if (key == "Type") {
                return type
            }
            return additionalProperties.get(key)
        }

        override fun setScalarProperty(key: String, value: String) {
            throw NotImplementedError()
        }

        override fun getSequenceProperty(key: String): YAMLSequence {
            throw NotImplementedError()
        }

        override fun getOptionalSequenceProperty(key: String): YAMLSequence? {
            throw NotImplementedError()
        }

        override fun defaultValue(): String? = defaultValue

        override fun description(): String? = null

        override fun constraintDescription(): String? = null
    }
}


// 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.RunManager
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.util.PathMappingSettings.PathMapping
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.region.AwsRegion
import software.aws.toolkits.jetbrains.core.executables.ExecutableManager
import software.aws.toolkits.jetbrains.core.executables.setExecutablePath
import software.aws.toolkits.jetbrains.core.region.getDefaultRegion
import software.aws.toolkits.jetbrains.services.lambda.execution.LambdaRunConfigurationType
import software.aws.toolkits.jetbrains.services.lambda.sam.SamExecutable
import software.aws.toolkits.jetbrains.services.lambda.sam.SamOptions
import software.aws.toolkits.jetbrains.services.lambda.sam.SamVersionCache
import software.aws.toolkits.jetbrains.services.lambda.validation.LambdaHandlerValidator
import java.nio.file.Paths
import java.security.InvalidParameterException

private const val TEST_EVALUATE_BLOCKING_TIMEOUT_MS = 2000

fun createTemplateRunConfiguration(
    project: Project,
    input: String? = "inputText",
    templateFile: String? = null,
    isImage: Boolean = false,
    runtime: LambdaRuntime? = null,
    architecture: LambdaArchitecture? = null,
    pathMappings: List<PathMapping> = listOf(),
    logicalId: String? = null,
    inputIsFile: Boolean = false,
    credentialsProviderId: String? = null,
    region: AwsRegion? = getDefaultRegion(),
    samOptions: SamOptions = SamOptions()
): LocalLambdaRunConfiguration {
    val runConfiguration = samRunConfiguration(project)
    runConfiguration.useTemplate(templateFile, logicalId)
    runConfiguration.isImage = isImage
    runConfiguration.runtime(runtime)
    runConfiguration.architecture(architecture)
    runConfiguration.pathMappings = pathMappings

    createBaseRunConfiguration(
        runConfiguration,
        region,
        credentialsProviderId,
        inputIsFile,
        input,
        samOptions
    )
    return runConfiguration
}

fun createHandlerBasedRunConfiguration(
    project: Project,
    runtime: Runtime? = Runtime.JAVA8,
    handler: String? = "com.example.LambdaHandler::handleRequest",
    input: String? = "inputText",
    inputIsFile: Boolean = false,
    credentialsProviderId: String? = null,
    region: AwsRegion? = getDefaultRegion(),
    environmentVariables: MutableMap<String, String> = mutableMapOf(),
    samOptions: SamOptions = SamOptions()
): LocalLambdaRunConfiguration {
    val runConfiguration = samRunConfiguration(project)
    runConfiguration.useHandler(runtime, handler)

    runConfiguration.environmentVariables(environmentVariables)

    createBaseRunConfiguration(
        runConfiguration,
        region,
        credentialsProviderId,
        inputIsFile,
        input,
        samOptions
    )

    return runConfiguration
}

private fun createBaseRunConfiguration(
    runConfiguration: LocalLambdaRunConfiguration,
    region: AwsRegion?,
    credentialsProviderId: String?,
    inputIsFile: Boolean,
    input: String?,
    samOptions: SamOptions
) {
    runConfiguration.regionId(region?.id)
    runConfiguration.credentialProviderId(credentialsProviderId)

    if (inputIsFile) {
        runConfiguration.useInputFile(input)
    } else {
        runConfiguration.useInputText(input)
    }

    runConfiguration.buildInContainer = samOptions.buildInContainer
    runConfiguration.skipPullImage = samOptions.skipImagePull
    runConfiguration.dockerNetwork = samOptions.dockerNetwork
}

fun samRunConfiguration(project: Project): LocalLambdaRunConfiguration {
    val runManager = RunManager.getInstance(project)
    val factory = LambdaRunConfigurationType.getInstance().configurationFactories.first { it is LocalLambdaRunConfigurationFactory }
    val runConfigurationAndSettings = runManager.createConfiguration("Test", factory)
    val runConfiguration = runConfigurationAndSettings.configuration as LocalLambdaRunConfiguration
    runManager.addConfiguration(runConfigurationAndSettings)
    return runConfiguration
}

fun preWarmSamVersionCache(path: String?, timeoutMs: Int = TEST_EVALUATE_BLOCKING_TIMEOUT_MS) {
    path ?: throw InvalidParameterException("Test SAM CLI executable path is not set")
    SamVersionCache.evaluateBlocking(path, timeoutMs)
    ExecutableManager.getInstance().setExecutablePath<SamExecutable>(Paths.get(path))
}

fun preWarmLambdaHandlerValidation(project: Project, runtime: Runtime, handler: String, timeoutMs: Int = TEST_EVALUATE_BLOCKING_TIMEOUT_MS) {
    val handlerValidator = project.service<LambdaHandlerValidator>()
    handlerValidator.evaluateBlocking(LambdaHandlerValidator.LambdaEntry(project, runtime, handler), timeoutMs)
}


// 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.Location
import com.intellij.execution.PsiLocation
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.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiElement
import com.intellij.testFramework.MapDataContext
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.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.services.lambda.sam.findByLocation
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.addModule
import software.aws.toolkits.jetbrains.utils.rules.addTestClass
import software.aws.toolkits.jetbrains.utils.rules.openClass
import kotlin.test.assertNotNull

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

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

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

        val lambdaMethod = psiClass.findMethodsByName("handleRequest", false).first()
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(lambdaMethod)
            assertThat(runConfiguration).isNotNull
            val configuration = runConfiguration?.configuration as LocalLambdaRunConfiguration
            assertThat(configuration.isUsingTemplate()).isFalse()
            assertThat(configuration.runtime()).isEqualTo(LambdaRuntime.JAVA8_AL2)
            assertThat(configuration.handler()).isEqualTo("com.example.LambdaHandler::handleRequest")
            assertThat(configuration.name).isEqualTo("[Local] LambdaHandler.handleRequest")
        }
    }

    @Test
    fun `does not create run configuration for test class`() {
        val newModule = projectRule.fixture.addModule("newModule")
        val psiClass = projectRule.fixture.addTestClass(
            newModule,
            """
            package com.example;

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

        val lambdaMethod = psiClass.findMethodsByName("handleRequest", false).first()
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(lambdaMethod)
            assertThat(runConfiguration).isNull()
        }
    }

    @Test
    fun validRunConfigurationIsCreatedFromTemplate() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText(
                "template.yaml",
                """
Resources:
    MyFunction:
        Type: AWS::Serverless::Function
        Properties:
            Handler: helloworld.App::handleRequest
            Runtime: java8
                """.trimIndent()
            ) as YAMLFile
            val psiElement = psiFile.findByLocation("Resources.MyFunction")?.key ?: throw RuntimeException("Can't find function")
            val runConfiguration = createRunConfiguration(psiElement)
            assertThat(runConfiguration).isNotNull
            val configuration = runConfiguration?.configuration as LocalLambdaRunConfiguration
            assertThat(configuration.isUsingTemplate()).isTrue()
            assertThat(configuration.templateFile()).isEqualTo(psiFile.containingFile.virtualFile.path)
            assertThat(configuration.logicalId()).isEqualTo("MyFunction")
            assertThat(configuration.name).isEqualTo("[Local] MyFunction")
        }
    }

    @Test
    fun canRoundTripTemplateBasedConfiguration() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText(
                "template.yaml",
                """
Resources:
    MyFunction:
        Type: AWS::Serverless::Function
        Properties:
            Handler: helloworld.App::handleRequest
            Runtime: java8
                """.trimIndent()
            ) as YAMLFile
            val psiElement = psiFile.findByLocation("Resources.MyFunction")?.key ?: throw RuntimeException("Can't find function")
            val runConfiguration = createRunConfiguration(psiElement)

            val sut = RunConfigurationProducer.getInstance(LocalLambdaRunConfigurationProducer::class.java)

            assertThat(
                sut.isConfigurationFromContext(
                    runConfiguration?.configuration as LocalLambdaRunConfiguration,
                    createContext(psiElement, MapDataContext())
                )
            ).isTrue()
        }
    }

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

            public class LambdaHandler {
                public void handleRequest() {
                }
            }
            """
        )

        val lambdaMethod = psiClass.findMethodsByName("handleRequest", false).first()
        runInEdtAndWait {
            val runConfiguration = createRunConfiguration(lambdaMethod)
            assertThat(runConfiguration).isNull()
        }
    }

    @Test
    fun `Valid image run configuration is created from template`() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText(
                "template.yaml",
                """
Resources:
    MyFunction:
        Type: AWS::Serverless::Function
        Properties:
            PackageType: Image
                """.trimIndent()
            ) as YAMLFile
            val psiElement = psiFile.findByLocation("Resources.MyFunction")?.key ?: throw RuntimeException("Can't find function")
            val runConfiguration = createRunConfiguration(psiElement)
            assertNotNull(runConfiguration)
            val configuration = runConfiguration.configuration as LocalLambdaRunConfiguration
            assertThat(configuration.templateFile()).isEqualTo(psiFile.containingFile.virtualFile.path)
            assertThat(configuration.logicalId()).isEqualTo("MyFunction")
            assertThat(configuration.name).isEqualTo("[Local] MyFunction")
        }
    }

    @Test
    fun `Can round trip image template based configuration`() {
        runInEdtAndWait {
            val psiFile = projectRule.fixture.configureByText(
                "template.yaml",
                """
Resources:
    MyFunction:
        Type: AWS::Serverless::Function
        Properties:
            PackageType: Image
                """.trimIndent()
            ) as YAMLFile
            val psiElement = psiFile.findByLocation("Resources.MyFunction")?.key ?: throw RuntimeException("Can't find function")
            val runConfiguration = createRunConfiguration(psiElement)

            val sut = RunConfigurationProducer.getInstance(LocalLambdaRunConfigurationProducer::class.java)

            assertThat(
                sut.isConfigurationFromContext(
                    runConfiguration?.configuration as LocalLambdaRunConfiguration,
                    createContext(psiElement, MapDataContext())
                )
            ).isTrue()
        }
    }

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

    private fun createContext(psiClass: PsiElement, dataContext: MapDataContext): ConfigurationContext {
        dataContext.put(CommonDataKeys.PROJECT, projectRule.project)
        if (LangDataKeys.MODULE.getData(dataContext) == null) {
            dataContext.put(LangDataKeys.MODULE, ModuleUtilCore.findModuleForPsiElement(psiClass))
        }
        dataContext.put(Location.DATA_KEY, PsiLocation.fromPsiElement(psiClass))
        return ConfigurationContext.getFromContext(dataContext)
    }
}