// 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.psi.PsiIdentifier
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.jetbrains.core.credentials.MockAwsConnectionManager
import software.aws.toolkits.jetbrains.services.lambda.upload.LambdaLineMarker
import software.aws.toolkits.jetbrains.settings.LambdaSettings
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.openClass
import software.aws.toolkits.jetbrains.utils.rules.openFile

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

    @Before
    fun setUp() {
        LambdaSettings.getInstance(projectRule.project).showAllHandlerGutterIcons = true
        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;
            }
            """
        )
    }

    @After
    fun tearDown() {
        MockAwsConnectionManager.getInstance(projectRule.project).reset()
    }

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

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

            public class UsefulUtils {

                private UsefulUtils() { }

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

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "upperCase")
        }
    }

    @Test
    fun singleArgumentStaticMethodsAreNotMarkedWhenDisablingLambdaSetting() {
        val fixture = projectRule.fixture
        LambdaSettings.getInstance(projectRule.project).showAllHandlerGutterIcons = false

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

            public class UsefulUtils {

                private UsefulUtils() { }

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

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

    @Test
    fun singleArgumentStaticMethodsMarkedWhenDisablingLambdaSettingButDefinedInTemplate() {
        val fixture = projectRule.fixture
        LambdaSettings.getInstance(projectRule.project).showAllHandlerGutterIcons = false

        fixture.openFile(
            "template.yaml",
            """
Resources:
  UpperCase:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: foo
      Handler: com.example.UsefulUtils::upperCase
      Runtime: java8
"""
        )

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

            public class UsefulUtils {

                private UsefulUtils() { }

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

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "upperCase")
        }
    }

    @Test
    fun singleArgumentStaticMethodsMarkedWhenDisablingLambdaSettingButDefinedInTemplateGlobals() {
        val fixture = projectRule.fixture
        LambdaSettings.getInstance(projectRule.project).showAllHandlerGutterIcons = false

        fixture.openFile(
            "template.yaml",
            """
Globals:
  Function:
    Handler: com.example.UsefulUtils::upperCase
    Runtime: java8
Resources:
  UpperCase:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: foo
