package com.sonymusic

import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

import static org.hamcrest.MatcherAssert.assertThat
import static org.hamcrest.core.StringContains.containsString
import static org.hamcrest.Matchers.hasItem
import static org.junit.jupiter.api.Assertions.*

class LambdaDeployTest extends BasePipelineTest {
    def lambdaDeploy

    @BeforeEach
    void setUp() {
        super.setUp()
        lambdaDeploy = loadScript("vars/lambdaDeploy.groovy")
        helper.registerAllowedMethod('sh')
        helper.registerAllowedMethod('withAWS', [Map, Closure])
    }

    @Test
    void testCallWithNoArgs() {
        // Assert error is thrown since required args are not supplied
        assertThrows(AssertionError.class, {
            lambdaDeploy.call(dummy: 'dummy')
        })
    }

    @Test
    void testCallWithNoFunctionNameOrEnvironment() {
        // Assert error is thrown since neither function name nor environment provided
        assertThrows(AssertionError.class, {
            lambdaDeploy.call(
                awsRegions: ['us-east-1'],
                imageName: 'foo',
                imageTag: 'bar'
            )
        })
    }

    @Test
    void testCallWithDeploymentAccountButNoRole() {
        // Assert error is thrown since deployment account ID is provided but role is not
        assertThrows(AssertionError.class, {
            lambdaDeploy.call(
                awsRegions: ['us-east-1'],
                imageName: 'foo',
                imageTag: 'bar',
                environment: 'dev',
                awsDeploymentTargetAccountId: '123456789012'
            )
        })
    }

    @Test
    void testCallWithDeploymentRoleButNoAccount() {
        // Assert error is thrown since deployment role is provided but account ID is not
        assertThrows(AssertionError.class, {
            lambdaDeploy.call(
                awsRegions: ['us-east-1'],
                imageName: 'foo',
                imageTag: 'bar',
                environment: 'dev',
                awsDeploymentRoleName: 'deploy-role'
            )
        })
    }

    @Test
    void testCallWithBadRegion() {
        // Assert error is thrown since awsRegions is a string
        assertThrows(AssertionError.class, {
            lambdaDeploy.call(
                awsRegions: 'us-east-1',
                imageName: 'foo',
                imageTag: 'bar',
                environment: 'dev'
            )
        })
    }

    @Test
    void testCallSuccess() {
        assertNull(lambdaDeploy(
            awsRegions: ['us-east-1'],
            imageName: 'foo',
            imageTag: 'bar',
            environment: 'dev'
        ))

        assertEquals(1, helper.callStack.findAll{ it.methodName == 'withEnv' }.size())
        def withEnvArgs = helper.callStack.findAll{ it.methodName == 'withEnv' }[0].args[0]
        assertEquals(1, withEnvArgs.size())
        assertEquals('AWS_REGION=us-east-1', withEnvArgs[0].toString())

        assertEquals(1, helper.callStack.findAll{ it.methodName == 'sh' }.size())
        def shArgs = helper.callStack.findAll{ it.methodName == 'sh' }[0].argsToString()
        assertThat(shArgs, containsString('update_containerized_lambda.py -f dev-foo -t bar -i foo'))
    }

    @Test
    void testCallWithOptionalArgs() {
        assertNull(lambdaDeploy(
            awsRegions: ['us-east-1'],
            imageName: 'foo',
            imageTag: 'bar',
            functionName: 'dev-lambda',
            publishVersion: true,
            aliasName: 'lambda-alias',
            ecrRegistryAccountId: '123456789012',
            ecrRegistryRegion: 'us-west-2'
        ))

        assertEquals(1, helper.callStack.findAll{ it.methodName == 'withEnv' }.size())
        def withEnvArgs = helper.callStack.findAll{ it.methodName == 'withEnv' }[0].args[0]
        assertEquals(3, withEnvArgs.size())
        assertEquals('AWS_REGION=us-east-1', withEnvArgs[0].toString())
        assertEquals('ECR_REGISTRY_ACCOUNT_ID=123456789012', withEnvArgs[1].toString())
        assertEquals('ECR_REGISTRY_REGION=us-west-2', withEnvArgs[2].toString())

        assertEquals(1, helper.callStack.findAll{ it.methodName == 'sh' }.size())
        def shArgs = helper.callStack.findAll{ it.methodName == 'sh' }[0].argsToString()
        assertThat(shArgs, containsString('update_containerized_lambda.py -f dev-lambda -t bar -i foo -p -a lambda-alias'))
    }

    @Test
    void testWorkspaceDirIncludesAccountId() {
        // When a target account is provided, the checkout dir must include it so that parallel
        // branches deploying the same functionName to different accounts don't collide.
        assertNull(lambdaDeploy(
            awsRegions: ['us-east-1'],
            imageName: 'foo',
            imageTag: 'bar',
            functionName: 'qa-lambda-sanitise-rds-data',
            awsDeploymentRoleName: 'deploy-role',
            awsDeploymentTargetAccountId: '123456789012'
        ))

        def dirArgs = helper.callStack.findAll { it.methodName == 'dir' }.collect { it.args[0] }
        assertThat(dirArgs, hasItem('lambdaDeploy/qa-lambda-sanitise-rds-data-123456789012'))
    }

    @Test
    void testWorkspaceDirWithoutAccountId() {
        // Without a target account, the checkout dir falls back to the bare functionName.
        assertNull(lambdaDeploy(
            awsRegions: ['us-east-1'],
            imageName: 'foo',
            imageTag: 'bar',
            environment: 'dev'
        ))

        def dirArgs = helper.callStack.findAll { it.methodName == 'dir' }.collect { it.args[0] }
        assertThat(dirArgs, hasItem('lambdaDeploy/dev-foo'))
    }

    @Test
    void testCallWithDeploymentRole() {
        assertNull(lambdaDeploy(
            awsRegions: ['us-east-1'],
            imageName: 'foo',
            imageTag: 'bar',
            environment: 'dev',
            awsDeploymentRoleName: 'deploy-role',
            awsDeploymentTargetAccountId: '123456789012'
        ))

        assertEquals(1, helper.callStack.findAll{ it.methodName == 'withAWS' }.size())
        def withAWSArgs = helper.callStack.findAll{ it.methodName == 'withAWS' }[0].args[0]
        assertEquals(withAWSArgs, [role: 'deploy-role', roleAccount: '123456789012', roleSessionName: 'deploy-role', useNode: true])
    }
}