"""
        )

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

            public class UsefulUtils {

                private UsefulUtils() { }

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

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "upperCase")
        }
    }

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

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

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

            public class UsefulUtils {

                private UsefulUtils() { }

                public static String upperCase(String input, Context context) {
                    return input.toUpperCase();
                }
            }
            """
        )

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "upperCase")
        }
    }

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

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

             public class UsefulUtils {

                 public UsefulUtils() { }

                 public String upperCase(String input) {
                     return input.toUpperCase();
                 }
             }
             """
        )

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "upperCase")
        }
    }

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

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

             public class UsefulUtils {

                 public String upperCase(String input) {
                     return input.toUpperCase();
                 }
             }
             """
        )

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "upperCase")
        }
    }

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

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

             public class UsefulUtils {

                 private String upperCase(String input) {
                     return input.toUpperCase();
                 }
             }
             """
        )

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

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

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

             public class UsefulUtils {

                 public UsefulUtils() {}

                 public UsefulUtils(String abc) {
                     System.out.println(abc);
                 }

                 private String upperCase(String input) {
                     return input.toUpperCase();
                 }
             }
             """
        )

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

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

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

             public class UsefulUtils {

                 public static void main(String[] args) {
                 }
             }
             """
        )

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

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

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

            public class UsefulUtils {

                private static String upperCase(String input) {
                    return input.toUpperCase();
                }
            }
            """
        )

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

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

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

             public class UsefulUtils {

                 private UsefulUtils() { }

                 public static String upperCase(String input, String secondArgument) {
                     return input.toUpperCase();
                 }
             }
             """
        )

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

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

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

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

            public abstract class AbstractHandler implements RequestHandler<String, String> { }
            """
        )

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

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

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

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "ConcreteHandler")
        }
    }

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

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

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

            public abstract class AbstractHandler implements RequestHandler<String, String> { }
            """
        )

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

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

            public abstract class ConcreteHandler extends AbstractHandler {
                public String handleRequest(String request, Context context) {
                    return request.toUpperCase();
                }
            }
            """
        )

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

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

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

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

            public abstract class AbstractHandler implements RequestStreamHandler { }
            """
        )

        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
                public void handleRequest(InputStream input, OutputStream output, Context context) { }
            }
            """
        )

        findAndAssertMarks(fixture) { marks ->
            assertLineMarkerIs(marks, "ConcreteHandler")
        }
    }

    @Test
    fun noCredentialsLeadsToNoMarkerIfNoOtherCriteriaPasses() {
        LambdaSettings.getInstance(projectRule.project).showAllHandlerGutterIcons = false
        MockAwsConnectionManager.getInstance(projectRule.project).nullifyCredentialProviderAndWait()

        val fixture = projectRule.fixture

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

             public class UsefulUtils {

                 public String upperCase(String input) {
                     return input.toUpperCase();
                 }
             }
             """
        )

        findAndAssertMarks(fixture) { marks ->
            assertThat(marks).isEmpty()
        }
    }

    private fun findAndAssertMarks(fixture: CodeInsightTestFixture, assertion: (List<LambdaLineMarker.LambdaGutterIcon>) -> Unit) {
        runInEdtAndWait {
            val marks = fixture.findAllGutters().filterIsInstance<LambdaLineMarker.LambdaGutterIcon>()
            assertion(marks)
        }
    }

    private fun assertLineMarkerIs(marks: List<LambdaLineMarker.LambdaGutterIcon>, elementName: String) {
        assertThat(marks).hasSize(1)
        assertThat(marks.first().lineMarkerInfo.element)
            .isInstanceOf(PsiIdentifier::class.java)
            .extracting { it?.text }
            .isEqualTo(elementName)
    }
}


// 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.application.WriteAction
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.IdeaTestUtil
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule

@RunWith(Parameterized::class)
class JavaRuntimeGroupTest(
    @Suppress("unused") private val name: String,
    private val sdk: () -> Sdk,
    private val expectedRuntime: LambdaRuntime?
) {
    @Rule
    @JvmField
    val projectRule = JavaCodeInsightTestFixtureRule()

    private val sut = JavaRuntimeGroup()

    @Test
    fun sdkResultsInExpectedRuntime() {
        val module = projectRule.module

        WriteAction.computeAndWait<Unit, Throwable> {
            val sdk = sdk()
            ProjectJdkTable.getInstance().addJdk(sdk, projectRule.fixture.testRootDisposable)
            ModuleRootModificationUtil.setModuleSdk(module, sdk)
        }

        assertThat(sut.determineRuntime(module)).isEqualTo(expectedRuntime)
    }

    companion object {
        @Parameters(name = "{0}")
        @JvmStatic
        fun parameters(): Collection<Array<*>> = listOf(
            arrayOf<Any?>("Java 7", { IdeaTestUtil.getMockJdk17() }, LambdaRuntime.JAVA8_AL2),
            arrayOf<Any?>("Java 8", { IdeaTestUtil.getMockJdk18() }, LambdaRuntime.JAVA8_AL2),
            arrayOf<Any?>("Java 9", { IdeaTestUtil.getMockJdk9() }, LambdaRuntime.JAVA11),
            arrayOf<Any?>("Java 10", { IdeaTestUtil.getMockJdk(LanguageLevel.JDK_10.toJavaVersion()) }, LambdaRuntime.JAVA11),
            arrayOf<Any?>("Java 11", { IdeaTestUtil.getMockJdk(LanguageLevel.JDK_11.toJavaVersion()) }, LambdaRuntime.JAVA11),
            arrayOf<Any?>("Java 12", { IdeaTestUtil.getMockJdk(LanguageLevel.JDK_12.toJavaVersion()) }, LambdaRuntime.JAVA17),
            arrayOf<Any?>("Java 17", { IdeaTestUtil.getMockJdk(LanguageLevel.JDK_17.toJavaVersion()) }, LambdaRuntime.JAVA17),
            arrayOf<Any?>("Java 18", { IdeaTestUtil.getMockJdk(LanguageLevel.JDK_18.toJavaVersion()) }, null)
        )
    }
}


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

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

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.PythonCodeInsightTestFixtureRule

class PythonHandlerCompletionProviderTest {

    @Rule
    @JvmField
    val projectRule = PythonCodeInsightTestFixtureRule()

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

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

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

    @Test
    fun completionIsNotSupportedPython310() {
        val provider = HandlerCompletionProvider(projectRule.project, LambdaRuntime.PYTHON3_10)
        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.python

import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.testFramework.runInEdtAndGet
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.psi.PyFunction
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.services.PathMapping
import software.aws.toolkits.jetbrains.services.lambda.LambdaBuilder
import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommon
import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommonTestUtils.addSamTemplate
import software.aws.toolkits.jetbrains.services.lambda.verifyPathMappings
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule
import java.nio.file.Paths

class PythonLambdaBuilderTest {
    @Rule
    @JvmField
    val projectRule = PythonCodeInsightTestFixtureRule()

    private val sut = PythonLambdaBuilder()

    @Test
    fun handlerBaseDirIsCorrect() {
        val handler = addPythonHandler("hello_world")
        addRequirementsFile()

        val baseDir = sut.handlerBaseDirectory(projectRule.module, handler)
        val root = Paths.get(projectRule.fixture.tempDirPath)
        assertThat(baseDir.toAbsolutePath()).isEqualTo(root)
    }

    @Test
    fun handlerBaseDirIsCorrectInSubDir() {
        val handler = addPythonHandler("hello-world/foo-bar")
        addRequirementsFile("hello-world")

        val baseDir = sut.handlerBaseDirectory(projectRule.module, handler)
        val root = Paths.get(projectRule.fixture.tempDirPath)
        assertThat(baseDir).isEqualTo(root.resolve("hello-world"))
    }

    @Test
    fun missingRequirementsThrowsForHandlerBaseDir() {
        val handler = addPythonHandler("hello-world/foo-bar")

        assertThatThrownBy {
            sut.handlerBaseDirectory(projectRule.module, handler)
        }.hasMessageStartingWith("Cannot locate requirements.txt")
    }

    @Test
    fun buildDirectoryIsCorrect() {
        val baseDir = sut.getBuildDirectory(projectRule.module)
        val root = ModuleRootManager.getInstance(projectRule.module).contentRoots.first().path
        assertThat(baseDir).isEqualTo(Paths.get(root, SamCommon.SAM_BUILD_DIR, "build"))
    }

    @Test
    fun defaultPathMappingsAreCorrect() {
        val handler = addPythonHandler("hello-world")
        addRequirementsFile("hello-world")

        val codeUri = sut.handlerBaseDirectory(projectRule.module, handler)
        val buildDir = sut.getBuildDirectory(projectRule.module)

        val logicalId = "SomeFunction"
        val template = projectRule.fixture.addSamTemplate(logicalId, codeUri.toString(), "hello_world/app.handle", LambdaRuntime.PYTHON3_8)
        val templatePath = Paths.get(template.virtualFile.path)

        val actualMappings = sut.defaultPathMappings(templatePath, logicalId, buildDir)
        verifyPathMappings(
            projectRule.module,
            actualMappings,
            listOf(
                PathMapping(codeUri.toString(), LambdaBuilder.TASK_PATH),
                PathMapping(buildDir.resolve(logicalId).toString(), LambdaBuilder.TASK_PATH)
            )
        )
    }

    private fun addPythonHandler(subPath: String): PyFunction {
        val psiFile = projectRule.fixture.addFileToProject(
            "$subPath/app.py",
            """
            def handle(event, context):
                return "HelloWorld"
            """.trimIndent()
        ) as PyFile

        return runInEdtAndGet {
            psiFile.findTopLevelFunction("handle")!!
        }
    }

    private fun addRequirementsFile(subPath: String = ".", content: String = "") {
        projectRule.fixture.addFileToProject("$subPath/requirements.txt", content)
    }
}


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

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

import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.NavigatablePsiElement
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.runInEdtAndWait
import com.jetbrains.python.psi.PyFunction
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.amazon.awssdk.services.lambda.model.Runtime
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.PythonCodeInsightTestFixtureRule
import software.aws.toolkits.jetbrains.utils.rules.addFileToModule

class PythonLambdaHandlerResolverTest {
    @Rule
    @JvmField
    val projectRule = PythonCodeInsightTestFixtureRule()

    @Test
    fun `finds requirementsTxt inside source root`() {
        createHandler("hello_world/app.py")
        createRequirementsTxt("hello_world")

        assertHandler("hello_world/app.handle", true)
    }

    @Test
    fun findWorksByPath() {
        createHandler("hello_world/app.py")
        createRequirementsTxt(".")

        assertHandler("hello_world/app.handle", true)
    }

    @Test
    fun findWorksByPathWithInit() {
        createHandler("hello_world/app.py")
        createInitPy("hello_world")
        createRequirementsTxt(".")

        assertHandler("hello_world/app.handle", true)
    }

    @Test
    fun findDoesntWorkIfNoRequirementsFound() {
        createHandler("hello_world/app.py")

        assertHandler("hello_world.app.handle", false)
    }

    @Test
    fun findWorksByModuleWithInit() {
        createHandler("hello_world/app.py")
        createInitPy("hello_world")
        createRequirementsTxt(".")

        assertHandler("hello_world.app.handle", true)
    }

    @Test
    fun findWorksInSubFolderByPath() {
        createHandler("hello_world/foo_bar/app.py")
        createRequirementsTxt("hello_world")

        assertHandler("foo_bar/app.handle", true)
    }

    @Test
    fun findWorksInSubFolderByPathWithInit() {
        createHandler("hello_world/foo_bar/app.py")
        createInitPy("hello_world/foo_bar")
        createRequirementsTxt("hello_world")

        assertHandler("foo_bar.app.handle", true)
    }

    @Test
    fun findDoesntWorkWithPathAndModuleWithoutInit() {
        createHandler("hello_world/foo_bar/app.py")
        createRequirementsTxt(".")

        assertHandler("hello_world/foo_bar.app.handle", false)
    }

    @Test
    fun findWorksWithPathAndModuleWithInit() {
        createHandler("hello_world/foo_bar/app.py")
        createInitPy("hello_world/foo_bar")
        createRequirementsTxt(".")

        assertHandler("hello_world/foo_bar.app.handle", true)
    }

    @Test
    fun findWorksWithSubmodulesWithInit() {
        createHandler("hello_world/foo_bar/app.py")
        createInitPy("hello_world")
        createInitPy("hello_world/foo_bar")
        createRequirementsTxt(".")

        assertHandler("hello_world.foo_bar.app.handle", true)
    }

    @Test
    fun findDoesntWorkWithSubmodulesWithMissingInit() {
        createHandler("hello_world/foo_bar/app.py")
        createInitPy("hello_world/foo_bar")
        createRequirementsTxt(".")

        assertHandler("hello_world.foo_bar.app.handle", false)
    }

    @Test
    fun findWorksIfHandlerRootIsASourceDirectory() {
        val virtualFile = createHandler("src/hello_world/foo_bar/app.py")
        createInitPy("src/hello_world/foo_bar")
        createInitPy("src/hello_world")
        createRequirementsTxt("src")

        markAsSourceRoot(virtualFile.parent.parent) // hello_world

        assertHandler("hello_world.foo_bar.app.handle", true)
    }

    @Test
    fun findDoesntWorkIfParentFolderDoesntExist() {
        createHandler("src/hello_world/foo_bar/app.py")
        createInitPy("src/hello_world/foo_bar")
        createRequirementsTxt(".")

        assertHandler("doesnt_exist/foo_bar.app.handle", false)
    }

    @Test
    fun invalidHandlerReturnsNothing() {
        createHandler("hello_world/app.py")
        createRequirementsTxt(".")

        assertHandler("doesnt_exist", false)
    }

    @Test
    fun findWorksByTopLevelModule() {
        createHandler("app.py")
        createRequirementsTxt(".")

        assertHandler("app.handle", true)
    }

    @Test
    fun foundHandlersNotPythonFileInvalid() {
        createHandler("hello_world/foo_bar/app.java")
        createRequirementsTxt(".")

        assertHandler("hello_world/foo_bar/app.handle", false)
    }

    @Test
    fun foundHandlersAreDeterminedValid() {
        createHandler("hello_world/foo_bar/app.py")
        createRequirementsTxt(".")

        assertHandlerDetermineHandlers("hello_world/foo_bar/app.handle", false)
    }

    @Test
    fun pyTestHandlersAreDeterminedInvalid() {
        createPyTestHandler("hello_world/foo_bar/app.py")
        createRequirementsTxt(".")

        assertHandlerDetermineHandlers("hello_world/foo_bar/app.test_handle", true)
    }

    @Test
    fun testDirectoryHandlerHandlersAreDeterminedInvalid() {
        val vfs = createHandler("hello_world/foo_bar/app.py")
        createRequirementsTxt(".")
        PsiTestUtil.addSourceRoot(projectRule.module, vfs.parent, true)

        assertHandlerDetermineHandlers("hello_world/foo_bar/app.handle", true)
    }

    @Test
    fun foundHandlersOneArgumentIsDeterminedInvalid() {
        createInvalidHandler("hello_world/foo_bar/app.py", 1)
        createRequirementsTxt(".")

        assertHandlerDetermineHandlers("hello_world/foo_bar/app.handle", true)
    }

    @Test
    fun foundHandlersThreeArgumentsIsDeterminedInvalid() {
        createInvalidHandler("hello_world/foo_bar/app.py", 3)
        createRequirementsTxt(".")

        assertHandlerDetermineHandlers("hello_world/foo_bar/app.handle", true)
    }

    private fun createRequirementsTxt(folder: String): VirtualFile = projectRule.fixture.addFileToProject(
        folder + "/requirements.txt",
        ""
    ).virtualFile

    private fun createHandler(path: String): VirtualFile = projectRule.fixture.addFileToProject(
        path,
        """
        def handle(event, context):
            return "HelloWorld"
        """.trimIndent()
    ).virtualFile

    private fun createPyTestHandler(path: String): VirtualFile = projectRule.fixture.addFileToProject(
        path,
        """
        def test_handle(event, context):
            return "HelloWorld"
        """.trimIndent()
    ).virtualFile

    private fun createInvalidHandler(path: String, numberArguments: Int): VirtualFile = projectRule.fixture.addFileToProject(
        path,
        """
        def handle(${(1..numberArguments).joinToString(", ") { "a$it" } }):
            return "HelloWorld"
        """.trimIndent()
    ).virtualFile

    private fun createInitPy(path: String) {
        projectRule.fixture.addFileToModule(projectRule.module, "$path/__init__.py", "")
    }

    private fun markAsSourceRoot(virtualFile: VirtualFile) {
        ModuleRootModificationUtil.updateModel(projectRule.module) {
            it.contentEntries[0].addSourceFolder(virtualFile.parent, false)
        }
    }

    private fun assertHandler(handler: String, shouldBeFound: Boolean) {
        runInEdtAndWait {
            val elements = findHandler(handler)
            if (shouldBeFound) {
                assertThat(elements).hasSize(1)
                assertThat(elements[0]).isInstanceOf(PyFunction::class.java)
            } else {
                assertThat(elements).isEmpty()
            }
        }
    }

    private fun assertHandlerDetermineHandlers(handler: String, shouldBeFilteredOut: Boolean) {
        runInEdtAndWait {
            val elementSet = findHandler(handler)
            assertThat(elementSet).hasSize(1)
            assertThat(elementSet[0]).isInstanceOf(PyFunction::class.java)
            val pyElement = elementSet[0] as PyFunction
            val elements = getHandlerResolver().determineHandlers(pyElement.identifyingElement!!, pyElement.containingFile.virtualFile)
            if (shouldBeFilteredOut) {
                assertThat(elements).isEmpty()
            } else {
                assertThat(elements).hasSize(1)
            }
        }
    }

    private fun getHandlerResolver() = Runtime.PYTHON3_9.runtimeGroup?.let { LambdaHandlerResolver.getInstanceOrNull(it) }!!
    private fun findHandler(handler: String): Array<NavigatablePsiElement> =
        Lambda.findPsiElementsForHandler(projectRule.project, Runtime.PYTHON3_9, handler)
}


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

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

import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.utils.rules.PyTestSdk
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule

class PythonRuntimeGroupTest {
    @Rule
    @JvmField
    val projectRule = PythonCodeInsightTestFixtureRule()

    private val sut = PythonRuntimeGroup()

    @Test
    fun testRuntimeDetection37() {
        val module = projectRule.module
        projectRule.setModuleSdk(module, PyTestSdk("3.7.0"))

        assertThat(sut.determineRuntime(module)).isEqualTo(LambdaRuntime.PYTHON3_7)
    }

    @Test
    fun testRuntimeDetection38() {
        val module = projectRule.module
        projectRule.setModuleSdk(module, PyTestSdk("3.8.0"))

        assertThat(sut.determineRuntime(module)).isEqualTo(LambdaRuntime.PYTHON3_8)
    }

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

        assertThat(sut.determineRuntime(module)).isEqualTo(LambdaRuntime.PYTHON3_9)
    }

    @Test
    fun testRuntimeDetection310() {
        val module = projectRule.module
        projectRule.setModuleSdk(module, PyTestSdk("3.10.0"))

        assertThat(sut.determineRuntime(module)).isEqualTo(LambdaRuntime.PYTHON3_10)
    }
}


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

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

import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.psi.PsiFile
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import software.aws.toolkits.core.utils.exists
import software.aws.toolkits.jetbrains.core.executables.ExecutableManager
import software.aws.toolkits.jetbrains.services.lambda.sam.SamCommonTestUtils.makeATestSam
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
import java.nio.file.Files
import java.nio.file.Paths
import java.util.concurrent.TimeUnit
import kotlin.test.assertNotNull

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

    private val executableManager = ExecutableManager.getInstance()

    @Test
    fun getVersion_badPath() {
        executableManager.removeExecutable(SamExecutable())
        executableManager.setExecutablePath(SamExecutable(), Paths.get("/bad/path/that/will/not/work")).toCompletableFuture()
            .get(1, TimeUnit.SECONDS)
        val actualVersion = SamCommon.getVersionString()
        assertThat(actualVersion).isEqualTo("UNKNOWN")
    }

    @Test
    fun getVersion_Valid_exitNonZero() {
        val samPath = makeATestSam("stderr", exitCode = 100)
        executableManager.removeExecutable(SamExecutable())
        executableManager.setExecutablePath(SamExecutable(), samPath).toCompletableFuture().get(1, TimeUnit.SECONDS)
        val actualVersion = SamCommon.getVersionString()
        assertThat(actualVersion).isEqualTo("UNKNOWN")
    }

    @Test(expected = java.lang.AssertionError::class)
    fun getTemplateFromDirectory_noYaml() {
        val basePath = projectRule.project.basePath?.let { Paths.get(it) } ?: throw NullPointerException("basepath is null")
        if (!basePath.exists()) {
            Files.createDirectory(basePath)
        }
        val projectBase = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(basePath.toFile())
            ?: throw NullPointerException("project base is null ($basePath)")
        SamCommon.getTemplateFromDirectory(projectBase)
    }

    @Test
    fun getTemplateFromDirectory_singleYaml() {
        val file = yamlTemplate()
        runReadAction {
            val dir = file.containingDirectory.virtualFile
            val templateFile = SamCommon.getTemplateFromDirectory(dir)
            assertThat(templateFile).isNotNull
        }
    }

    @Test(expected = java.lang.AssertionError::class)
    fun getTemplateFromDirectory_multipleYaml() {
        val file = yamlTemplate()
        yamlTemplate(filename = "template.yml")
        yamlTemplate(filename = "theBestTemplate.yml")
        yamlTemplate(filename = "i_need_more_templates.yaml")
        runReadAction {
            val dir = file.containingDirectory.virtualFile
            SamCommon.getTemplateFromDirectory(dir)
        }
    }

    @Test
    fun getCodeUri_noUri() {
        val file = yamlTemplate(
            """
Description: "Some description"
Resources:
    MyFunction:
        Type: AWS::Serverless::Function
        Properties:
            Handler: helloworld.App::handleRequest
            Runtime: java8
            CodeUri: target/out.jar
            """.trimIndent()
        )
        runInEdtAndWait {
            projectRule.fixture.addFileToProject("target/out.jar", "")
        }
        runReadAction {
            val dir = file.containingDirectory.virtualFile
            val templateFile = SamCommon.getTemplateFromDirectory(dir)
            assertNotNull(templateFile)
            val codeUris = SamCommon.getCodeUrisFromTemplate(projectRule.project, templateFile)
            assertEquals(0, codeUris.size)
        }
    }

    @Test
    fun getCodeUri_singleUri() {
        val file = yamlTemplate(
            """
Description: "Some description"
Resources:
    HelloWorldFunction:
        Type: AWS::Serverless::Function
        Properties:
            CodeUri: hello_world/
            Handler: app.handle_request
            Runtime: java8
            """.trimIndent()
        )
        createChildren("hello_world")
        runInEdtAndWait {
            projectRule.fixture.addFileToProject("target/out.jar", "")
        }
        runReadAction {
            val dir = file.containingDirectory.virtualFile
            val templateFile = SamCommon.getTemplateFromDirectory(dir)
            assertNotNull(templateFile)
            val codeUris = SamCommon.getCodeUrisFromTemplate(projectRule.project, templateFile)
            assertEquals(1, codeUris.size)
            assertEquals("hello_world", codeUris[0].name)
        }
    }

    @Test
    fun getCodeUri_samAndNotSam() {
        val file = yamlTemplate(
            """
Description: "Some description"
Resources:
    HelloWorldFunction:
        Type: AWS::Serverless::Function
        Properties:
            CodeUri: hello_world/
            Handler: app.handle_request
            Runtime: java8
    MyDynamoTable:
        Type: AWS::DynamoDB::Table
        Properties:
            AttributeDefinitions:
                - AttributeName: "ArtistId"
                  AttributeType: "S"
                - AttributeName: "Concert"
                  AttributeType: "S"
            KeySchema:
                - AttributeName: "ArtistId"
                  KeyType: "HASH"
                - AttributeName: "Concert"
                  KeyType: "RANGE"
            ProvisionedThroughput:
                ReadCapacityUnits: 1
                WriteCapacityUnits: 1
            """.trimIndent()
        )
        createChildren("hello_world")
        runReadAction {
            val dir = file.containingDirectory.virtualFile
            val templateFile = SamCommon.getTemplateFromDirectory(dir)
            assertNotNull(templateFile)
            val codeUris = SamCommon.getCodeUrisFromTemplate(projectRule.project, templateFile)
            assertEquals(1, codeUris.size)
            assertEquals("hello_world", codeUris[0].name)
        }
    }

    @Test
    fun getCodeUri_multipleUris() {
        val file = yamlTemplate(
            """
Description: "Some description"
Resources:
    MyFunction:
        Type: AWS::Serverless::Function
        Properties:
            Handler: helloworld.App::handleRequest
            Runtime: java8
            CodeUri: target/out.jar
    HelloWorldFunction:
        Type: AWS::Serverless::Function
        Properties:
            CodeUri: hello_world/
            Handler: app.handle_request
            Runtime: java8
    AnotherHelloWorldFunction:
        Type: AWS::Serverless::Function
        Properties:
            CodeUri: hello_world_42/
            Handler: app.handle_request
            Runtime: java8
            """.trimIndent()
        )
        createChildren("hello_world")
        createChildren("hello_world_42")
        createChildren("target", "out.jar")
        runReadAction {
            val dir = file.containingDirectory.virtualFile
            val templateFile = SamCommon.getTemplateFromDirectory(dir)
            assertNotNull(templateFile)
            val codeUris = SamCommon.getCodeUrisFromTemplate(projectRule.project, templateFile)
            assertEquals(2, codeUris.size)
            assertTrue(codeUris.any { it.name == "hello_world" })
            assertTrue(codeUris.any { it.name == "hello_world_42" })
        }
    }

    private fun yamlTemplate(template: String = TEST_TEMPLATE, filename: String = "template.yaml"): PsiFile = runInEdtAndGet {
        projectRule.fixture.addFileToProject(filename, template)
    }

    private fun createChildren(directory: String, file: String = "TestFile") {
        projectRule.fixture.addFileToProject("$directory/$file", "")
    }

    private companion object {
        val TEST_TEMPLATE =
            """
Description: "Some description"
Resources:
    MyFunction:
        Type: AWS::Serverless::Function
        Properties:
            Handler: helloworld.App::handleRequest
            Runtime: java8
            CodeUri: target/out.jar
    MyDynamoTable:
        Type: AWS::DynamoDB::Table
        Properties:
            AttributeDefinitions:
                - AttributeName: "ArtistId"
                  AttributeType: "S"
                - AttributeName: "Concert"
                  AttributeType: "S"
            KeySchema:
                - AttributeName: "ArtistId"
                  KeyType: "HASH"
                - AttributeName: "Concert"
                  KeyType: "RANGE"
            ProvisionedThroughput:
                ReadCapacityUnits: 1
                WriteCapacityUnits: 1
            """.trimIndent()
    }
}


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

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

import com.intellij.openapi.util.SystemInfo
import com.intellij.psi.PsiFile
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import software.aws.toolkits.core.lambda.LambdaRuntime
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.attribute.PosixFilePermissions

object SamCommonTestUtils {
    fun getVersionAsJson(version: String): String {
        val tree = SamCommon.mapper.createObjectNode()
        tree.put(SamCommon.SAM_INFO_VERSION_KEY, version)
        return SamCommon.mapper.writeValueAsString(tree)
    }

    fun getMinVersionAsJson() = getVersionAsJson(SamExecutable.minVersion.toString())

    fun getMaxVersionAsJson() = getVersionAsJson(SamExecutable.maxVersion.toString())

    fun makeATestSam(message: String, path: String? = null, exitCode: Int = 0): Path {
        val sam = path?.let {
            Paths.get(it)
        } ?: Files.createTempFile(
            "sam",
            if (SystemInfo.isWindows) ".bat" else ".sh"
        )

        val stream = if (exitCode == 0) 1 else 2

        val contents = if (SystemInfo.isWindows) {
            """
                @echo off
                echo${if (message.isEmpty()) "." else " $message"} ${if (stream != 1) "1>&$stream" else ""}
                exit $exitCode
            """.trimIndent()
        } else {
            """
                echo '$message' >&$stream
                exit $exitCode
            """.trimIndent()
        }
        Files.write(sam, contents.toByteArray())

        if (SystemInfo.isUnix) {
            Files.setPosixFilePermissions(
                sam,
                PosixFilePermissions.fromString("r-xr-xr-x")
            )
        }

        return sam
    }

    fun CodeInsightTestFixture.addSamTemplate(
        logicalName: String = "Function",
        codeUri: String,
        handler: String,
        runtime: LambdaRuntime
    ): PsiFile = this.addFileToProject(
        "template.yaml",
        """
        Resources:
          $logicalName:
            Type: AWS::Serverless::Function
            Properties:
              CodeUri: $codeUri
              Handler: $handler
              Runtime: $runtime
              Timeout: 900
        """.trimIndent()
    )
}


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

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

import com.intellij.execution.configurations.GeneralCommandLine
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import software.aws.toolkits.core.lambda.LambdaArchitecture
import software.aws.toolkits.core.lambda.LambdaRuntime
import software.aws.toolkits.jetbrains.services.lambda.deploy.CreateCapabilities
import software.aws.toolkits.jetbrains.services.lambda.deploy.DeployServerlessApplicationSettings
import software.aws.toolkits.jetbrains.services.lambda.sam.sync.SyncServerlessApplicationSettings
import software.aws.toolkits.jetbrains.services.lambda.wizard.AppBasedImageTemplate
import software.aws.toolkits.jetbrains.services.lambda.wizard.AppBasedZipTemplate
import software.aws.toolkits.jetbrains.services.lambda.wizard.LocationBasedTemplate

class SamExecutableTest {
    @Rule
    @JvmField
    val tempFolder = TemporaryFolder()

    @Test
    fun `sam build command is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val buildDir = tempFolder.newFolder("build").toPath()
        val cmd = GeneralCommandLine("sam").samBuildCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            buildDir = buildDir,
            samOptions = SamOptions(
                buildInContainer = false
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "build",
                "--template",
                "$templatePath",
                "--build-dir",
                "$buildDir"
            ).joinToString(separator = " ")
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)

        assertThat(cmd.environment).containsEntry("Foo", "Bar")
    }

    @Test
    fun `sam build can take a logical ID`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val buildDir = tempFolder.newFolder("build").toPath()
        val cmd = GeneralCommandLine("sam").samBuildCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            logicalId = "FooResource",
            buildDir = buildDir,
            samOptions = SamOptions(
                buildInContainer = false
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "build",
                "FooResource",
                "--template",
                "$templatePath",
                "--build-dir",
                "$buildDir"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam build can skip image pull`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val buildDir = tempFolder.newFolder("build").toPath()
        val cmd = GeneralCommandLine("sam").samBuildCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            logicalId = "FooResource",
            buildDir = buildDir,
            samOptions = SamOptions(
                skipImagePull = true
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "build",
                "FooResource",
                "--template",
                "$templatePath",
                "--build-dir",
                "$buildDir",
                "--skip-pull-image"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam build can take a docker network`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val buildDir = tempFolder.newFolder("build").toPath()
        val cmd = GeneralCommandLine("sam").samBuildCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            buildDir = buildDir,
            samOptions = SamOptions(
                dockerNetwork = "FooNetwork"
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "build",
                "--template",
                "$templatePath",
                "--build-dir",
                "$buildDir",
                "--docker-network",
                "FooNetwork"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam build can append custom provided args`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val buildDir = tempFolder.newFolder("build").toPath()
        val cmd = GeneralCommandLine("sam").samBuildCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            buildDir = buildDir,
            samOptions = SamOptions(
                additionalBuildArgs = "--foo bar"
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "build",
                "--template",
                "$templatePath",
                "--build-dir",
                "$buildDir",
                "--foo",
                "bar"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam build command is correct with container`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val buildDir = tempFolder.newFolder("build").toPath()
        val cmd = GeneralCommandLine("sam").samBuildCommand(
            environmentVariables = emptyMap(),
            templatePath = templatePath,
            buildDir = buildDir,
            samOptions = SamOptions(
                buildInContainer = true
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "build",
                "--template",
                "$templatePath",
                "--build-dir",
                "$buildDir",
                "--use-container"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam package command without repo is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val packagedTemplatePath = tempFolder.newFile("packagedTemplate.yaml").toPath()
        val cmd = GeneralCommandLine("sam").samPackageCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            packagedTemplatePath = packagedTemplatePath,
            s3Bucket = "myBucket",
            ecrRepo = null
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "package",
                "--template-file",
                "$templatePath",
                "--output-template-file",
                "$packagedTemplatePath",
                "--s3-bucket",
                "myBucket"
            ).joinToString(separator = " ")
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)

        assertThat(cmd.environment).containsEntry("Foo", "Bar")
    }

    @Test
    fun `sam package command without bucket is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val packagedTemplatePath = tempFolder.newFile("packagedTemplate.yaml").toPath()
        val cmd = GeneralCommandLine("sam").samPackageCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            packagedTemplatePath = packagedTemplatePath,
            s3Bucket = null,
            ecrRepo = "myRepo"
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "package",
                "--template-file",
                "$templatePath",
                "--output-template-file",
                "$packagedTemplatePath",
                "--image-repository",
                "myRepo"
            ).joinToString(separator = " ")
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)

        assertThat(cmd.environment).containsEntry("Foo", "Bar")
    }

    @Test
    fun `sam package command is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val packagedTemplatePath = tempFolder.newFile("packagedTemplate.yaml").toPath()
        val cmd = GeneralCommandLine("sam").samPackageCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            packagedTemplatePath = packagedTemplatePath,
            s3Bucket = "myBucket",
            ecrRepo = "myRepo"
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "package",
                "--template-file",
                "$templatePath",
                "--output-template-file",
                "$packagedTemplatePath",
                "--s3-bucket",
                "myBucket",
                "--image-repository",
                "myRepo"
            ).joinToString(separator = " ")
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)

        assertThat(cmd.environment).containsEntry("Foo", "Bar")
    }

    @Test
    fun `sam deploy command is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val cmd = GeneralCommandLine("sam").samDeployCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            DeployServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                autoExecute = true,
                parameters = emptyMap(),
                tags = emptyMap(),
                useContainer = false,
                capabilities = listOf(CreateCapabilities.IAM, CreateCapabilities.NAMED_IAM)
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "deploy",
                "--template-file",
                "$templatePath",
                "--stack-name",
                "MyStack",
                "--s3-bucket",
                "myBucket",
                "--capabilities",
                "CAPABILITY_IAM",
                "CAPABILITY_NAMED_IAM",
                "--no-execute-changeset"
            ).joinToString(separator = " ")
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)

        assertThat(cmd.environment).containsEntry("Foo", "Bar")
    }

    @Test
    fun `sam deploy parameters are correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val cmd = GeneralCommandLine("sam").samDeployCommand(
            environmentVariables = emptyMap(),
            templatePath = templatePath,
            DeployServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                autoExecute = true,
                parameters = mapOf(
                    "Hello1" to "World",
                    "Hello2" to "Wor ld",
                    "Hello3" to "\"Wor ld\"",
                    "Hello4" to "It's",
                    "Hello5" to "2+2=22"
                ),
                tags = emptyMap(),
                useContainer = false,
                capabilities = emptyList()
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "deploy",
                "--template-file",
                "$templatePath",
                "--stack-name",
                "MyStack",
                "--s3-bucket",
                "myBucket",
                "--no-execute-changeset",
                "--parameter-overrides",
                """ \"Hello1\"=\"World\" """.trim(),
                """ "\"Hello2\"=\"Wor ld\"" """.trim(),
                """ "\"Hello3\"='\"Wor ld\"'" """.trim(),
                """ \"Hello4\"=\"It's\" """.trim(),
                """ \"Hello5\"=\"2+2=22\" """.trim()
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam deploy with tags are correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()
        val cmd = GeneralCommandLine("sam").samDeployCommand(
            environmentVariables = emptyMap(),
            templatePath = templatePath,
            DeployServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                autoExecute = true,
                parameters = emptyMap(),
                tags = mapOf(
                    "Hello1" to "World",
                    "Hello2" to "Wor ld",
                    "Hello3" to "\"Wor ld\"",
                    "Hello4" to "It's",
                    "Hello5" to "2+2=22",
                    "Hello;6" to "World",
                ),
                useContainer = false,
                capabilities = emptyList()
            )
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "deploy",
                "--template-file",
                "$templatePath",
                "--stack-name",
                "MyStack",
                "--s3-bucket",
                "myBucket",
                "--no-execute-changeset",
                "--tags",
                """ \"Hello1\"=\"World\" """.trim(),
                """ "\"Hello2\"=\"Wor ld\"" """.trim(),
                """ "\"Hello3\"='\"Wor ld\"'" """.trim(),
                """ \"Hello4\"=\"It's\" """.trim(),
                """ \"Hello5\"=\"2+2=22\" """.trim(),
                """ \"Hello;6\"=\"World\" """.trim(),
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam init with zip app template is correct`() {
        val outputDir = tempFolder.newFolder()
        val cmd = GeneralCommandLine("sam").samInitCommand(
            outputDir = outputDir.toPath(),
            parameters = AppBasedZipTemplate(
                name = "Hello",
                runtime = LambdaRuntime.JAVA11,
                architecture = LambdaArchitecture.X86_64,
                appTemplate = "HelloWorldTemplate",
                dependencyManager = "maven"
            ),
            extraContext = emptyMap()
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "init",
                "--no-input",
                "--output-dir",
                "$outputDir",
                "--name",
                "Hello",
                "--runtime",
                "java11",
                "--dependency-manager",
                "maven",
                "--app-template",
                "HelloWorldTemplate"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam init with zip app template arm architecture is correct`() {
        val outputDir = tempFolder.newFolder()
        val cmd = GeneralCommandLine("sam").samInitCommand(
            outputDir = outputDir.toPath(),
            parameters = AppBasedZipTemplate(
                name = "Hello",
                runtime = LambdaRuntime.JAVA11,
                architecture = LambdaArchitecture.ARM64,
                appTemplate = "HelloWorldTemplate",
                dependencyManager = "maven"
            ),
            extraContext = emptyMap()
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "init",
                "--no-input",
                "--output-dir",
                "$outputDir",
                "--name",
                "Hello",
                "--runtime",
                "java11",
                "--architecture",
                "arm64",
                "--dependency-manager",
                "maven",
                "--app-template",
                "HelloWorldTemplate"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam init with image app template is correct`() {
        val outputDir = tempFolder.newFolder()
        val cmd = GeneralCommandLine("sam").samInitCommand(
            outputDir = outputDir.toPath(),
            parameters = AppBasedImageTemplate(
                name = "Hello",
                baseImage = "amazon/runtime-base",
                architecture = LambdaArchitecture.X86_64,
                appTemplate = "HelloWorld",
                dependencyManager = "maven",
            ),
            extraContext = emptyMap()
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "init",
                "--no-input",
                "--output-dir",
                "$outputDir",
                "--package-type",
                "Image",
                "--name",
                "Hello",
                "--base-image",
                "amazon/runtime-base",
                "--dependency-manager",
                "maven",
                "--app-template",
                "HelloWorld"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam init with image app arm architecture correct`() {
        val outputDir = tempFolder.newFolder()
        val cmd = GeneralCommandLine("sam").samInitCommand(
            outputDir = outputDir.toPath(),
            parameters = AppBasedImageTemplate(
                name = "Hello",
                baseImage = "amazon/runtime-base",
                architecture = LambdaArchitecture.ARM64,
                appTemplate = "HelloWorld",
                dependencyManager = "maven",
            ),
            extraContext = emptyMap()
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "init",
                "--no-input",
                "--output-dir",
                "$outputDir",
                "--package-type",
                "Image",
                "--name",
                "Hello",
                "--base-image",
                "amazon/runtime-base",
                "--architecture",
                "arm64",
                "--dependency-manager",
                "maven",
                "--app-template",
                "HelloWorld"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam init with location template is correct`() {
        val outputDir = tempFolder.newFolder()
        val cmd = GeneralCommandLine("sam").samInitCommand(
            outputDir = outputDir.toPath(),
            parameters = LocationBasedTemplate(
                location = "SomeUrl"
            ),
            extraContext = emptyMap()
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "init",
                "--no-input",
                "--output-dir",
                "$outputDir",
                "--location",
                "SomeUrl"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam init with extra context is correct`() {
        val outputDir = tempFolder.newFolder()
        val cmd = GeneralCommandLine("sam").samInitCommand(
            outputDir = outputDir.toPath(),
            parameters = AppBasedZipTemplate(
                name = "Hello",
                runtime = LambdaRuntime.JAVA11,
                architecture = LambdaArchitecture.X86_64,
                appTemplate = "HelloWorldTemplate",
                dependencyManager = "maven"
            ),
            extraContext = mapOf("Foo" to "Bar")
        )

        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "init",
                "--no-input",
                "--output-dir",
                "$outputDir",
                "--name",
                "Hello",
                "--runtime",
                "java11",
                "--dependency-manager",
                "maven",
                "--app-template",
                "HelloWorldTemplate",
                "--extra-context",
                """{\"Foo\":\"Bar\"}"""
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam sync command is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()

        val cmd = GeneralCommandLine("sam").samSyncCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            SyncServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                parameters = emptyMap(),
                tags = emptyMap(),
                useContainer = false,
                capabilities = listOf(CreateCapabilities.NAMED_IAM, CreateCapabilities.AUTO_EXPAND)
            )
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)
        assertThat(cmd.environment).containsEntry("Foo", "Bar")
        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "sync",
                "--stack-name",
                "MyStack",
                "--template-file",
                "$templatePath",
                "--s3-bucket",
                "myBucket",
                "--capabilities",
                "CAPABILITY_NAMED_IAM",
                "CAPABILITY_AUTO_EXPAND",
                "--no-dependency-layer",
                "--no-watch"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam sync with parameters is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()

        val cmd = GeneralCommandLine("sam").samSyncCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            SyncServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                parameters = mapOf(
                    "Hello1" to "World",
                    "Hello2" to "Wor ld",
                    "Hello3" to "\"Wor ld\"",
                    "Hello4" to "It's",
                    "Hello5" to "2+2=22"
                ),
                tags = emptyMap(),
                useContainer = false,
                capabilities = listOf(CreateCapabilities.NAMED_IAM, CreateCapabilities.AUTO_EXPAND)
            )
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)
        assertThat(cmd.environment).containsEntry("Foo", "Bar")
        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "sync",
                "--stack-name",
                "MyStack",
                "--template-file",
                "$templatePath",
                "--s3-bucket",
                "myBucket",
                "--capabilities",
                "CAPABILITY_NAMED_IAM",
                "CAPABILITY_AUTO_EXPAND",
                "--parameter-overrides",
                """ \"Hello1\"=\"World\" """.trim(),
                """ "\"Hello2\"=\"Wor ld\"" """.trim(),
                """ "\"Hello3\"='\"Wor ld\"'" """.trim(),
                """ \"Hello4\"=\"It's\" """.trim(),
                """ \"Hello5\"=\"2+2=22\" """.trim(),
                "--no-dependency-layer",
                "--no-watch"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam sync command with tags is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()

        val cmd = GeneralCommandLine("sam").samSyncCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            SyncServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                parameters = emptyMap(),
                tags = mapOf(
                    "Hello1" to "World",
                    "Hello2" to "Wor ld",
                    "Hello3" to "\"Wor ld\"",
                    "Hello4" to "It's",
                    "Hello5" to "2+2=22",
                    "Hello;6" to "World",
                ),
                useContainer = false,
                capabilities = listOf(CreateCapabilities.NAMED_IAM, CreateCapabilities.AUTO_EXPAND)
            )
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)
        assertThat(cmd.environment).containsEntry("Foo", "Bar")
        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "sync",
                "--stack-name",
                "MyStack",
                "--template-file",
                "$templatePath",
                "--s3-bucket",
                "myBucket",
                "--capabilities",
                "CAPABILITY_NAMED_IAM",
                "CAPABILITY_AUTO_EXPAND",
                "--tags",
                """ \"Hello1\"=\"World\" """.trim(),
                """ "\"Hello2\"=\"Wor ld\"" """.trim(),
                """ "\"Hello3\"='\"Wor ld\"'" """.trim(),
                """ \"Hello4\"=\"It's\" """.trim(),
                """ \"Hello5\"=\"2+2=22\" """.trim(),
                """ \"Hello;6\"=\"World\" """.trim(),
                "--no-dependency-layer",
                "--no-watch"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam sync command with use container option checked is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()

        val cmd = GeneralCommandLine("sam").samSyncCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            SyncServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                parameters = emptyMap(),
                tags = emptyMap(),
                useContainer = true,
                capabilities = listOf(CreateCapabilities.NAMED_IAM, CreateCapabilities.AUTO_EXPAND)
            )
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)
        assertThat(cmd.environment).containsEntry("Foo", "Bar")
        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "sync",
                "--stack-name",
                "MyStack",
                "--template-file",
                "$templatePath",
                "--s3-bucket",
                "myBucket",
                "--capabilities",
                "CAPABILITY_NAMED_IAM",
                "CAPABILITY_AUTO_EXPAND",
                "--use-container",
                "--no-dependency-layer",
                "--no-watch"
            ).joinToString(separator = " ")
        )
    }

    @Test
    fun `sam sync command with capabilities added is correct`() {
        val templatePath = tempFolder.newFile("template.yaml").toPath()

        val cmd = GeneralCommandLine("sam").samSyncCommand(
            environmentVariables = mapOf("Foo" to "Bar"),
            templatePath = templatePath,
            SyncServerlessApplicationSettings(
                stackName = "MyStack",
                bucket = "myBucket",
                ecrRepo = null,
                parameters = emptyMap(),
                tags = emptyMap(),
                useContainer = true,
                capabilities = listOf(CreateCapabilities.AUTO_EXPAND)
            )
        )

        assertThat(cmd.workDirectory).isEqualTo(tempFolder.root)
        assertThat(cmd.environment).containsEntry("Foo", "Bar")
        assertThat(cmd.commandLineString).isEqualTo(
            listOf(
                "sam",
                "sync",
                "--stack-name",
                "MyStack",
                "--template-file",
                "$templatePath",
                "--s3-bucket",
                "myBucket",
                "--capabilities",
                "CAPABILITY_AUTO_EXPAND",
                "--use-container",
                "--no-dependency-layer",
                "--no-watch"
            ).joinToString(separator = " ")
        )
    }
}


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

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

import com.intellij.psi.PsiFileFactory
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.runInEdtAndGet
import com.intellij.testFramework.runInEdtAndWait
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.jetbrains.yaml.YAMLLanguage
import org.jetbrains.yaml.psi.YAMLFile
import org.jetbrains.yaml.psi.YAMLKeyValue
import org.jetbrains.yaml.psi.YAMLMapping
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import software.aws.toolkits.core.utils.writeText
import software.aws.toolkits.jetbrains.services.cloudformation.Function
import software.aws.toolkits.jetbrains.services.lambda.sam.SamTemplateUtils.findFunctionsFromTemplate
import software.aws.toolkits.jetbrains.services.lambda.sam.SamTemplateUtils.functionFromElement
import software.aws.toolkits.jetbrains.services.lambda.steps.UploadedEcrCode
import software.aws.toolkits.jetbrains.services.lambda.steps.UploadedS3Code

class SamTemplateUtilsTest {

    @Rule
    @JvmField
    val projectRule = ProjectRule()

    @Rule
    @JvmField
    val tempFolder = TemporaryFolder()

    @Test
    fun `getUploadedCodeUri image uri block is parsed`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
              Resources:
                Function:
                  Type: AWS::Serverless::Function
                  Properties:
                    PackageType: Image
                    ImageUri: 111122223333.dkr.ecr.us-east-1.amazonaws.com/repo:tag
                    Timeout: 300
                    MemorySize: 128
            """.trimIndent()
        )

        val uploadedCode = SamTemplateUtils.getUploadedCodeUri(templateFile, "Function")
        assertThat(uploadedCode).isInstanceOfSatisfying(UploadedEcrCode::class.java) { code ->
            assertThat(code.imageUri).isEqualTo("111122223333.dkr.ecr.us-east-1.amazonaws.com/repo:tag")
        }
    }

    @Test
    fun `getUploadedCodeUri s3 bucket URI is parsed`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  PackageType: Zip
                  Handler: helloworld.App::handleRequest
                  CodeUri: s3://FooBucket/Foo/Key
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        val uploadedCode = SamTemplateUtils.getUploadedCodeUri(templateFile, "Function")
        assertThat(uploadedCode).isInstanceOfSatisfying(UploadedS3Code::class.java) { code ->
            assertThat(code.bucket).isEqualTo("FooBucket")
            assertThat(code.key).isEqualTo("Foo/Key")
            assertThat(code.version).isNull()
        }
    }

    @Test
    fun `getUploadedCodeUri s3 block is parsed`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  PackageType: Zip
                  Handler: helloworld.App::handleRequest
                  CodeUri:
                    Bucket: FooBucket
                    Key: FooKey
                    Version: FooVersion
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        val uploadedCode = SamTemplateUtils.getUploadedCodeUri(templateFile, "Function")
        assertThat(uploadedCode).isInstanceOfSatisfying(UploadedS3Code::class.java) { code ->
            assertThat(code.bucket).isEqualTo("FooBucket")
            assertThat(code.key).isEqualTo("FooKey")
            assertThat(code.version).isEqualTo("FooVersion")
        }
    }

    @Test
    fun `getUploadedCodeUri no package type is treated as Zip`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: s3://FooBucket/Foo/Key
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        val uploadedCode = SamTemplateUtils.getUploadedCodeUri(templateFile, "Function")
        assertThat(uploadedCode).isInstanceOfSatisfying(UploadedS3Code::class.java) { code ->
            assertThat(code.bucket).isEqualTo("FooBucket")
            assertThat(code.key).isEqualTo("Foo/Key")
            assertThat(code.version).isNull()
        }
    }

    @Test
    fun `getUploadedCodeUri wrong S3 scheme throws`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: file://FooBucket/Foo/Key
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        assertThatThrownBy {
            SamTemplateUtils.getUploadedCodeUri(templateFile, "Function")
        }.hasMessageContaining("does not start with s3://")
    }

    @Test
    fun `getUploadedCodeUri wrong S3 URI format throws`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri:  s3://FooBucket
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        assertThatThrownBy {
            SamTemplateUtils.getUploadedCodeUri(templateFile, "Function")
        }.hasMessageContaining("does not follow the format s3://<bucket>/<key>")
    }

    @Test
    fun `getUploadedCodeUri missing logical id throws`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri:  file://FooBucket/Foo/Key
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        assertThatThrownBy {
            SamTemplateUtils.getUploadedCodeUri(templateFile, "MissingFunction")
        }.hasMessageContaining("No resource with the logical ID MissingFunction")
    }

    @Test
    fun `getCodeLocation for serverless functions returns codeUri`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: src
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        val codeLocation = SamTemplateUtils.getCodeLocation(templateFile, "Function")
        assertThat(codeLocation).isEqualTo("src")
    }

    @Test
    fun `getCodeLocation for lambda functions returns code`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Lambda::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  Code: src
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        val codeLocation = SamTemplateUtils.getCodeLocation(templateFile, "Function")
        assertThat(codeLocation).isEqualTo("src")
    }

    @Test
    fun `getCodeLocation for images returns the docker context`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  PackageType: Image
                  Timeout: 300
                  MemorySize: 128
                Metadata:
                  DockerTag: v1
                  DockerContext: src
                  Dockerfile: Dockerfile
            """.trimIndent()
        )

        val codeLocation = SamTemplateUtils.getCodeLocation(templateFile, "Function")
        assertThat(codeLocation).isEqualTo("src")
    }

    @Test
    fun `getCodeLocation missing logical id throws for serverless function`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri:  foo/
                  Runtime: java8.al2
                  Timeout: 300
                  MemorySize: 128
            """.trimIndent()
        )

        assertThatThrownBy {
            SamTemplateUtils.getCodeLocation(templateFile, "MissingFunction")
        }.hasMessageContaining("No resource with the logical ID MissingFunction")
    }

    @Test
    fun `getCodeLocation missing logical id throws for lambda function`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Lambda::Function
                Properties:
                  Code: foo.zip
                  Handler: foobar.App::handleRequest
                  Runtime: java8
            """.trimIndent()
        )

        assertThatThrownBy {
            SamTemplateUtils.getCodeLocation(templateFile, "MissingFunction")
        }.hasMessageContaining("No resource with the logical ID MissingFunction")
    }

    @Test
    fun `getCodeLocation missing logical id throws for image based function`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  PackageType: Image
                  Timeout: 300
                  MemorySize: 128
                Metadata:
                  DockerTag: v1
                  DockerContext: src
                  Dockerfile: Dockerfile
            """.trimIndent()
        )

        assertThatThrownBy {
            SamTemplateUtils.getCodeLocation(templateFile, "MissingFunction")
        }.hasMessageContaining("No resource with the logical ID MissingFunction")
    }

    @Test
    fun `can pull functions from a sam template`() {
        val file = yamlFile().virtualFile

        runInEdtAndWait {
            val functions = findFunctionsFromTemplate(projectRule.project, file)
            assertThat(functions).hasSize(2)
            assertFunction(functions[0], "MySamFunction", "hello.zip", "helloworld.App::handleRequest", "java8")
            assertFunction(functions[1], "MyLambdaFunction", "foo.zip", "foobar.App::handleRequest", "java8")
        }
    }

    @Test
    fun `can pull function from a sam template with global`() {
        val file = yamlFileWithGlobal().virtualFile
        runInEdtAndWait {
            val functions = findFunctionsFromTemplate(projectRule.project, file)
            assertThat(functions).hasSize(2)
            assertFunction(functions[0], "MySamFunction", "hello.zip", "helloworld.App::handleRequest", "java8")
            assertFunction(functions[1], "MyLambdaFunction", "foo.zip", "foobar.App::handleRequest", "java8")
        }
    }

    @Test
    fun `can convert a psi element function`() {
        val file = yamlFile()

        val function = runInEdtAndGet {
            val functionElement =
                file.findByLocation("Resources.MySamFunction") ?: throw RuntimeException("Can't find MySamFunction")
            functionFromElement(functionElement)
        } ?: throw AssertionError("Function not found")

        assertFunction(function, "MySamFunction", "hello.zip", "helloworld.App::handleRequest", "java8")
    }

    @Test
    fun `getFunctionEnvironmentVariables returns empty when globals is malformed`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Globals:
              Function:
                Environment:
                  Variables: string
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: src
                  Runtime: java11
            """.trimIndent()
        )

        assertThat(SamTemplateUtils.getFunctionEnvironmentVariables(templateFile, "Function")).isEqualTo(emptyMap<String, String>())
    }

    @Test
    fun `getFunctionEnvironmentVariables returns empty when function env vars is malformed`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: src
                  Runtime: java11
                  Environment:
                    Variables: string
            """.trimIndent()
        )

        assertThat(SamTemplateUtils.getFunctionEnvironmentVariables(templateFile, "Function")).isEqualTo(emptyMap<String, String>())
    }

    @Test
    fun `getFunctionEnvironmentVariables returns empty when there is neither globals nor function specific ones`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: src
                  Runtime: java11
            """.trimIndent()
        )

        assertThat(SamTemplateUtils.getFunctionEnvironmentVariables(templateFile, "Function")).isEqualTo(emptyMap<String, String>())
    }

    @Test
    fun `getFunctionEnvironmentVariables returns globals`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Globals:
              Function:
                Environment:
                  Variables:
                    VAR1: abc
                    VAR2: def
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: src
                  Runtime: java11
            """.trimIndent()
        )

        assertThat(
            SamTemplateUtils.getFunctionEnvironmentVariables(templateFile, "Function")
        ).isEqualTo(
            mapOf(
                "VAR1" to "abc",
                "VAR2" to "def"
            )
        )
    }

    @Test
    fun `getFunctionEnvironmentVariables returns function env vars`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: src
                  Runtime: java11
                  Environment:
                    Variables:
                      VAR1: abc
                      VAR2: def
            """.trimIndent()
        )

        assertThat(
            SamTemplateUtils.getFunctionEnvironmentVariables(templateFile, "Function")
        ).isEqualTo(
            mapOf(
                "VAR1" to "abc",
                "VAR2" to "def"
            )
        )
    }

    @Test
    fun `getFunctionEnvironmentVariables returns global and function env vars overwriting globals`() {
        val templateFile = tempFolder.newFile().toPath()
        templateFile.writeText(
            """
            Globals:
              Function:
                Environment:
                  Variables:
                    VAR1: abc
                    VAR2: def
            Resources:
              Function:
                Type: AWS::Serverless::Function
                Properties:
                  Handler: helloworld.App::handleRequest
                  CodeUri: src
                  Runtime: java11
                  Environment:
                    Variables:
                      VAR2: 123
                      VAR3: ghi
            """.trimIndent()
        )

        assertThat(
            SamTemplateUtils.getFunctionEnvironmentVariables(templateFile, "Function")
        ).isEqualTo(
            mapOf(
                "VAR1" to "abc",
                "VAR2" to "123",
                "VAR3" to "ghi"
            )
        )
    }

    private fun yamlFile(): YAMLFile = runInEdtAndGet {
        PsiFileFactory.getInstance(projectRule.project).createFileFromText(
            YAMLLanguage.INSTANCE,
            """
Resources:
    MySamFunction:
        Type: AWS::Serverless::Function
        Properties:
            CodeUri: hello.zip
            Handler: helloworld.App::handleRequest
            Runtime: java8
    MyLambdaFunction:
        Type: AWS::Lambda::Function
        Properties:
            Code: foo.zip
            Handler: foobar.App::handleRequest
            Runtime: java8
            """.trimIndent()
        ) as YAMLFile
    }

    private fun yamlFileWithGlobal(): YAMLFile = runInEdtAndGet {
        PsiFileFactory.getInstance(projectRule.project).createFileFromText(
            YAMLLanguage.INSTANCE,
            """
Globals:
    Function:
        Runtime: java8
        Timeout: 180
Resources:
    MySamFunction:
        Type: AWS::Serverless::Function
        Properties:
            CodeUri: hello.zip
            Handler: helloworld.App::handleRequest
    MyLambdaFunction:
        Type: AWS::Lambda::Function
        Properties:
            Code: foo.zip
            Handler: foobar.App::handleRequest
            Runtime: java8
            """.trimIndent()
        ) as YAMLFile
    }

    private fun assertFunction(
        function: Function,
        logicalName: String,
        codeLocation: String,
        handler: String,
        runtime: String
    ) {
        runInEdtAndWait {
            assertThat(function.logicalName).isEqualTo(logicalName)
            assertThat(function.codeLocation()).isEqualTo(codeLocation)
            assertThat(function.handler()).isEqualTo(handler)
            assertThat(function.runtime()).isEqualTo(runtime)
        }
    }
}

fun YAMLFile.findByLocation(location: String): YAMLKeyValue? = (documents.firstOrNull()?.topLevelValue as? YAMLMapping)?.findByLocation(location)

fun YAMLMapping.findByLocation(location: String): YAMLKeyValue? {
    val parts = location.split('.')
    val head = parts.first()
    val tail = parts.takeLast(parts.size - 1)
    return when (tail.isEmpty()) {
        true -> getKeyValueByKey(head)
        false -> (getKeyValueByKey(head)?.value as? YAMLMapping)?.findByLocation(tail.joinToString("."))
    }
}


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

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

import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
import software.aws.toolkits.resources.message

class SamVersionCacheTest {

    @JvmField
    @Rule
    val expectedException: ExpectedException = ExpectedException.none()

    @Rule
    @JvmField
    val projectRule = HeavyJavaCodeInsightTestFixtureRule()

    @After
    fun tearDown() {
        SamVersionCache.testOnlyGetRequestCache().clear()
    }

    @Test
    fun invalidSamExecutablePath() {
        val invalidPath = "invalid_path"

        expectedException.expect(IllegalStateException::class.java)
        expectedException.expectMessage(message("general.file_not_found", invalidPath))

        SamVersionCache.evaluateBlocking(invalidPath)
    }

    @Test
    fun samCliMinVersion() {
        val samPath = SamCommonTestUtils.makeATestSam(SamCommonTestUtils.getMinVersionAsJson()).toString()
        val samVersion = SamVersionCache.evaluateBlocking(samPath).result
        assertEquals("Mismatch SAM executable version", samVersion, SamExecutable.minVersion)
    }

    @Test
    fun samCliInvalidVersion() {
        val version = "0.0.a"

        expectedException.expect(IllegalStateException::class.java)
        expectedException.expectMessage(message("executableCommon.version_parse_error", SamCommon.SAM_NAME, version))

        val samPath = SamCommonTestUtils.makeATestSam(SamCommonTestUtils.getVersionAsJson(version)).toString()
        SamVersionCache.evaluateBlocking(samPath)
    }

    @Test
    fun errorCode_RandomError() {
        val message = "No such file or directory"

        expectedException.expect(IllegalStateException::class.java)
        expectedException.expectMessage(message)

        val samPath = SamCommonTestUtils.makeATestSam(message, exitCode = 1).toString()
        SamVersionCache.evaluateBlocking(samPath)
    }

    @Test
    fun errorCode_InvalidOption() {
        val message = "Error: no such option: --some_option"

        expectedException.expect(IllegalStateException::class.java)
        expectedException.expectMessage(message("executableCommon.unexpected_output", SamCommon.SAM_NAME, message))

        val samPath = SamCommonTestUtils.makeATestSam(message, exitCode = 1).toString()
        SamVersionCache.evaluateBlocking(samPath)
    }

    @Test
    fun successExecution_EmptyOutput() {
        val message = ""

        expectedException.expect(IllegalStateException::class.java)
        expectedException.expectMessage(message("executableCommon.empty_info", SamCommon.SAM_NAME))

        val samPath = SamCommonTestUtils.makeATestSam(message).toString()
        SamVersionCache.evaluateBlocking(samPath)
    }
}